//===================== Copyright (c) Valve Corporation. All Rights Reserved. ====================== // SKIP: $NORMALMAPALPHAENVMAPMASK && $BASEALPHAENVMAPMASK // SKIP: $NORMALMAPALPHAENVMAPMASK && $ENVMAPMASK // SKIP: $BASEALPHAENVMAPMASK && $ENVMAPMASK // SKIP: $BASEALPHAENVMAPMASK && $SELFILLUM // SKIP: !$FASTPATH && $FASTPATHENVMAPCONTRAST // SKIP: !$FASTPATH && $FASTPATHENVMAPTINT // SKIP: !$BUMPMAP && $BUMPMAP2 // SKIP: $BASEALPHAENVMAPMASK && ( $BUMPMAP && !$ENVMAPANISOTROPY ) // SKIP: $SEAMLESS && ( $DETAIL_BLEND_MODE != 12 ) // SKIP: $BUMPMASK && ( $SEAMLESS || ( $DETAILTEXTURE != 12 ) || $SELFILLUM || $BASETEXTURE2 ) // SKIP: $ENVMAPANISOTROPY && !$ENVMAP && ( $BUMPMAP != 1 ) // SKIP: $ENVMAPANISOTROPY && $NORMALMAPALPHAENVMAPMASK // SKIP: !$BUMPMAP && $ADDBUMPMAPS // SKIP: !$BUMPMAP2 && $ADDBUMPMAPS // SKIP: $BUMPMASK && $ADDBUMPMAPS // SKIP: $ADDBUMPMAPS && ( $DETAIL_BLEND_MODE != 12 ) [ps20] // SKIP: ( $DETAIL == 2 ) && !$BASETEXTURE2 // 360 compiler craps out on some combo in this family. Content doesn't use blendmode 10 anyway // SKIP: $FASTPATH && $PIXELFOGTYPE && $BASETEXTURE2 && $CUBEMAP && ($DETAIL_BLEND_MODE == 10 ) [CONSOLE] // Turning off 32bit lightmaps on Portal 2 to save shader perf. --Thorsten //#define USE_32BIT_LIGHTMAPS_ON_360 //uncomment to use 32bit lightmaps, be sure to keep this in sync with the same #define in materialsystem/cmatlightmaps.cpp #if defined( SHADER_MODEL_PS_2_0 ) //#error ps2.0 support removed for this shader #endif // NOTE: This has to be before inclusion of common_lightmappedgeneric_fxc.h to get the vertex format right! #if ( DETAIL_BLEND_MODE == 12 ) #define DETAILTEXTURE 0 #else #if ( DETAIL2 == 1) #define DETAILTEXTURE 2 #else #define DETAILTEXTURE 1 #endif #endif #include "common_ps_fxc.h" #include "common_flashlight_fxc.h" #define PIXELSHADER #include "common_lightmappedgeneric_fxc.h" #if SEAMLESS #define USE_FAST_PATH 1 #else #define USE_FAST_PATH FASTPATH #endif const float4 g_EnvmapTint : register( c0 ); #if ( USE_FAST_PATH == 1 || LIGHTING_PREVIEW == 1 ) #if FASTPATHENVMAPCONTRAST == 0 static const float3 g_EnvmapContrast = { 0.0f, 0.0f, 0.0f }; #else static const float3 g_EnvmapContrast = { 1.0f, 1.0f, 1.0f }; #endif static const float3 g_EnvmapSaturation = { 1.0f, 1.0f, 1.0f }; static const float g_FresnelReflection = 1.0f; static const float g_OneMinusFresnelReflection = 0.0f; static const float4 g_SelfIllumTint = { 1.0f, 1.0f, 1.0f, 1.0f }; #else // ( USE_FAST_PATH == 0 ) const float3 g_EnvmapContrast : register( c2 ); const float3 g_EnvmapSaturation : register( c3 ); const float4 g_FresnelReflectionReg : register( c4 ); #define g_FresnelReflection g_FresnelReflectionReg.a #define g_OneMinusFresnelReflection g_FresnelReflectionReg.b const float4 g_SelfIllumTint : register( c7 ); #endif const float4 g_DetailTint_and_BlendFactor : register( c8 ); #define g_DetailTint (g_DetailTint_and_BlendFactor.rgb) #define g_DetailBlendFactor (g_DetailTint_and_BlendFactor.w) #if ADDBUMPMAPS == 1 #define g_vAddBumpMapScale1 g_DetailTint_and_BlendFactor.r; #define g_vAddBumpMapScale2 g_DetailTint_and_BlendFactor.g; #endif const float4 g_Detail2Tint_and_BlendFactor : register(c9); #define g_Detail2Tint (g_Detail2Tint_and_BlendFactor.rgb) #define g_Detail2BlendFactor (g_Detail2Tint_and_BlendFactor.w) const float3 g_EyePos : register( c10 ); const float4 g_FogParams : register( c11 ); const float4 g_TintValuesTimesLightmapScale : register( c12 ); #define g_flAlpha2 g_TintValuesTimesLightmapScale.w const float4 g_FlashlightAttenuationFactors : register( c13 ); const float3 g_FlashlightPos : register( c14 ); const float4x4 g_FlashlightWorldToTexture : register( c15 ); // through c18 const float4 g_ShadowTweaks : register( c19 ); #if !defined( SHADER_MODEL_PS_2_0 ) && ( FLASHLIGHT == 0 ) #define g_cAmbientColor cFlashlightScreenScale.rgb //const float3 g_cAmbientColor : register( c31 ); #endif #if ( ( CUBEMAP == 2 ) || ( ENVMAPANISOTROPY ) ) const float4 g_envMapParams : register( c20 ); #endif #if ( CUBEMAP == 2 ) #define g_DiffuseCubemapScale g_envMapParams.y #define g_fvDiffuseCubemapMin float3( g_envMapParams.z, g_envMapParams.z, g_envMapParams.z ) #define g_fvDiffuseCubemapMax float3( g_envMapParams.w, g_envMapParams.w, g_envMapParams.w ) #endif #if ( ENVMAPANISOTROPY ) #define g_EnvmapAnisotropyScale g_envMapParams.x #endif #if defined( SHADER_MODEL_PS_3_0 ) const float3 g_TintValuesWithoutLightmapScale : register( c21 ); #else const float4 g_vCSMLightColor : register(c21); #endif #if ( PHONG ) const float4 g_Phong_Exp_and_BaseTint : register( c22 ); #define g_PhongExp g_Phong_Exp_and_BaseTint.x #define g_PhongTint g_Phong_Exp_and_BaseTint.y #define g_PhongExp2 g_Phong_Exp_and_BaseTint.z #define g_PhongTint2 g_Phong_Exp_and_BaseTint.w const float4 g_PhongMask_Contrast_and_Brightness : register( c23 ); #define g_PhongMaskContrast g_PhongMask_Contrast_and_Brightness.x #define g_PhongMaskBrightness g_PhongMask_Contrast_and_Brightness.y #define g_PhongMaskContrast2 g_PhongMask_Contrast_and_Brightness.z #define g_PhongMaskBrightness2 g_PhongMask_Contrast_and_Brightness.w const float4 g_PhongAmount : register( c24 ); const float4 g_PhongAmount2 : register( c25 ); #endif #if ( ENVMAPMASK ) && defined( SHADER_MODEL_PS_3_0 ) const float4 g_EnvmapMaskTexCoordTransform[2] : register( c35 ); const float4 g_EnvmapMaskTexCoordTransform2[2] : register( c37 ); #endif sampler BaseTextureSampler : register( s0 ); sampler LightmapSampler : register( s1 ); samplerCUBE EnvmapSampler : register( s2 ); #if FANCY_BLENDING sampler BlendModulationSampler : register( s3 ); #if ( CASCADED_SHADOW_MAPPING ) && defined( SHADER_MODEL_PS_3_0 ) && ( BUMPMAP > 0 ) const float4 g_vDropShadowParams : register( c26 ); #define g_fDropShadowScale g_vDropShadowParams.x #define g_fDropShadowOpacity g_vDropShadowParams.y #define g_fDropShadowHighlightScale g_vDropShadowParams.z #define g_fDropShadowDepthExaggeration g_vDropShadowParams.w #endif #endif #if ( BASETEXTURE2 ) && defined( SHADER_MODEL_PS_3_0 ) const float4 g_vTintLayer1 : register( c33 ); const float4 g_vTintLayer2 : register( c34 ); #endif #if ( FANCY_BLENDING >= 2 ) && defined( SHADER_MODEL_PS_3_0 ) const float4 g_vBlendParams : register( c46 ); #define g_flBlendSoftness g_vBlendParams.x #define g_flLayerBorderStrength g_vBlendParams.y #define g_flLayerBorderOffset g_vBlendParams.z #define g_flLayerBorderSoftness g_vBlendParams.w const float4 g_vLayerBorderTint : register( c47 ); const float4 g_vEdgeBlendParams : register( c48 ); #define g_flLayerNormalEdgePunchInSign g_vEdgeBlendParams.x #define g_flLayerNormalEdgeStrength g_vEdgeBlendParams.y #define g_flLayerNormalEdgeOffset g_vEdgeBlendParams.z #define g_flLayerNormalEdgeSoftness g_vEdgeBlendParams.w #endif #if ( DETAILTEXTURE != 0 ) sampler DetailSampler : register( s12 ); #if ( DETAILTEXTURE == 2 ) sampler DetailSampler2 : register( s9 ); #endif #endif sampler BumpmapSampler : register( s4 ); #if (BUMPMAP == 1) && defined( _PS3 ) // Causes the Cg compiler to automatically produce _bx2 modifier on the texture load instead of producing a MAD to range expand the vector, saving one instruction. #pragma texsign BumpmapSampler #pragma texformat BumpmapSampler RGBA8 #endif #if BUMPMAP2 == 1 sampler BumpmapSampler2 : register( s5 ); #endif #if ( ENVMAPMASK ) && defined( SHADER_MODEL_PS_3_0 ) sampler EnvmapMaskSampler : register( s6 ); #if ( BASETEXTURE2 ) sampler EnvmapMaskSampler2 : register( s10 ); #endif #endif sampler BaseTextureSampler2 : register( s7 ); #if BUMPMASK == 1 sampler BumpMaskSampler : register( s8 ); #if NORMALMASK_DECODE_MODE == NORM_DECODE_ATI2N_ALPHA sampler AlphaMaskSampler : register( s11 ); // alpha #else #define AlphaMaskSampler BumpMaskSampler #endif #endif #if ( defined( _X360 ) || defined( _PS3 ) ) && FLASHLIGHT sampler FlashlightSampler : register( s13 ); sampler ShadowDepthSampler : register( s14 ); sampler RandRotSampler : register( s15 ); #if defined(_PS3) // Needed for optimal shadow filter code generation on PS3. #pragma texformat ShadowDepthSampler DEPTH_COMPONENT24 #endif #endif #ifdef PHONG_DEBUG #undef PHONG_DEBUG #endif #define PHONG_DEBUG 0 //const float g_flTime : register( c24 ); float Luminance( float3 cColor ) { // Formula for calculating luminance based on NTSC standard return dot( cColor.rgb, float3( 0.2125, 0.7154, 0.0721 ) ); } //----------------------------------------------------------------------------------------------------------------------------- #if ( CASCADED_SHADOW_MAPPING ) && !defined( _X360 ) && !defined( _PS3 ) && !defined( SHADER_MODEL_PS_2_B ) const bool g_bCSMEnabled : register(b0); #undef CASCADE_SIZE #define CASCADE_SIZE 1 #endif #if ( CASCADE_SIZE > 0 ) #undef CASCADE_SIZE #define CASCADE_SIZE 3 #endif #if ( ( CASCADED_SHADOW_MAPPING ) && ( CASCADE_SIZE > 0 ) ) sampler CSMDepthAtlasSampler : register( s15 ); #if defined(_PS3) // Needed for optimal shadow filter code generation on PS3. #pragma texformat CSMDepthAtlasSampler DEPTH_COMPONENT24 #endif #if defined( SHADER_MODEL_PS_2_B ) #define CSM_LIGHTMAPPEDGENERIC #endif #include "csm_common_fxc.h" #include "csm_blending_fxc.h" #endif //----------------------------------------------------------------------------------------------------------------------------- #if defined( _X360 ) // The compiler runs out of temp registers in certain combos, increase the maximum for now #if ( BASETEXTURE2 && (BUMPMAP == 2) && CUBEMAP && NORMALMAPALPHAENVMAPMASK && DIFFUSEBUMPMAP && FLASHLIGHT && SHADER_SRGB_READ ) [maxtempreg(44)] #elif ( SHADER_SRGB_READ == 1 ) [maxtempreg(41)] #else [maxtempreg(36)] #endif #endif #if LIGHTING_PREVIEW == 2 LPREVIEW_PS_OUT main( PS_INPUT i ) #else float4_color_return_type main( PS_INPUT i ) : COLOR #endif { bool bBaseTexture2 = BASETEXTURE2 ? true : false; bool bDetailTexture = ( DETAILTEXTURE != 0 ) ? true : false; bool bDetailTexture2 = ( DETAILTEXTURE == 2 ) ? true : false; bool bBumpmap = BUMPMAP ? true : false; bool bDiffuseBumpmap = DIFFUSEBUMPMAP ? true : false; bool bEnvmapMask = ENVMAPMASK ? true : false; bool bBaseAlphaEnvmapMask = BASEALPHAENVMAPMASK ? true : false; bool bSelfIllum = SELFILLUM ? true : false; bool bNormalMapAlphaEnvmapMask = NORMALMAPALPHAENVMAPMASK ? true : false; HALF4 baseColor = 0.0h; HALF4 baseColor2 = 0.0h; HALF4 vNormal = HALF4( 0, 0, 1, 1 ); float3 baseTexCoords = float3( 0, 0, 0 ); float3 baseTexCoords2 = float3( 0, 0, 0 ); float3 worldPos = i.worldPos_projPosZ.xyz; HALF3x3 tangenttranspose = HALF3x3( HALF3(i.tangentSpaceTranspose0_vertexBlendX.xyz), HALF3(i.tangentSpaceTranspose1_bumpTexCoord2u.xyz), HALF3(i.tangentSpaceTranspose2_bumpTexCoord2v.xyz) ); float3 worldVertToEyeVector = g_EyePos - worldPos; #if SEAMLESS baseTexCoords = i.SeamlessTexCoord.xyz; baseTexCoords2 = i.SeamlessTexCoord.xyz; #else baseTexCoords.xy = i.BASETEXCOORD; baseTexCoords2.xy = i.BASETEXCOORD2; #endif float3 coords = baseTexCoords; float3 coords2 = baseTexCoords2; float2 detailTexCoord = 0.0f; float2 detailTexCoord2 = 0.0f; float2 bumpmapTexCoord = 0.0f; float2 bumpmapTexCoord2 = 0.0f; #if ( DETAILTEXTURE != 0 ) detailTexCoord = i.DETAILCOORD; #if ( DETAILTEXTURE == 2 ) && defined( SHADER_MODEL_PS_3_0 ) detailTexCoord2 = i.DETAILCOORD2; #endif #endif #if BUMPMAP bumpmapTexCoord = i.BUMPCOORD; bumpmapTexCoord2 = float2( i.BUMPCOORD2U, i.BUMPCOORD2V ); #endif GetBaseTextureAndNormal( BaseTextureSampler, BaseTextureSampler2, BumpmapSampler, bBaseTexture2, bBumpmap || bNormalMapAlphaEnvmapMask, coords, coords2, bumpmapTexCoord, (HALF3)i.vertexColor.rgb, baseColor, baseColor2, vNormal ); #if ( ENVMAPANISOTROPY ) HALF anisotropyFactor = g_EnvmapAnisotropyScale; #endif #if BUMPMAP == 1 // not ssbump vNormal.xyz = vNormal.xyz * 2.0h - 1.0h; // make signed if we're not ssbump HALF3 vThisReallyIsANormal = vNormal.xyz; #if ( ENVMAPANISOTROPY ) anisotropyFactor *= (HALF)vNormal.a; #endif #endif HALF4 lightmapColor1 = HALF4( 1.0, 1.0, 1.0, 1.0 ); HALF4 lightmapColor2 = HALF4( 1.0, 1.0, 1.0, 1.0 ); HALF4 lightmapColor3 = HALF4( 1.0, 1.0, 1.0, 1.0 ); #if LIGHTING_PREVIEW == 0 if ( bBumpmap && bDiffuseBumpmap ) { float2 bumpCoord1; float2 bumpCoord2; float2 bumpCoord3; ComputeBumpedLightmapCoordinates( i.lightmapTexCoord1And2, i.lightmapTexCoord3_bumpTexCoord.xy, bumpCoord1, bumpCoord2, bumpCoord3 ); lightmapColor1 = LightMapSample( LightmapSampler, bumpCoord1 ); lightmapColor2 = LightMapSample( LightmapSampler, bumpCoord2 ); lightmapColor3 = LightMapSample( LightmapSampler, bumpCoord3 ); } else { float2 bumpCoord1 = ComputeLightmapCoordinates( i.lightmapTexCoord1And2, i.lightmapTexCoord3_bumpTexCoord.xy ); lightmapColor1 = LightMapSample( LightmapSampler, bumpCoord1 ); } #endif HALF4 detailColor = HALF4( 1.0f, 1.0f, 1.0f, 1.0f ); HALF4 detailColor2 = HALF4( 1.0f, 1.0f, 1.0f, 1.0f ); #if ( DETAILTEXTURE != 0 ) #if SHADER_MODEL_PS_2_0 || ADDBUMPMAPS == 1 detailColor = h4tex2D( DetailSampler, detailTexCoord ); #else detailColor = HALF4( g_DetailTint, 1.0h ) * h4tex2D( DetailSampler, detailTexCoord ); #endif #if ( DETAILTEXTURE == 2 ) detailColor2 = HALF4( g_Detail2Tint, 1.0h ) * h4tex2D( DetailSampler2, detailTexCoord2 ); #endif #endif HALF blendedAlpha = baseColor.a; HALF blendfactor = i.tangentSpaceTranspose0_vertexBlendX.w; #if ( BASETEXTURE2 ) && defined( SHADER_MODEL_PS_3_0 ) baseColor.rgb *= g_vTintLayer1.rgb; baseColor2.rgb *= g_vTintLayer2.rgb; #endif #if ( PHONG ) // save off basecolor for phong mask generation before it potentially gets overwritten with a blend of basecolor and basecolor2 HALF4 baseColor1 = baseColor; #endif HALF4 vBlendModulateTexel = HALF4( 0.0f, 0.0f, 0.0f, 0.0f ); float flBlendModulateFactor = 0.0f; if ( bBaseTexture2 ) { #if (SELFILLUM == 0) && (PIXELFOGTYPE != PIXEL_FOG_TYPE_HEIGHT) && (FANCY_BLENDING == 1) && (SEAMLESS == 0) vBlendModulateTexel = h4tex2D( BlendModulationSampler, i.BLENDMODULATECOORD ); HALF minb=max(0, vBlendModulateTexel.g - vBlendModulateTexel.r ); HALF maxb=min(1, vBlendModulateTexel.g + vBlendModulateTexel.r ); blendfactor=smoothstep(minb,maxb,blendfactor); #if ( CASCADED_SHADOW_MAPPING == 1 ) && ( BUMPMAP > 0 ) && defined( SHADER_MODEL_PS_3_0 )// drop shadows on blend textures if ( g_fDropShadowOpacity > 0.0f ) { float3 vWorldLightDir = normalize( g_vCSMLightDir ); float3x3 worldToTangentSpace = transpose( tangenttranspose ); HALF2 vShadowOffset = float2( 0, 0 ); vShadowOffset.x = dot( vWorldLightDir, worldToTangentSpace[0] ); vShadowOffset.y = dot( vWorldLightDir, worldToTangentSpace[1] ); HALF NdotL = dot( vWorldLightDir, tangenttranspose[2] ); HALF HNdotL = NdotL * 0.5f + 0.5f; HALF fShadowOffset = ( vBlendModulateTexel.g - pow( vBlendModulateTexel.g, g_fDropShadowDepthExaggeration ) * g_fDropShadowDepthExaggeration ) * g_fDropShadowScale; HALF fHighlightOffset = -vBlendModulateTexel.g * g_fDropShadowHighlightScale; fShadowOffset = lerp( fShadowOffset, fHighlightOffset, blendfactor ); vShadowOffset = vShadowOffset * fShadowOffset * NdotL; HALF4 vShadowSample = h4tex2D( BlendModulationSampler, i.BLENDMODULATECOORD + vShadowOffset ); minb=max(0.0039, vShadowSample.g - vShadowSample.r ); maxb=min(1, vShadowSample.g + vShadowSample.r ); HALF dropshadow=smoothstep( maxb, minb, i.tangentSpaceTranspose0_vertexBlendX.w ); baseColor.rgb *= lerp( 1.0f, max( dropshadow, 1.0f - g_fDropShadowOpacity ), smoothstep( 0.1f, 0.5f, HNdotL ) ); HALF highlight=smoothstep(maxb,minb,i.tangentSpaceTranspose0_vertexBlendX.w) * g_fDropShadowOpacity; baseColor2.rgb += baseColor2.rgb * highlight * HNdotL; } #endif #elif (FANCY_BLENDING >= 2) && (SELFILLUM == 0) && (PIXELFOGTYPE != PIXEL_FOG_TYPE_HEIGHT) && (SEAMLESS == 0) && defined( SHADER_MODEL_PS_3_0 ) vBlendModulateTexel = h4tex2D( BlendModulationSampler, i.BLENDMODULATECOORD ); flBlendModulateFactor = vBlendModulateTexel.g; #if ( FANCY_BLENDING == 3 ) { flBlendModulateFactor = vBlendModulateTexel.a; } #endif HALF minb = max( 0, flBlendModulateFactor - g_flBlendSoftness ); HALF maxb = min( 1, flBlendModulateFactor + g_flBlendSoftness ); float flBlendfactor = smoothstep( minb, maxb, blendfactor ); HALF minborder = max( 0, flBlendModulateFactor - g_flLayerBorderSoftness ); HALF maxborder = min( 1, flBlendModulateFactor + g_flLayerBorderSoftness ); float flBorderWeight = smoothstep( minborder, maxborder, saturate( blendfactor - g_flLayerBorderOffset ) ); float flBorderStrength = ( 1.0 - abs( flBorderWeight * 2.0 - 1.0 ) ) * g_flLayerBorderStrength; baseColor.rgb *= lerp( float3( 1.0, 1.0, 1.0 ), g_vLayerBorderTint.rgb, flBorderStrength ); blendfactor = flBlendfactor; #endif baseColor.rgb = lerp( baseColor.rgb, baseColor2.rgb, blendfactor ); blendedAlpha = lerp( baseColor.a, baseColor2.a, blendfactor ); } HALF3 specularFactor = 1.0h; HALF4 vNormalMask = HALF4(0, 0, 1, 1); if ( bBumpmap ) { #if ( BUMPMAP2 == 1 ) { float2 b2TexCoord = bumpmapTexCoord2; HALF4 vNormal2; #if ( BUMPMAP == 2 ) { vNormal2 = h4tex2D( BumpmapSampler2, b2TexCoord ); } #else { HALF4 normalTexel = h4tex2D( BumpmapSampler2, b2TexCoord ); vNormal2 = HALF4( normalTexel.xyz * 2.0h - 1.0h, normalTexel.a ); } #endif #if ( BUMPMASK == 1 ) HALF3 vNormal1 = DecompressNormal( BumpmapSampler, i.BUMPCOORD, NORMALMASK_DECODE_MODE, AlphaMapSampler ); vNormal.xyz = normalize( vNormal1.xyz + vNormal2.xyz ); // Third normal map...same coords as base normalTexel = h4tex2D( BumpMaskSampler, i.BASETEXCOORD ); vNormalMask = HALF4( normalTexel.xyz * 2.0h - 1.0h, normalTexel.a ); vNormal.xyz = lerp( vNormalMask.xyz, vNormal.xyz, vNormalMask.a ); // Mask out normals from vNormal specularFactor = vNormalMask.a; #else // BUMPMASK == 0 #if ADDBUMPMAPS == 1 vNormal.xy *= g_vAddBumpMapScale1; vNormal2.xy *= g_vAddBumpMapScale2; vNormal.xyz = normalize( vNormal.xyz + vNormal2.xyz ); #elif (FANCY_BLENDING == 3) && (SELFILLUM == 0) && (PIXELFOGTYPE != PIXEL_FOG_TYPE_HEIGHT) && (SEAMLESS == 0) && defined( SHADER_MODEL_PS_3_0 ) float3 vEdgeNormal = float3( vBlendModulateTexel.xy * 2 - 1, 0.0 ); vEdgeNormal.xy *= g_flLayerNormalEdgePunchInSign; HALF minedge = max( 0, flBlendModulateFactor - g_flLayerNormalEdgeSoftness ); HALF maxedge = min( 1, flBlendModulateFactor + g_flLayerNormalEdgeSoftness ); float flEdgeWeight = smoothstep( minedge, maxedge, saturate( i.tangentSpaceTranspose0_vertexBlendX.w - g_flLayerNormalEdgeOffset ) ); float flEdgeBlendStrength = ( 1.0 - abs( flEdgeWeight * 2.0 - 1.0 ) ) * g_flLayerNormalEdgeStrength; flEdgeBlendStrength *= blendfactor; flEdgeBlendStrength = saturate( flEdgeBlendStrength ); vNormal2.xyz = lerp( vNormal2.xyz, vEdgeNormal.xyz, flEdgeBlendStrength ); vNormal2.xyz = normalize( vNormal2.xyz ); vNormal.xyz = lerp( vNormal.xyz, vNormal2.xyz, blendfactor); #else vNormal.xyz = lerp( vNormal.xyz, vNormal2.xyz, blendfactor); #endif #endif if ( bNormalMapAlphaEnvmapMask ) { specularFactor *= (HALF)vNormal.a; // Mappers don't like that the 2nd normal alpha contributes to the envmap mask. //specularFactor *= lerp( vNormal.a, vNormal2.a, blendfactor ); } } #else // BUMPMAP2 == 1 { if ( bNormalMapAlphaEnvmapMask ) { specularFactor *= (HALF)vNormal.a; } } #endif // BUMPMAP2 == 1 } else if ( bNormalMapAlphaEnvmapMask ) { specularFactor *= (HALF)vNormal.a; } #if ENVMAPMASK && defined( SHADER_MODEL_PS_3_0 ) { // note - dropped support for sm2/2b float2 envmapMaskTexCoord = float2( dot( i.ENVMAPMASKCOORD, g_EnvmapMaskTexCoordTransform[0].xy ) + g_EnvmapMaskTexCoordTransform[0].w, dot( i.ENVMAPMASKCOORD, g_EnvmapMaskTexCoordTransform[1].xy ) + g_EnvmapMaskTexCoordTransform[1].w ); float3 envmapMask = h3tex2D( EnvmapMaskSampler, envmapMaskTexCoord ).xyz; #if BASETEXTURE2 { float2 envmapMaskTexCoord2 = float2( dot( i.ENVMAPMASKCOORD, g_EnvmapMaskTexCoordTransform2[0].xy ) + g_EnvmapMaskTexCoordTransform2[0].w, dot( i.ENVMAPMASKCOORD, g_EnvmapMaskTexCoordTransform2[1].xy ) + g_EnvmapMaskTexCoordTransform2[1].w ); float3 envmapMask2 = h3tex2D( EnvmapMaskSampler2, envmapMaskTexCoord2 ).xyz; envmapMask.rgb = lerp( envmapMask.rgb, envmapMask2.rgb, blendfactor ); } #endif specularFactor *= envmapMask; } #endif if ( bBaseAlphaEnvmapMask ) { specularFactor *= 1.0h - blendedAlpha; // Reversing alpha blows! } HALF4 albedo = HALF4( 1.0f, 1.0f, 1.0f, 1.0f ); HALF alpha = 1.0h; albedo *= baseColor; if ( #if ( DETAIL_BLEND_MODE == TCOMBINE_MASK_BASE_BY_DETAIL_ALPHA ) ( !bDetailTexture ) && // In this mode we must latch alpha post detail lerp blend with base texture (see "alpha *= albedo.a" below) #endif ( !bBaseAlphaEnvmapMask && !bSelfIllum ) ) { alpha *= baseColor.a; } float detailBlendFactor = 0.0f; if ( bDetailTexture ) { if ( bDetailTexture2 ) { // combine detail maps and blend factors detailColor = lerp( detailColor, detailColor2, blendfactor ); detailBlendFactor = lerp( g_DetailBlendFactor, g_Detail2BlendFactor, blendfactor ); } else { detailBlendFactor = g_DetailBlendFactor; } albedo = TextureCombine( albedo, detailColor, DETAIL_BLEND_MODE, detailBlendFactor ); #if ( DETAIL_BLEND_MODE == TCOMBINE_MASK_BASE_BY_DETAIL_ALPHA ) alpha *= albedo.a; // In this mode we latch alpha post detail lerp now, #if above ensures that we don't pre-multiply by baseColor.a earlier #endif #if ( ( DETAIL_BLEND_MODE == TCOMBINE_MOD2X_SELECT_TWO_PATTERNS ) && !BASETEXTURE2 && !SELFILLUM ) { // don't do this in the SELFILLUM case since we don't have enough instructions in ps20 specularFactor *= 2.0h * lerp( detailColor.g, detailColor.b, baseColor.a ); } #endif } // The vertex color contains the modulation color + vertex color combined #if ( SEAMLESS == 0 ) albedo.rgb *= i.vertexColor.rgb; #endif // MAINTOL4DMERGEFIXME //alpha *= i.vertexColor.a * g_flAlpha2; // not sure about this one alpha *= i.vertexColor.a; // not sure about this one float flShadowScalar = 0.0; float flShadow = 1.0; // Save this off for single-pass flashlight, since we'll still need the SSBump vector, not a real normal HALF3 vSSBumpVector = vNormal.xyz; HALF3 diffuseLighting; if ( bBumpmap && bDiffuseBumpmap ) { // ssbump #if ( BUMPMAP == 2 ) #if ( DETAIL_BLEND_MODE == TCOMBINE_SSBUMP_BUMP ) vNormal.xyz *= lerp( HALF3( 1, 1, 1 ), 2 * detailColor.xyz, alpha ); vSSBumpVector = vNormal.xyz; alpha = 1; #endif diffuseLighting = vNormal.x * lightmapColor1.rgb + vNormal.y * lightmapColor2.rgb + vNormal.z * lightmapColor3.rgb; #if ( ( CSM_BLENDING == 1 ) && ( CASCADED_SHADOW_MAPPING ) && ( CASCADE_SIZE > 0 ) ) diffuseLighting = BlendBumpDiffuseLightmapWithCSM( diffuseLighting, lightmapColor1.a, lightmapColor2.a, lightmapColor3.a, vNormal.xyz, worldPos, flShadow, flShadowScalar ); #endif // SSBump textures are created assuming the shader decodes lighting for each basis vector by taking dot( N, basis )*lightmap. // But the lightmaps are created assuming that the 3 coeffs sum to 1.0 and are more like barycentric coords than visibility // along the basis vector...so the lightmap math is really just a weighted average of the 3 directional light maps. So a flat // normal should have 3 weights each = 0.333. But since ssbump textures are created assuming the other math, a flat normal // converted into an ssbump texture generates 3 weights each = 0.578, so instead of all 3 weights summing to 1.0, they sum // to 1.733. To adjust for this, I'm scaling these coefficients by 1 / 1.733 = 0.578. NOTE: I'm not scaling vNormal directly // since it is used elsewhere for flashlight computations and shouldn't be scaled for that code. diffuseLighting *= 0.57735025882720947h; diffuseLighting *= (HALF3)g_TintValuesTimesLightmapScale.rgb; // now, calculate vNormal for reflection purposes. if vNormal isn't needed, hopefully // the compiler will eliminate these calculations vNormal.xyz = normalize( bumpBasis[0]*vNormal.x + bumpBasis[1]*vNormal.y + bumpBasis[2]*vNormal.z); #else HALF3 dp; dp.x = saturate( dot( vNormal.xyz, bumpBasis[0] ) ); dp.y = saturate( dot( vNormal.xyz, bumpBasis[1] ) ); dp.z = saturate( dot( vNormal.xyz, bumpBasis[2] ) ); dp *= dp; #if ( DETAIL_BLEND_MODE == TCOMBINE_SSBUMP_BUMP ) dp *= 2*detailColor.rgb; #endif diffuseLighting = dp.x * lightmapColor1.rgb + dp.y * lightmapColor2.rgb + dp.z * lightmapColor3.rgb; HALF sum = dot( dp, HALF3( 1.0f, 1.0f, 1.0f ) ); #if ( ( CSM_BLENDING == 1 ) && ( CASCADED_SHADOW_MAPPING ) && ( CASCADE_SIZE > 0 ) ) diffuseLighting = BlendBumpDiffuseLightmapWithCSM( diffuseLighting.rgb, lightmapColor1.a, lightmapColor2.a, lightmapColor3.a, dp, worldPos, flShadow, flShadowScalar ); #endif diffuseLighting *= (HALF3)g_TintValuesTimesLightmapScale.rgb / sum; #endif } else { diffuseLighting = lightmapColor1.rgb; #if ( ( CSM_BLENDING == 1 ) && ( CASCADED_SHADOW_MAPPING ) && ( CASCADE_SIZE > 0 ) ) diffuseLighting = BlendDiffuseLightmapWithCSM( diffuseLighting, lightmapColor1.a, worldPos, flShadow, flShadowScalar ); #endif diffuseLighting.rgb *= g_TintValuesTimesLightmapScale.rgb; } // OLD CSM BLENDING - see above for fixed/improved. This version supports older vrad baked lightmap alpha // also a catch path for ssbump #if ( ( ( CSM_BLENDING == 0 ) ) && ( CASCADED_SHADOW_MAPPING ) && ( CASCADE_SIZE > 0 ) ) { #if !defined( _X360 ) && !defined( _PS3 ) && !defined( SHADER_MODEL_PS_2_B ) if ( g_bCSMEnabled ) { #endif // Can't enable dynamic jumps around the Fetch4 shader, because it can't use tex2dlod() #if ( CSM_MODE != CSM_MODE_ATI_FETCH4 ) && !defined( SHADER_MODEL_PS_2_B ) [branch] #endif if ( lightmapColor1.a > 0.0f ) { float flSunPercent; if ( bBumpmap && bDiffuseBumpmap ) { flSunPercent = lightmapColor1.a / ( Luminance( lightmapColor1.rgb + lightmapColor2.rgb + lightmapColor3.rgb ) * 0.3333 ); } else { flSunPercent = lightmapColor1.a / Luminance( lightmapColor1.rgb ); } flShadow = CSMComputeShadowing( worldPos ); flShadowScalar = 1.0 - ( flSunPercent * ( 1.0 - flShadow ) ); /* Debug - blink full shadows if ( step( frac( g_flTime * 0.5 ), 0.5 ) ) { flShadowScalar = 1.0 - lightmapColor1.a; } //*/ // Apply csm shadows diffuseLighting.rgb *= flShadowScalar; // Desaturate shadow color since we only have a grayscale dim factor diffuseLighting.rgb = lerp( diffuseLighting.bgr, diffuseLighting.rgb, flShadowScalar * 0.5 + 0.5 ); // debug visualization // diffuseLighting.rgb = lerp( float3(1.0f-flShadowScalar,1.0f-flShadowScalar,1.0f-flShadowScalar), CSMVisualizeSplit( worldPos ), .3f ); // return float4(diffuseLighting.rgb, 1.0f); } #if !defined( _X360 ) && !defined( _PS3 ) && !defined( SHADER_MODEL_PS_2_B ) } #endif } #endif HALF3 worldSpaceNormal = mul( vNormal.xyz, tangenttranspose ); #if !defined( SHADER_MODEL_PS_2_0 ) && ( FLASHLIGHT == 0 ) diffuseLighting += (HALF3)g_cAmbientColor; #endif HALF3 diffuseComponent = albedo.rgb * diffuseLighting; #if ( defined( _X360 ) || defined( _PS3 ) ) && FLASHLIGHT // ssbump doesn't pass a normal to the flashlight...it computes shadowing a different way #if ( BUMPMAP == 2 ) bool bHasNormal = false; float3 worldPosToLightVector = g_FlashlightPos - worldPos; HALF3 tangentPosToLightVector; tangentPosToLightVector.x = dot( worldPosToLightVector, tangenttranspose[0] ); tangentPosToLightVector.y = dot( worldPosToLightVector, tangenttranspose[1] ); tangentPosToLightVector.z = dot( worldPosToLightVector, tangenttranspose[2] ); tangentPosToLightVector = normalize( tangentPosToLightVector ); HALF nDotL = saturate( vSSBumpVector.x*dot( tangentPosToLightVector, bumpBasis[0]) + vSSBumpVector.y*dot( tangentPosToLightVector, bumpBasis[1]) + vSSBumpVector.z*dot( tangentPosToLightVector, bumpBasis[2]) ); #else bool bHasNormal = true; HALF nDotL = 1.0h; #endif bool bShadows = FLASHLIGHTSHADOWS ? true : false; HALF3 flashlightColor = DoFlashlight( g_FlashlightPos, worldPos, i.flashlightSpacePos, worldSpaceNormal, g_FlashlightAttenuationFactors.xyz, g_FlashlightAttenuationFactors.w, FlashlightSampler, ShadowDepthSampler, RandRotSampler, 0, bShadows, i.vProjPos.xy / i.vProjPos.w, false, g_ShadowTweaks, bHasNormal ); diffuseComponent = albedo.xyz * ( diffuseLighting + ( flashlightColor * nDotL * (HALF3)g_TintValuesWithoutLightmapScale.rgb ) ); #endif if ( bSelfIllum ) { HALF3 selfIllumComponent = (HALF3)g_SelfIllumTint.xyz * albedo.xyz; diffuseComponent = lerp( diffuseComponent, selfIllumComponent, baseColor.a ); } HALF3 specularLighting = HALF3( 0.0f, 0.0f, 0.0f ); #if ( CUBEMAP ) { float3 reflectVect = CalcReflectionVectorUnnormalized( worldSpaceNormal, worldVertToEyeVector ); // Calc Fresnel factor HALF3 eyeVect = normalize(worldVertToEyeVector); HALF fresnel = 1.0h - dot( worldSpaceNormal, eyeVect ); #if ( ENVMAPANISOTROPY ) // For anisotropic reflections on macroscopically rough sufaces like asphalt // Orthogonalize the view vector to the surface normal, and use it as the anisotropy direction reflectVect = normalize( reflectVect ); float3 rvec = cross( -eyeVect.xyz, worldSpaceNormal.xyz ); float3 tang = cross( rvec, worldSpaceNormal.xyz ); rvec = cross( tang, reflectVect ); float3 reflectVectAniso = normalize( cross( rvec, worldSpaceNormal.xyz ) ); // Anisotropy amount is influenced by the view angle to the surface. The more oblique the angle the more anisotropic the surface appears. anisotropyFactor *= dot( reflectVectAniso, -eyeVect ); anisotropyFactor *= anisotropyFactor; reflectVect = normalize( lerp( reflectVect, reflectVectAniso, anisotropyFactor ) ); #endif fresnel = max( 0, fresnel ); // precision issues on RSX cause this value to occasionally go negative, which results in a NaN presumably because of the exp(log(n)) operation fresnel = pow( fresnel, 4.0h ); //changing this to 4th power to save 2 cycles - visually it's very similar fresnel = fresnel * (HALF)g_OneMinusFresnelReflection + (HALF)g_FresnelReflection; specularLighting = (HALF)ENV_MAP_SCALE * h3texCUBE( EnvmapSampler, reflectVect ).rgb; #if (CUBEMAP == 2) //cubemap darkened by lightmap mode float3 cubemapLight = saturate( ( diffuseLighting - g_fvDiffuseCubemapMin ) * g_fvDiffuseCubemapMax ); specularLighting = lerp( specularLighting, specularLighting * cubemapLight, (HALF)g_DiffuseCubemapScale ); //reduce the cubemap contribution when the pixel is in shadow #endif specularLighting *= specularFactor; specularLighting *= (HALF3)g_EnvmapTint.rgb; #if FANCY_BLENDING == 0 HALF3 specularLightingSquared = specularLighting * specularLighting; specularLighting = lerp( specularLighting, specularLightingSquared, (HALF)g_EnvmapContrast ); HALF3 greyScale = dot( specularLighting, HALF3( 0.299f, 0.587f, 0.114f ) ); specularLighting = lerp( greyScale, specularLighting, (HALF)g_EnvmapSaturation ); #endif specularLighting *= fresnel; } #endif if ( bDetailTexture ) { diffuseComponent = TextureCombinePostLighting( diffuseComponent, detailColor, DETAIL_BLEND_MODE, detailBlendFactor ); } // PHONG #if ( PHONG ) && ( CASCADED_SHADOW_MAPPING ) [branch] if ( flShadowScalar > 0.0f ) { float3 phongLighting = float3( 0.0f, 0.0f, 0.0f ); float3 eyeVect = normalize( worldVertToEyeVector ); float3 vLightDir = normalize( g_vCSMLightDir ); float3 vHalfAngle = normalize( eyeVect.xyz + vLightDir.xyz); // need normalized worldspacenormal here else NDotH raised to phongExp blows up worldSpaceNormal = normalize(worldSpaceNormal); float NDotH = saturate( dot( worldSpaceNormal.xyz, vHalfAngle.xyz ) ); float2 phongMask = float2( 0.0f, 0.0f ); float2 phongTerm = float2( 0.0f, 0.0f ); // greyscale phong mask phongMask.x = dot( baseColor1.rgb, float3(0.299f, 0.587f, 0.114f) ); // phong lighting phongTerm.x = pow( NDotH, g_PhongExp ); // Raise to specular exponent if ( bBaseTexture2 ) { phongMask.y = dot( baseColor2.rgb, float3(0.299f, 0.587f, 0.114f) ); phongTerm.y = pow( NDotH, g_PhongExp2 ); } // phong mask contrast, brightness phongMask.xy = saturate( ( (phongMask.xy - 0.5f) * g_PhongMask_Contrast_and_Brightness.xz ) + 0.5f + g_PhongMask_Contrast_and_Brightness.yw ); // * mask phongTerm.xy *= phongMask.xy; // phong lighting material1 phongLighting = phongTerm.xxx * g_PhongAmount.rgb * lerp( float3(1.0f, 1.0f, 1.0f), baseColor1.rgb, g_PhongTint ); // * amount * tint // material2 if ( bBaseTexture2 ) { float3 phongLighting2 = phongTerm.yyy * g_PhongAmount2.rgb * lerp( float3(1.0f, 1.0f, 1.0f), baseColor2.rgb, g_PhongTint2 ); // term * mask * amount * tint // blend phongLighting = lerp( phongLighting, phongLighting2, blendfactor ); } #if ( CSM_BLENDING == 1 ) // mask with N.L * ao * baked shadow * dynamic shadow phongLighting *= flShadow * flShadowScalar * specularFactor; #else phongLighting *= pow( saturate( dot( worldSpaceNormal, vLightDir ) ), 0.5f ); // Mask with N.L raised to a power phongLighting *= flShadow * diffuseLighting * specularFactor; // modulate with csm shadow, diffuse lighting, spec(env map) mask if present #endif specularLighting += phongLighting; #if ( PHONG_DEBUG == 1 ) // debug phong mask diffuseComponent = 0.0f; specularLighting = lerp( phongMask.xxx, 0.0f, blendfactor ); specularLighting += lerp( 0.0f, phongMask.yyy, blendfactor ); #endif } // use .a channel of phongAmount as a modulator for the diffuse component (useful for debugging, or approximating energy conservation if ( bBaseTexture2 ) { diffuseComponent *= lerp( g_PhongAmount.a, g_PhongAmount2.a, blendfactor ); } else { diffuseComponent *= g_PhongAmount.a; } #endif HALF3 result = diffuseComponent + specularLighting; #if ( LIGHTING_PREVIEW == 3 ) { return float4( worldSpaceNormal, i.worldPos_projPosZ.w ); } #endif #if ( LIGHTING_PREVIEW == 1 ) { float dotprod = 0.2 + abs( dot( normalize(worldSpaceNormal), normalize(worldVertToEyeVector) ) ); return FinalOutput( float4( dotprod*albedo.xyz*(g_TintValuesTimesLightmapScale.rgb/g_TintValuesTimesLightmapScale.w), alpha ), 0, PIXEL_FOG_TYPE_NONE, TONEMAP_SCALE_NONE ); } #endif #if ( LIGHTING_PREVIEW == 2 ) { LPREVIEW_PS_OUT ret; ret.color = float4( albedo.xyz,alpha ); ret.normal = float4( worldSpaceNormal, i.worldPos_projPosZ.w ); ret.position = float4( worldPos, alpha ); ret.flags = float4( 1, 1, 1, alpha ); return FinalOutput( ret, 0, PIXEL_FOG_TYPE_NONE, TONEMAP_SCALE_NONE ); } #endif #if ( LIGHTING_PREVIEW == 0 ) { bool bWriteDepthToAlpha = false; // ps_2_b and beyond #if !(defined(SHADER_MODEL_PS_1_1) || defined(SHADER_MODEL_PS_1_4) || defined(SHADER_MODEL_PS_2_0)) bWriteDepthToAlpha = ( WRITE_DEPTH_TO_DESTALPHA != 0 ) && ( WRITEWATERFOGTODESTALPHA == 0 ); #endif HALF flVertexFogFactor = 0.0h; // FIXME: Reintroduce support for vertex fog //#if !HARDWAREFOGBLEND && !DOPIXELFOG //{ // #if ( SEAMLESS ) // { // flVertexFogFactor = i.SeamlessTexCoord_fogFactorW.w; // } // #else // { // flVertexFogFactor = i.baseTexCoord_fogFactorZ.z; // } // #endif //} //#endif HALF fogFactor = CalcPixelFogFactor( PIXELFOGTYPE, g_FogParams, g_EyePos.xyz, worldPos, i.worldPos_projPosZ.w ); //HALF fogFactor = CalcPixelFogFactorSupportsVertexFog( PIXELFOGTYPE, g_FogParams, g_EyePos.xyz, worldPos, i.worldPos_projPosZ.w, flVertexFogFactor ); #if WRITEWATERFOGTODESTALPHA && (PIXELFOGTYPE == PIXEL_FOG_TYPE_HEIGHT) alpha = fogFactor; #endif float4_color_return_type vOutput = FinalOutputHalf( HALF4( result.rgb, alpha ), fogFactor, PIXELFOGTYPE, TONEMAP_SCALE_LINEAR, bWriteDepthToAlpha, i.worldPos_projPosZ.w ); #if ( defined( _X360 ) ) { vOutput.xyz += ScreenSpaceOrderedDither( i.vScreenPos ); } #endif return vOutput; } #endif }