This repository has been archived on 2024-06-13. You can view files and clone it, but cannot push or open issues or pull requests.
2020-08-04 13:13:01 -04:00

806 lines
28 KiB
C

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Common pixel shader code
//
// $NoKeywords: $
//
//=============================================================================//
#ifndef COMMON_PS_FXC_H_
#define COMMON_PS_FXC_H_
#include "common_fxc.h"
// Put global skip commands here. . make sure and check that the appropriate
// vars are defined so these aren't used on the wrong shaders!
// --------------------------------------------------------------------------------
// HDR should never be enabled if we don't aren't running in float or integer
// HDR mode. SKIP: defined $HDRTYPE && defined $HDRENABLED && !$HDRTYPE &&
// $HDRENABLED
// --------------------------------------------------------------------------------
// We don't ever write water fog to dest alpha if we aren't doing water fog.
// SKIP: defined $PIXELFOGTYPE && defined $WRITEWATERFOGTODESTALPHA && (
// $PIXELFOGTYPE != 1 ) && $WRITEWATERFOGTODESTALPHA
// --------------------------------------------------------------------------------
// We don't need fog in the pixel shader if we aren't in float fog mode2
// NOSKIP: defined $HDRTYPE && defined $HDRENABLED && defined $PIXELFOGTYPE &&
// $HDRTYPE != HDR_TYPE_FLOAT && $FOGTYPE != 0
// --------------------------------------------------------------------------------
// We don't do HDR and LIGHTING_PREVIEW at the same time since it's running LDR
// in hammer.
// SKIP: defined $LIGHTING_PREVIEW && defined $HDRTYPE && $LIGHTING_PREVIEW &&
// $HDRTYPE != 0
// --------------------------------------------------------------------------------
// Ditch all fastpath attempts if we are doing LIGHTING_PREVIEW.
// SKIP: defined $LIGHTING_PREVIEW && defined $FASTPATHENVMAPTINT &&
//$LIGHTING_PREVIEW && $FASTPATHENVMAPTINT SKIP: defined $LIGHTING_PREVIEW &&
//defined $FASTPATHENVMAPCONTRAST && $LIGHTING_PREVIEW &&
//$FASTPATHENVMAPCONTRAST SKIP: defined $LIGHTING_PREVIEW && defined $FASTPATH
//&& $LIGHTING_PREVIEW && $FASTPATH
// --------------------------------------------------------------------------------
// Ditch flashlight depth when flashlight is disabled
// SKIP: ($FLASHLIGHT || $FLASHLIGHTSHADOWS) && $LIGHTING_PREVIEW
// --------------------------------------------------------------------------------
// System defined pixel shader constants
#if defined(_X360)
const bool g_bHighQualityShadows : register(b0);
#endif
// NOTE: w == 1.0f / (Dest alpha compressed depth range).
const float4 g_LinearFogColor : register(c29);
#define OO_DESTALPHA_DEPTH_RANGE (g_LinearFogColor.w)
// Linear and gamma light scale values
const float4 cLightScale : register(c30);
#define LINEAR_LIGHT_SCALE (cLightScale.x)
#define LIGHT_MAP_SCALE (cLightScale.y)
#define ENV_MAP_SCALE (cLightScale.z)
#define GAMMA_LIGHT_SCALE (cLightScale.w)
// Flashlight constants
#if defined(SHADER_MODEL_PS_2_0) || defined(SHADER_MODEL_PS_2_B) || \
defined(SHADER_MODEL_PS_3_0)
const float4 cFlashlightColor : register(c28);
const float4 cFlashlightScreenScale
: register(c31); // .zw are currently unused
#define flFlashlightNoLambertValue \
cFlashlightColor.w // This is either 0.0 or 2.0
#endif
#define HDR_INPUT_MAP_SCALE 16.0f
#define TONEMAP_SCALE_NONE 0
#define TONEMAP_SCALE_LINEAR 1
#define TONEMAP_SCALE_GAMMA 2
#define PIXEL_FOG_TYPE_NONE \
-1 // MATERIAL_FOG_NONE is handled by PIXEL_FOG_TYPE_RANGE, this is for
// explicitly disabling fog in the shader
#define PIXEL_FOG_TYPE_RANGE \
0 // range+none packed together in ps2b. Simply none in ps20 (instruction
// limits)
#define PIXEL_FOG_TYPE_HEIGHT 1
// If you change these, make the corresponding change in hardwareconfig.cpp
#define NVIDIA_PCF_POISSON 0
#define ATI_NOPCF 1
#define ATI_NO_PCF_FETCH4 2
struct LPREVIEW_PS_OUT {
float4 color : COLOR0;
float4 normal : COLOR1;
float4 position : COLOR2;
float4 flags : COLOR3;
};
/*
// unused
HALF Luminance( HALF3 color )
{
return dot( color, HALF3( HALF_CONSTANT(0.30f), HALF_CONSTANT(0.59f),
HALF_CONSTANT(0.11f) ) );
}
*/
/*
// unused
HALF LuminanceScaled( HALF3 color )
{
return dot( color, HALF3( HALF_CONSTANT(0.30f) / MAX_HDR_OVERBRIGHT,
HALF_CONSTANT(0.59f) / MAX_HDR_OVERBRIGHT, HALF_CONSTANT(0.11f) /
MAX_HDR_OVERBRIGHT ) );
}
*/
/*
// unused
HALF AvgColor( HALF3 color )
{
return dot( color, HALF3( HALF_CONSTANT(0.33333f),
HALF_CONSTANT(0.33333f), HALF_CONSTANT(0.33333f) ) );
}
*/
/*
// unused
HALF4 DiffuseBump( sampler lightmapSampler,
float2 lightmapTexCoord1,
float2 lightmapTexCoord2,
float2 lightmapTexCoord3,
HALF3 normal )
{
HALF3 lightmapColor1 = tex2D( lightmapSampler, lightmapTexCoord1 );
HALF3 lightmapColor2 = tex2D( lightmapSampler, lightmapTexCoord2 );
HALF3 lightmapColor3 = tex2D( lightmapSampler, lightmapTexCoord3 );
HALF3 diffuseLighting;
diffuseLighting = saturate( dot( normal, bumpBasis[0] ) ) *
lightmapColor1 + saturate( dot( normal, bumpBasis[1] ) ) * lightmapColor2 +
saturate( dot( normal, bumpBasis[2] )
) * lightmapColor3;
return HALF4( diffuseLighting, LuminanceScaled( diffuseLighting ) );
}
*/
/*
// unused
HALF Fresnel( HALF3 normal,
HALF3 eye,
HALF2 scaleBias )
{
HALF fresnel = HALF_CONSTANT(1.0f) - dot( normal, eye );
fresnel = pow( fresnel, HALF_CONSTANT(5.0f) );
return fresnel * scaleBias.x + scaleBias.y;
}
*/
/*
// unused
HALF4 GetNormal( sampler normalSampler,
float2 normalTexCoord )
{
HALF4 normal = tex2D( normalSampler, normalTexCoord );
normal.rgb = HALF_CONSTANT(2.0f) * normal.rgb - HALF_CONSTANT(1.0f);
return normal;
}
*/
// Needs to match NormalDecodeMode_t enum in imaterialsystem.h
#define NORM_DECODE_NONE 0
#define NORM_DECODE_ATI2N 1
#define NORM_DECODE_ATI2N_ALPHA 2
float4 DecompressNormal(sampler NormalSampler, float2 tc,
int nDecompressionMode, sampler AlphaSampler) {
float4 normalTexel = tex2D(NormalSampler, tc);
float4 result;
if (nDecompressionMode == NORM_DECODE_NONE) {
result = float4(normalTexel.xyz * 2.0f - 1.0f, normalTexel.a);
} else if (nDecompressionMode == NORM_DECODE_ATI2N) {
result.xy = normalTexel.xy * 2.0f - 1.0f;
result.z = sqrt(1.0f - dot(result.xy, result.xy));
result.a = 1.0f;
} else // ATI2N plus ATI1N for alpha
{
result.xy = normalTexel.xy * 2.0f - 1.0f;
result.z = sqrt(1.0f - dot(result.xy, result.xy));
result.a = tex2D(AlphaSampler, tc)
.x; // Note that this comes in on the X channel
}
return result;
}
float4 DecompressNormal(sampler NormalSampler, float2 tc,
int nDecompressionMode) {
return DecompressNormal(NormalSampler, tc, nDecompressionMode,
NormalSampler);
}
HALF3 NormalizeWithCubemap(sampler normalizeSampler, HALF3 input) {
// return texCUBE( normalizeSampler, input ) * 2.0f - 1.0f;
return texCUBE(normalizeSampler, input);
}
/*
HALF4 EnvReflect( sampler envmapSampler,
sampler normalizeSampler,
HALF3 normal,
float3 eye,
HALF2 fresnelScaleBias )
{
HALF3 normEye = NormalizeWithCubemap( normalizeSampler, eye );
HALF fresnel = Fresnel( normal, normEye, fresnelScaleBias );
HALF3 reflect = CalcReflectionVectorUnnormalized( normal, eye );
return texCUBE( envmapSampler, reflect );
}
*/
float CalcWaterFogAlpha(const float flWaterZ, const float flEyePosZ,
const float flWorldPosZ, const float flProjPosZ,
const float flFogOORange) {
// float flDepthFromWater = flWaterZ - flWorldPosZ + 2.0f; // hackity hack
//. .this is for the DF_FUDGE_UP in view_scene.cpp
float flDepthFromWater = flWaterZ - flWorldPosZ;
// if flDepthFromWater < 0, then set it to 0
// This is the equivalent of moving the vert to the water surface if it's
// above the water surface We'll do this with the saturate at the end
// instead.
// flDepthFromWater = max( 0.0f, flDepthFromWater );
// Calculate the ratio of water fog to regular fog (ie. how much of the
// distance from the viewer to the vert is actually underwater.
float flDepthFromEye = flEyePosZ - flWorldPosZ;
float f = saturate(flDepthFromWater * (1.0 / flDepthFromEye));
// $tmp.w is now the distance that we see through water.
return saturate(f * flProjPosZ * flFogOORange);
}
float CalcRangeFog(const float flProjPosZ, const float flFogStartOverRange,
const float flFogMaxDensity, const float flFogOORange) {
#if !(defined(SHADER_MODEL_PS_1_1) || defined(SHADER_MODEL_PS_1_4) || \
defined(SHADER_MODEL_PS_2_0)) // Minimum requirement of ps2b
return saturate(min(flFogMaxDensity,
(flProjPosZ * flFogOORange) - flFogStartOverRange));
#else
return 0.0f; // ps20 shaders will never have range fog enabled because too
// many ran out of slots.
#endif
}
float CalcPixelFogFactor(int iPIXELFOGTYPE, const float4 fogParams,
const float flEyePosZ, const float flWorldPosZ,
const float flProjPosZ) {
float retVal;
if (iPIXELFOGTYPE == PIXEL_FOG_TYPE_NONE) {
retVal = 0.0f;
}
if (iPIXELFOGTYPE == PIXEL_FOG_TYPE_RANGE) // range fog, or no fog
// depending on fog parameters
{
retVal =
CalcRangeFog(flProjPosZ, fogParams.x, fogParams.z, fogParams.w);
} else if (iPIXELFOGTYPE == PIXEL_FOG_TYPE_HEIGHT) // height fog
{
retVal = CalcWaterFogAlpha(fogParams.y, flEyePosZ, flWorldPosZ,
flProjPosZ, fogParams.w);
}
return retVal;
}
// g_FogParams not defined by default, but this is the same layout for every
// shader that does define it
#define g_FogEndOverRange g_FogParams.x
#define g_WaterZ g_FogParams.y
#define g_FogMaxDensity g_FogParams.z
#define g_FogOORange g_FogParams.w
float3 BlendPixelFog(const float3 vShaderColor, float pixelFogFactor,
const float3 vFogColor, const int iPIXELFOGTYPE) {
if (iPIXELFOGTYPE ==
PIXEL_FOG_TYPE_RANGE) // either range fog or no fog depending on fog
// parameters and whether this is ps20 or ps2b
{
#if !(defined(SHADER_MODEL_PS_1_1) || defined(SHADER_MODEL_PS_1_4) || \
defined(SHADER_MODEL_PS_2_0)) // Minimum requirement of ps2b
pixelFogFactor = saturate(pixelFogFactor);
return lerp(
vShaderColor.rgb, vFogColor.rgb,
pixelFogFactor *
pixelFogFactor); // squaring the factor will get the middle
// range mixing closer to hardware fog
#else
return vShaderColor;
#endif
} else if (iPIXELFOGTYPE == PIXEL_FOG_TYPE_HEIGHT) {
return lerp(vShaderColor.rgb, vFogColor.rgb, saturate(pixelFogFactor));
} else if (iPIXELFOGTYPE == PIXEL_FOG_TYPE_NONE) {
return vShaderColor;
}
}
#if ((defined(SHADER_MODEL_PS_2_B) || defined(SHADER_MODEL_PS_3_0)) && \
(CONVERT_TO_SRGB != 0))
sampler1D GammaTableSampler : register(s15);
float3 SRGBOutput(const float3 vShaderColor) {
// On ps2b capable hardware we always have the linear->gamma conversion
// table texture in sampler s15.
float3 result;
result.r = tex1D(GammaTableSampler, vShaderColor.r).r;
result.g = tex1D(GammaTableSampler, vShaderColor.g).r;
result.b = tex1D(GammaTableSampler, vShaderColor.b).r;
return result;
}
#else
float3 SRGBOutput(const float3 vShaderColor) {
return vShaderColor; // ps 1.1, 1.4, and 2.0 never do srgb conversion in
// the pixel shader
}
#endif
float SoftParticleDepth(float flDepth) {
return flDepth * OO_DESTALPHA_DEPTH_RANGE;
}
float DepthToDestAlpha(const float flProjZ) {
#if !(defined(SHADER_MODEL_PS_1_1) || defined(SHADER_MODEL_PS_1_4) || \
defined(SHADER_MODEL_PS_2_0)) // Minimum requirement of ps2b
return SoftParticleDepth(flProjZ);
#else
return 1.0f;
#endif
}
float4 FinalOutput(const float4 vShaderColor, float pixelFogFactor,
const int iPIXELFOGTYPE, const int iTONEMAP_SCALE_TYPE,
const bool bWriteDepthToDestAlpha = false,
const float flProjZ = 1.0f) {
float4 result;
if (iTONEMAP_SCALE_TYPE == TONEMAP_SCALE_LINEAR) {
result.rgb = vShaderColor.rgb * LINEAR_LIGHT_SCALE;
} else if (iTONEMAP_SCALE_TYPE == TONEMAP_SCALE_GAMMA) {
result.rgb = vShaderColor.rgb * GAMMA_LIGHT_SCALE;
} else if (iTONEMAP_SCALE_TYPE == TONEMAP_SCALE_NONE) {
result.rgb = vShaderColor.rgb;
}
if (bWriteDepthToDestAlpha)
result.a = DepthToDestAlpha(flProjZ);
else
result.a = vShaderColor.a;
result.rgb = BlendPixelFog(result.rgb, pixelFogFactor, g_LinearFogColor.rgb,
iPIXELFOGTYPE);
#if !(defined(SHADER_MODEL_PS_1_1) || defined(SHADER_MODEL_PS_1_4) || \
defined(SHADER_MODEL_PS_2_0)) // Minimum requirement of ps2b
result.rgb = SRGBOutput(result.rgb); // SRGB in pixel shader conversion
#endif
return result;
}
LPREVIEW_PS_OUT FinalOutput(const LPREVIEW_PS_OUT vShaderColor,
float pixelFogFactor, const int iPIXELFOGTYPE,
const int iTONEMAP_SCALE_TYPE) {
LPREVIEW_PS_OUT result;
result.color = FinalOutput(vShaderColor.color, pixelFogFactor,
iPIXELFOGTYPE, iTONEMAP_SCALE_TYPE);
result.normal.rgb = SRGBOutput(vShaderColor.normal.rgb);
result.normal.a = vShaderColor.normal.a;
result.position.rgb = SRGBOutput(vShaderColor.position.rgb);
result.position.a = vShaderColor.position.a;
result.flags.rgb = SRGBOutput(vShaderColor.flags.rgb);
result.flags.a = vShaderColor.flags.a;
return result;
}
float RemapValClamped(float val, float A, float B, float C, float D) {
float cVal = (val - A) / (B - A);
cVal = saturate(cVal);
return C + (D - C) * cVal;
}
//===================================================================================//
// This is based on Natasha Tatarchuk's Parallax Occlusion Mapping (ATI)
//===================================================================================//
// INPUT:
// inTexCoord:
// the texcoord for the height/displacement map before
//parallaxing
//
// vParallax:
// Compute initial parallax displacement direction:
// float2 vParallaxDirection = normalize( vViewTS.xy );
// float fLength = length( vViewTS );
// float fParallaxLength = sqrt( fLength * fLength - vViewTS.z
//* vViewTS.z ) / vViewTS.z; Out.vParallax = vParallaxDirection *
//fParallaxLength * fProjectedBumpHeight;
//
// vNormal:
// tangent space normal
//
// vViewW:
// float3 vViewW = /*normalize*/(mul( matViewInverse, float4(
//0, 0, 0, 1)) - inPosition );
//
// OUTPUT:
// the new texcoord after parallaxing
float2 CalcParallaxedTexCoord(float2 inTexCoord, float2 vParallax,
float3 vNormal, float3 vViewW,
sampler HeightMapSampler) {
const int nMinSamples = 8;
const int nMaxSamples = 50;
// Normalize the incoming view vector to avoid artifacts:
// vView = normalize( vView );
vViewW = normalize(vViewW);
// vLight = normalize( vLight );
// Change the number of samples per ray depending on the viewing angle
// for the surface. Oblique angles require smaller step sizes to achieve
// more accurate precision
int nNumSteps = (int)lerp(nMaxSamples, nMinSamples, dot(vViewW, vNormal));
float4 cResultColor = float4(0, 0, 0, 1);
//===============================================//
// Parallax occlusion mapping offset computation //
//===============================================//
float fCurrHeight = 0.0;
float fStepSize = 1.0 / (float)nNumSteps;
float fPrevHeight = 1.0;
float fNextHeight = 0.0;
int nStepIndex = 0;
// bool bCondition = true;
float2 dx = ddx(inTexCoord);
float2 dy = ddy(inTexCoord);
float2 vTexOffsetPerStep = fStepSize * vParallax;
float2 vTexCurrentOffset = inTexCoord;
float fCurrentBound = 1.0;
float x = 0;
float y = 0;
float xh = 0;
float yh = 0;
float2 texOffset2 = 0;
bool bCondition = true;
while (bCondition == true && nStepIndex < nNumSteps) {
vTexCurrentOffset -= vTexOffsetPerStep;
fCurrHeight = tex2Dgrad(HeightMapSampler, vTexCurrentOffset, dx, dy).r;
fCurrentBound -= fStepSize;
if (fCurrHeight > fCurrentBound) {
x = fCurrentBound;
y = fCurrentBound + fStepSize;
xh = fCurrHeight;
yh = fPrevHeight;
texOffset2 = vTexCurrentOffset - vTexOffsetPerStep;
bCondition = false;
} else {
nStepIndex++;
fPrevHeight = fCurrHeight;
}
} // End of while ( bCondition == true && nStepIndex > -1 )#else
fCurrentBound -= fStepSize;
float fParallaxAmount;
float numerator = (x * (y - yh) - y * (x - xh));
float denomenator = ((y - yh) - (x - xh));
// avoid NaN generation
if ((numerator == 0.0f) && (denomenator == 0.0f)) {
fParallaxAmount = 0.0f;
} else {
fParallaxAmount = numerator / denomenator;
}
float2 vParallaxOffset = vParallax * (1 - fParallaxAmount);
// Sample the height at the next possible step:
fNextHeight = tex2Dgrad(HeightMapSampler, texOffset2, dx, dy).r;
// Original offset:
float2 texSampleBase = inTexCoord - vParallaxOffset;
return texSampleBase;
#if 0
cResultColor.rgb = ComputeDiffuseColor( texSampleBase, vLight );
float fBound = 1.0 - fStepSize * nStepIndex;
if ( fNextHeight < fCurrentBound )
// if( 0 )
{
//void DoIteration( in float2 vParallaxJittered, in float3 vLight, inout float4 cResultColor )
//cResultColor.rgb = float3(1,0,0);
DoIteration( vParallax + vPixelSize, vLight, fStepSize, inTexCoord, nStepIndex, dx, dy, fBound, cResultColor );
DoIteration( vParallax - vPixelSize, vLight, fStepSize, inTexCoord, nStepIndex, dx, dy, fBound, cResultColor );
DoIteration( vParallax + float2( -vPixelSize.x, vPixelSize.y ), vLight, fStepSize, inTexCoord, nStepIndex, dx, dy, fBound, cResultColor );
DoIteration( vParallax + float2( vPixelSize.x, -vPixelSize.y ), vLight, fStepSize, inTexCoord, nStepIndex, dx, dy, fBound, cResultColor );
cResultColor.rgb /= 5;
// cResultColor.rgb = float3( 1.0f, 0.0f, 0.0f );
} // End of if ( fNextHeight < fCurrentBound )
#if DOSHADOWS
{
//============================================//
// Soft shadow and self-occlusion computation //
//============================================//
// Compute the blurry shadows (note that this computation takes into
// account self-occlusion for shadow computation):
float sh0 = tex2D( sNormalMap, texSampleBase).w;
float shA = (tex2D( sNormalMap, texSampleBase + inXY * 0.88 ).w - sh0 - 0.88 ) * 1 * fShadowSoftening;
float sh9 = (tex2D( sNormalMap, texSampleBase + inXY * 0.77 ).w - sh0 - 0.77 ) * 2 * fShadowSoftening;
float sh8 = (tex2D( sNormalMap, texSampleBase + inXY * 0.66 ).w - sh0 - 0.66 ) * 4 * fShadowSoftening;
float sh7 = (tex2D( sNormalMap, texSampleBase + inXY * 0.55 ).w - sh0 - 0.55 ) * 6 * fShadowSoftening;
float sh6 = (tex2D( sNormalMap, texSampleBase + inXY * 0.44 ).w - sh0 - 0.44 ) * 8 * fShadowSoftening;
float sh5 = (tex2D( sNormalMap, texSampleBase + inXY * 0.33 ).w - sh0 - 0.33 ) * 10 * fShadowSoftening;
float sh4 = (tex2D( sNormalMap, texSampleBase + inXY * 0.22 ).w - sh0 - 0.22 ) * 12 * fShadowSoftening;
// Compute the actual shadow strength:
float fShadow = 1 - max( max( max( max( max( max( shA, sh9 ), sh8 ), sh7 ), sh6 ), sh5 ), sh4 );
cResultColor.rgb *= fShadow * 0.6 + 0.4;
}
#endif
return cResultColor;
#endif
}
//======================================//
// HSL Color space conversion routines //
//======================================//
#define HUE 0
#define SATURATION 1
#define LIGHTNESS 2
// Convert from RGB to HSL color space
float4 RGBtoHSL(float4 inColor) {
float h, s;
float flMax = max(inColor.r, max(inColor.g, inColor.b));
float flMin = min(inColor.r, min(inColor.g, inColor.b));
float l = (flMax + flMin) / 2.0f;
if (flMax == flMin) // achromatic case
{
s = h = 0;
} else // chromatic case
{
// Next, calculate the hue
float delta = flMax - flMin;
// First, calculate the saturation
if (l < 0.5f) // If we're in the lower hexcone
{
s = delta / (flMax + flMin);
} else {
s = delta / (2 - flMax - flMin);
}
if (inColor.r == flMax) {
h = (inColor.g - inColor.b) /
delta; // color between yellow and magenta
} else if (inColor.g == flMax) {
h = 2 + (inColor.b - inColor.r) /
delta; // color between cyan and yellow
} else // blue must be max
{
h = 4 + (inColor.r - inColor.g) /
delta; // color between magenta and cyan
}
h *= 60.0f;
if (h < 0.0f) {
h += 360.0f;
}
h /= 360.0f;
}
return float4(h, s, l, 1.0f);
}
float HueToRGB(float v1, float v2, float vH) {
float fResult = v1;
vH = fmod(vH + 1.0f, 1.0f);
if ((6.0f * vH) < 1.0f) {
fResult = (v1 + (v2 - v1) * 6.0f * vH);
} else if ((2.0f * vH) < 1.0f) {
fResult = (v2);
} else if ((3.0f * vH) < 2.0f) {
fResult = (v1 + (v2 - v1) * ((2.0f / 3.0f) - vH) * 6.0f);
}
return fResult;
}
// Convert from HSL to RGB color space
float4 HSLtoRGB(float4 hsl) {
float r, g, b;
float h = hsl[HUE];
float s = hsl[SATURATION];
float l = hsl[LIGHTNESS];
if (s == 0) {
r = g = b = l;
} else {
float v1, v2;
if (l < 0.5f)
v2 = l * (1.0f + s);
else
v2 = (l + s) - (s * l);
v1 = 2 * l - v2;
r = HueToRGB(v1, v2, h + (1.0f / 3.0f));
g = HueToRGB(v1, v2, h);
b = HueToRGB(v1, v2, h - (1.0f / 3.0f));
}
return float4(r, g, b, 1.0f);
}
// texture combining modes for combining base and detail/basetexture2
#define TCOMBINE_RGB_EQUALS_BASE_x_DETAILx2 0 // original mode
#define TCOMBINE_RGB_ADDITIVE 1 // base.rgb+detail.rgb*fblend
#define TCOMBINE_DETAIL_OVER_BASE 2
#define TCOMBINE_FADE 3 // straight fade between base and detail.
#define TCOMBINE_BASE_OVER_DETAIL 4 // use base alpha for blend over detail
#define TCOMBINE_RGB_ADDITIVE_SELFILLUM 5 // add detail color post lighting
#define TCOMBINE_RGB_ADDITIVE_SELFILLUM_THRESHOLD_FADE 6
#define TCOMBINE_MOD2X_SELECT_TWO_PATTERNS \
7 // use alpha channel of base to select between mod2x channels in r+a of
// detail
#define TCOMBINE_MULTIPLY 8
#define TCOMBINE_MASK_BASE_BY_DETAIL_ALPHA \
9 // use alpha channel of detail to mask base
#define TCOMBINE_SSBUMP_BUMP 10 // use detail to modulate lighting as an ssbump
#define TCOMBINE_SSBUMP_NOBUMP \
11 // detail is an ssbump but use it as an albedo. shader does the magic
// here - no user needs to specify mode 11
float4 TextureCombine(float4 baseColor, float4 detailColor, int combine_mode,
float fBlendFactor) {
if (combine_mode == TCOMBINE_MOD2X_SELECT_TWO_PATTERNS) {
float3 dc = lerp(detailColor.r, detailColor.a, baseColor.a);
baseColor.rgb *= lerp(float3(1, 1, 1), 2.0 * dc, fBlendFactor);
}
if (combine_mode == TCOMBINE_RGB_EQUALS_BASE_x_DETAILx2)
baseColor.rgb *=
lerp(float3(1, 1, 1), 2.0 * detailColor.rgb, fBlendFactor);
if (combine_mode == TCOMBINE_RGB_ADDITIVE)
baseColor.rgb += fBlendFactor * detailColor.rgb;
if (combine_mode == TCOMBINE_DETAIL_OVER_BASE) {
float fblend = fBlendFactor * detailColor.a;
baseColor.rgb = lerp(baseColor.rgb, detailColor.rgb, fblend);
}
if (combine_mode == TCOMBINE_FADE) {
baseColor = lerp(baseColor, detailColor, fBlendFactor);
}
if (combine_mode == TCOMBINE_BASE_OVER_DETAIL) {
float fblend = fBlendFactor * (1 - baseColor.a);
baseColor.rgb = lerp(baseColor.rgb, detailColor.rgb, fblend);
baseColor.a = detailColor.a;
}
if (combine_mode == TCOMBINE_MULTIPLY) {
baseColor = lerp(baseColor, baseColor * detailColor, fBlendFactor);
}
if (combine_mode == TCOMBINE_MASK_BASE_BY_DETAIL_ALPHA) {
baseColor.a =
lerp(baseColor.a, baseColor.a * detailColor.a, fBlendFactor);
}
if (combine_mode == TCOMBINE_SSBUMP_NOBUMP) {
baseColor.rgb = baseColor.rgb * dot(detailColor.rgb, 2.0 / 3.0);
}
return baseColor;
}
float3 lerp5(float3 f1, float3 f2, float i1, float i2, float x) {
return f1 + (f2 - f1) * (x - i1) / (i2 - i1);
}
float3 TextureCombinePostLighting(float3 lit_baseColor, float4 detailColor,
int combine_mode, float fBlendFactor) {
if (combine_mode == TCOMBINE_RGB_ADDITIVE_SELFILLUM)
lit_baseColor += fBlendFactor * detailColor.rgb;
if (combine_mode == TCOMBINE_RGB_ADDITIVE_SELFILLUM_THRESHOLD_FADE) {
// fade in an unusual way - instead of fading out color, remap an
// increasing band of it from 0..1
// if (fBlendFactor > 0.5)
// lit_baseColor += min(1, (1.0/fBlendFactor)*max(0,
//detailColor.rgb-(1-fBlendFactor) ) ); else lit_baseColor +=
//2*fBlendFactor*2*max(0, detailColor.rgb-.5);
float f = fBlendFactor - 0.5;
float fMult = (f >= 0) ? 1.0 / fBlendFactor : 4 * fBlendFactor;
float fAdd = (f >= 0) ? 1.0 - fMult : -0.5 * fMult;
lit_baseColor += saturate(fMult * detailColor.rgb + fAdd);
}
return lit_baseColor;
}
// NOTE: On X360. fProjZ is expected to be pre-reversed for cheaper math here in
// the pixel shader
float DepthFeathering(sampler DepthSampler, const float2 vScreenPos,
float fProjZ, float fProjW, float4 vDepthBlendConstants) {
#if (!(defined(SHADER_MODEL_PS_1_1) || defined(SHADER_MODEL_PS_1_4) || \
defined(SHADER_MODEL_PS_2_0))) // minimum requirement of ps2b
{
float flFeatheredAlpha;
float2 flDepths;
#define flSceneDepth flDepths.x
#define flSpriteDepth flDepths.y
#if (defined(_X360))
{
// Get depth from the depth texture. Need to sample with the offset
// of (0.5, 0.5) to fix rounding errors
asm {
tfetch2D flDepths.x___, vScreenPos, DepthSampler, OffsetX=0.5, OffsetY=0.5, MinFilter=point, MagFilter=point, MipFilter=point
}
;
#if (!defined(REVERSE_DEPTH_ON_X360))
flSceneDepth = 1.0f - flSceneDepth;
#endif
// get the sprite depth into the same range as the texture depth
flSpriteDepth = fProjZ / fProjW;
// unproject to get at the pre-projection z. This value is much more
// linear than depth
flDepths = vDepthBlendConstants.z / flDepths;
flDepths = vDepthBlendConstants.y - flDepths;
flFeatheredAlpha = flSceneDepth - flSpriteDepth;
flFeatheredAlpha *= vDepthBlendConstants.x;
flFeatheredAlpha = saturate(flFeatheredAlpha);
}
#else
{
flSceneDepth = tex2D(DepthSampler, vScreenPos)
.a; // PC uses dest alpha of the frame buffer
flSpriteDepth = SoftParticleDepth(fProjZ);
flFeatheredAlpha =
abs(flSceneDepth - flSpriteDepth) * vDepthBlendConstants.x;
flFeatheredAlpha = max(
smoothstep(0.75f, 1.0f, flSceneDepth),
flFeatheredAlpha); // as the sprite approaches the edge of our
// compressed depth space, the math stops
// working. So as the sprite approaches the
// far depth, smoothly remove feathering.
flFeatheredAlpha = saturate(flFeatheredAlpha);
}
#endif
#undef flSceneDepth
#undef flSpriteDepth
return flFeatheredAlpha;
}
#else
{ return 1.0f; }
#endif
}
#endif //#ifndef COMMON_PS_FXC_H_