/* This file implements standard programs for depth shadow mapping. These particular ones are suitable for additive lighting models, and include 3 techniques to reduce depth fighting on self-shadowed surfaces, constant bias, gradient (slope-scale) bias, and a fuzzy shadow map comparison*/ // Shadow caster vertex program. void casterVP( float4 position : POSITION, out float4 outPos : POSITION, out float2 outDepth : TEXCOORD0, uniform float4x4 worldViewProj, uniform float4 texelOffsets, uniform float4 depthRange ) { outPos = mul(worldViewProj, position); // fix pixel / texel alignment outPos.xy += texelOffsets.zw * outPos.w; // linear depth storage // offset / scale range output #if LINEAR_RANGE outDepth.x = (outPos.z - depthRange.x) * depthRange.w; #else outDepth.x = outPos.z; #endif outDepth.y = outPos.w; } // Shadow caster fragment program for high-precision single-channel textures void casterFP( float2 depth : TEXCOORD0, out float4 result : COLOR) { #if LINEAR_RANGE float finalDepth = depth.x; #else float finalDepth = depth.x / depth.y; #endif // just smear across all components // therefore this one needs high individual channel precision result = float4(finalDepth, finalDepth, finalDepth, 1); } void receiverVP( float4 position : POSITION, float4 normal : NORMAL, out float4 outPos : POSITION, out float4 outColour : COLOR, out float4 outShadowUV : TEXCOORD0, uniform float4x4 world, uniform float4x4 worldIT, uniform float4x4 worldViewProj, uniform float4x4 texViewProj, uniform float4 lightPosition, uniform float4 lightColour, uniform float4 shadowDepthRange ) { float4 worldPos = mul(world, position); outPos = mul(worldViewProj, position); float3 worldNorm = mul(worldIT, normal).xyz; // calculate lighting (simple vertex lighting) float3 lightDir = normalize( lightPosition.xyz - (worldPos.xyz * lightPosition.w)); outColour = lightColour * max(dot(lightDir, worldNorm), 0.0); // calculate shadow map coords outShadowUV = mul(texViewProj, worldPos); #if LINEAR_RANGE // adjust by fixed depth bias, rescale into range outShadowUV.z = (outShadowUV.z - shadowDepthRange.x) * shadowDepthRange.w; #endif } void receiverFP( float4 position : POSITION, float4 shadowUV : TEXCOORD0, float4 vertexColour : COLOR, uniform sampler2D shadowMap : register(s0), uniform float inverseShadowmapSize, uniform float fixedDepthBias, uniform float gradientClamp, uniform float gradientScaleBias, uniform float shadowFuzzyWidth, out float4 result : COLOR) { // point on shadowmap #if LINEAR_RANGE shadowUV.xy = shadowUV.xy / shadowUV.w; #else shadowUV = shadowUV / shadowUV.w; #endif float centerdepth = tex2D(shadowMap, shadowUV.xy).x; // gradient calculation float pixeloffset = inverseShadowmapSize; float4 depths = float4( tex2D(shadowMap, shadowUV.xy + float2(-pixeloffset, 0)).x, tex2D(shadowMap, shadowUV.xy + float2(+pixeloffset, 0)).x, tex2D(shadowMap, shadowUV.xy + float2(0, -pixeloffset)).x, tex2D(shadowMap, shadowUV.xy + float2(0, +pixeloffset)).x); float2 differences = abs( depths.yw - depths.xz ); float gradient = min(gradientClamp, max(differences.x, differences.y)); float gradientFactor = gradient * gradientScaleBias; // visibility function float depthAdjust = gradientFactor + (fixedDepthBias * centerdepth); float finalCenterDepth = centerdepth + depthAdjust; // shadowUV.z contains lightspace position of current object #if FUZZY_TEST // fuzzy test - introduces some ghosting in result and doesn't appear to be needed? //float visibility = saturate(1 + delta_z / (gradient * shadowFuzzyWidth)); float visibility = saturate(1 + (finalCenterDepth - shadowUV.z) * shadowFuzzyWidth * shadowUV.w); result = vertexColour * visibility; #else // hard test #if PCF // use depths from prev, calculate diff depths += depthAdjust.xxxx; float final = (finalCenterDepth > shadowUV.z) ? 1.0f : 0.0f; final += (depths.x > shadowUV.z) ? 1.0f : 0.0f; final += (depths.y > shadowUV.z) ? 1.0f : 0.0f; final += (depths.z > shadowUV.z) ? 1.0f : 0.0f; final += (depths.w > shadowUV.z) ? 1.0f : 0.0f; final *= 0.2f; result = float4(vertexColour.xyz * final, 1); #else result = (finalCenterDepth > shadowUV.z) ? vertexColour : float4(0,0,0,1); #endif #endif } // Expand a range-compressed vector float3 expand(float3 v) { return (v - 0.5) * 2; } /* Normal mapping plus depth shadowmapping receiver programs */ void normalMapShadowReceiverVp(float4 position : POSITION, float3 normal : NORMAL, float2 uv : TEXCOORD0, float3 tangent : TANGENT0, // outputs out float4 outPos : POSITION, out float4 outShadowUV : TEXCOORD0, out float2 oUv : TEXCOORD1, out float3 oTSLightDir : TEXCOORD2, // parameters uniform float4 lightPosition, // object space uniform float4x4 world, uniform float4x4 worldViewProj, uniform float4x4 texViewProj) { float4 worldPos = mul(world, position); outPos = mul(worldViewProj, position); // calculate shadow map coords outShadowUV = mul(texViewProj, worldPos); #if LINEAR_RANGE // adjust by fixed depth bias, rescale into range outShadowUV.z = (outShadowUV.z - shadowDepthRange.x) * shadowDepthRange.w; #endif // pass the main uvs straight through unchanged oUv = uv; // calculate tangent space light vector // Get object space light direction // Non-normalised since we'll do that in the fragment program anyway float3 lightDir = lightPosition.xyz - (position * lightPosition.w); // Calculate the binormal (NB we assume both normal and tangent are // already normalised) // NB looks like nvidia cross params are BACKWARDS to what you'd expect // this equates to NxT, not TxN float3 binormal = cross(tangent, normal); // Form a rotation matrix out of the vectors float3x3 rotation = float3x3(tangent, binormal, normal); // Transform the light vector according to this matrix oTSLightDir = mul(rotation, lightDir); } void normalMapShadowReceiverFp( float4 shadowUV : TEXCOORD0, float2 uv : TEXCOORD1, float3 TSlightDir : TEXCOORD2, out float4 result : COLOR, uniform float4 lightColour, uniform float inverseShadowmapSize, uniform float fixedDepthBias, uniform float gradientClamp, uniform float gradientScaleBias, uniform float shadowFuzzyWidth, uniform sampler2D shadowMap : register(s0), uniform sampler2D normalMap : register(s1), uniform samplerCUBE normalCubeMap : register(s2)) { // retrieve normalised light vector, expand from range-compressed float3 lightVec = expand(texCUBE(normalCubeMap, TSlightDir).xyz); // get bump map vector, again expand from range-compressed float3 bumpVec = expand(tex2D(normalMap, uv).xyz); // Calculate dot product float4 vertexColour = lightColour * dot(bumpVec, lightVec); // point on shadowmap #if LINEAR_RANGE shadowUV.xy = shadowUV.xy / shadowUV.w; #else shadowUV = shadowUV / shadowUV.w; #endif float centerdepth = tex2D(shadowMap, shadowUV.xy).x; // gradient calculation float pixeloffset = inverseShadowmapSize; float4 depths = float4( tex2D(shadowMap, shadowUV.xy + float2(-pixeloffset, 0)).x, tex2D(shadowMap, shadowUV.xy + float2(+pixeloffset, 0)).x, tex2D(shadowMap, shadowUV.xy + float2(0, -pixeloffset)).x, tex2D(shadowMap, shadowUV.xy + float2(0, +pixeloffset)).x); float2 differences = abs( depths.yw - depths.xz ); float gradient = min(gradientClamp, max(differences.x, differences.y)); float gradientFactor = gradient * gradientScaleBias; // visibility function float depthAdjust = gradientFactor + (fixedDepthBias * centerdepth); float finalCenterDepth = centerdepth + depthAdjust; // shadowUV.z contains lightspace position of current object #if FUZZY_TEST // fuzzy test - introduces some ghosting in result and doesn't appear to be needed? //float visibility = saturate(1 + delta_z / (gradient * shadowFuzzyWidth)); float visibility = saturate(1 + (finalCenterDepth - shadowUV.z) * shadowFuzzyWidth * shadowUV.w); result = vertexColour * visibility; #else // hard test #if PCF // use depths from prev, calculate diff depths += depthAdjust.xxxx; float final = (finalCenterDepth > shadowUV.z) ? 1.0f : 0.0f; final += (depths.x > shadowUV.z) ? 1.0f : 0.0f; final += (depths.y > shadowUV.z) ? 1.0f : 0.0f; final += (depths.z > shadowUV.z) ? 1.0f : 0.0f; final += (depths.w > shadowUV.z) ? 1.0f : 0.0f; final *= 0.2f; result = float4(vertexColour.xyz * final, 1); #else result = (finalCenterDepth > shadowUV.z) ? vertexColour : float4(0,0,0,1); #endif #endif }