// LFS Pixel Shader : ShadowToWorld // Vertex shader output structures struct VS_OUTPUT_1_SHADOW { float4 oPos : POSITION; float oFog : FOG; float Height : TEXCOORD0; float2 oT0 : TEXCOORD1; }; struct VS_OUTPUT_2_SHADOW { float4 oPos : POSITION; float oFog : FOG; float Height : TEXCOORD0; float2 oT0 : TEXCOORD1; float2 oT1 : TEXCOORD2; }; struct VS_OUTPUT_3_SHADOW { float4 oPos : POSITION; float oFog : FOG; float Height : TEXCOORD0; float2 oT0 : TEXCOORD1; float2 oT1 : TEXCOORD2; float2 oT2 : TEXCOORD3; }; struct VS_OUTPUT_4_SHADOW { float4 oPos : POSITION; float oFog : FOG; float Height : TEXCOORD0; float2 oT0 : TEXCOORD1; float2 oT1 : TEXCOORD2; float2 oT2 : TEXCOORD3; float2 oT3 : TEXCOORD4; }; // Global variables float4 shadow_darkness_4 : register(c8); // for ambient shadows sampler2D s_T0 : register(s0); sampler2D s_T1 : register(s1); sampler2D s_T2 : register(s2); sampler2D s_T3 : register(s3); // function to get the height of the shadow caster float get_caster_height(float4 tex_col) { const float MIN_ALT = -20.0f; // these constants must match the ones in CarToShadow.vsh const float MAX_ALT = 160.0f; const float ALT_RANGE = MAX_ALT - MIN_ALT; // the shadow caster height is stored in tex_col.g // tex_col.g must be divided by tex_col.r to correct any reduction that occurred during edge softening float height_corrector = max(tex_col.r, 0.01f); // avoiding divide by zero // map the green value back to the altitude it represents return (tex_col.g / height_corrector) * ALT_RANGE + MIN_ALT; // h = g * 180 - 20 } // function to get the brightness of a single car's ambient shadow float4 ambient_shadow(float4 tex_col, float ground_height, float max_darkness) { // the shadow darkness is tex_col.r // 0 = no shadow / 1 = shadow / between 0 and 1 at shadow edges due to edge softening float diff = ground_height - get_caster_height(tex_col); float lightness = 1.0f - max_darkness * tex_col.r / (1.0f + diff * diff * 16.0f); return float4(lightness, lightness, lightness, 1.0f); } // function to get the brightness of a single car's direct shadow float4 direct_shadow(float4 tex_col, float ground_height) { // the shadow darkness is tex_col.r // 0 = no shadow / 1 = shadow / between 0 and 1 at shadow edges due to edge softening const float ALLOW = 0.01f; // allow upward casting 1cm (height accuracy is 180 / 65536 = 0.0027) const float H_MUL = 1.0f / ALLOW; // fade from shadow to no shadow over ALLOW height float diff = get_caster_height(tex_col) + ALLOW - ground_height; float lightness = 1.0f - tex_col.r * saturate(diff * H_MUL); return float4(lightness, lightness, lightness, 1.0f); } // Main functions // AMBIENT float4 ps_1_shadow_ambient( in VS_OUTPUT_1_SHADOW In ) : COLOR { float4 shad0_col = tex2D(s_T0, In.oT0); return ambient_shadow(shad0_col, In.Height, shadow_darkness_4.x); } float4 ps_2_shadow_ambient( in VS_OUTPUT_2_SHADOW In ) : COLOR { float4 shad0_col = tex2D(s_T0, In.oT0); float4 shad1_col = tex2D(s_T1, In.oT1); return ambient_shadow(shad0_col, In.Height, shadow_darkness_4.x) * ambient_shadow(shad1_col, In.Height, shadow_darkness_4.y); } float4 ps_3_shadow_ambient( in VS_OUTPUT_3_SHADOW In ) : COLOR { float4 shad0_col = tex2D(s_T0, In.oT0); float4 shad1_col = tex2D(s_T1, In.oT1); float4 shad2_col = tex2D(s_T2, In.oT2); return ambient_shadow(shad0_col, In.Height, shadow_darkness_4.x) * ambient_shadow(shad1_col, In.Height, shadow_darkness_4.y) * ambient_shadow(shad2_col, In.Height, shadow_darkness_4.z); } float4 ps_4_shadow_ambient( in VS_OUTPUT_4_SHADOW In ) : COLOR { float4 shad0_col = tex2D(s_T0, In.oT0); float4 shad1_col = tex2D(s_T1, In.oT1); float4 shad2_col = tex2D(s_T2, In.oT2); float4 shad3_col = tex2D(s_T3, In.oT3); return ambient_shadow(shad0_col, In.Height, shadow_darkness_4.x) * ambient_shadow(shad1_col, In.Height, shadow_darkness_4.y) * ambient_shadow(shad2_col, In.Height, shadow_darkness_4.z) * ambient_shadow(shad3_col, In.Height, shadow_darkness_4.w); } // DIRECT float4 ps_1_shadow_direct( in VS_OUTPUT_1_SHADOW In ) : COLOR { float4 shad0_col = tex2D(s_T0, In.oT0); return direct_shadow(shad0_col, In.Height); } float4 ps_2_shadow_direct( in VS_OUTPUT_2_SHADOW In ) : COLOR { float4 shad0_col = tex2D(s_T0, In.oT0); float4 shad1_col = tex2D(s_T1, In.oT1); return direct_shadow(shad0_col, In.Height) * direct_shadow(shad1_col, In.Height); } float4 ps_3_shadow_direct( in VS_OUTPUT_3_SHADOW In ) : COLOR { float4 shad0_col = tex2D(s_T0, In.oT0); float4 shad1_col = tex2D(s_T1, In.oT1); float4 shad2_col = tex2D(s_T2, In.oT2); return direct_shadow(shad0_col, In.Height) * direct_shadow(shad1_col, In.Height) * direct_shadow(shad2_col, In.Height); } float4 ps_4_shadow_direct( in VS_OUTPUT_4_SHADOW In ) : COLOR { float4 shad0_col = tex2D(s_T0, In.oT0); float4 shad1_col = tex2D(s_T1, In.oT1); float4 shad2_col = tex2D(s_T2, In.oT2); float4 shad3_col = tex2D(s_T3, In.oT3); return direct_shadow(shad0_col, In.Height) * direct_shadow(shad1_col, In.Height) * direct_shadow(shad2_col, In.Height) * direct_shadow(shad3_col, In.Height); }