//========= 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_