#include "$ENGINE$\PerObjectData.bslinc" #include "$ENGINE$\VertexInput.bslinc" mixin ShadowDepthBase { mixin PerObjectData; mixin VertexInput; // Render back-faces to reduce shadow acne (assuming thick geometry everywhere) raster { cull = cw; }; code { struct ShadowVStoFS { float4 position : SV_Position; #ifdef USES_GS float4 worldPos : TEXCOORD0; #else #ifdef USES_PS float shadowPos : TEXCOORD0; #endif #endif }; cbuffer ShadowParams { float4x4 gMatViewProj; float2 gNDCZToDeviceZ; float gDepthBias; float gInvDepthRange; }; /** Converts Z value from device range ([0, 1]) to NDC space. */ float DeviceZToNDCZ(float deviceZ) { return deviceZ / gNDCZToDeviceZ.x - gNDCZToDeviceZ.y; } /** Converts Z value from NDC space to device Z value in range [0, 1]. */ float NDCZToDeviceZ(float ndcZ) { return (ndcZ + gNDCZToDeviceZ.y) * gNDCZToDeviceZ.x; } ShadowVStoFS vsmain(VertexInput_PO input) { ShadowVStoFS output; float4 worldPosition = getVertexWorldPosition(input); // If using a geometry shader, just pass through the relevant information // as the GS does the necessary transform and applies the depth bias #ifdef USES_GS output.worldPos = worldPosition; output.position = worldPosition; #else // USES_GS // Not using a geometry shader, transform to clip space float4 clipPos = mul(gMatViewProj, worldPosition); // Clamp geometry behind the near plane #ifdef CLAMP_TO_NEAR_PLANE float ndcZ = clipPos.z / clipPos.w; float deviceZ = NDCZToDeviceZ(ndcZ); #ifdef USES_PS if (deviceZ < 0) #else if (deviceZ < -gDepthBias) #endif { clipPos.z = DeviceZToNDCZ(0); clipPos.w = 1.0f; } #endif // CLAMP_TO_NEAR_PLANE // If using a pixel shader, output shadow depth in clip space, as // we'll apply bias to it in PS (depth needs to be interpolated in // a perspective correct way) #ifdef USES_PS output.shadowPos = clipPos.z; #else // Otherwise apply bias immediately clipPos.z = max(DeviceZToNDCZ(0), clipPos.z + gDepthBias); #endif // USES_PS output.position = clipPos; #endif // USES_GS return output; } }; };