|
| 1 | +// Screen-Space Shadow Application Shader |
| 2 | +// Applied as a fullscreen pass to composite shadows onto the rendered scene |
| 3 | + |
| 4 | +#include "PostProcessBase.hlsl" |
| 5 | + |
| 6 | +cbuffer ShadowData : register(b0) |
| 7 | +{ |
| 8 | + row_major matrix matLightViewProj; // Light space transformation |
| 9 | + row_major matrix matInvViewProj; // Inverse of camera view-projection (for position reconstruction) |
| 10 | + float3 LightDirection; |
| 11 | + float ShadowBias; |
| 12 | + float ShadowIntensity; // How dark shadows are (0 = black, 1 = no shadow) |
| 13 | + float2 padding; |
| 14 | +} |
| 15 | + |
| 16 | +Texture2D colorTexture : register(t0); // Scene color (backbuffer) |
| 17 | +Texture2D depthTexture : register(t1); // Scene depth |
| 18 | +Texture2D shadowMap : register(t2); // Shadow map from light |
| 19 | + |
| 20 | +SamplerState colorSampler : register(s0); |
| 21 | +SamplerState depthSampler : register(s1); |
| 22 | +SamplerState shadowSampler : register(s2); |
| 23 | + |
| 24 | +// Reconstruct world position from depth buffer |
| 25 | +float3 ReconstructWorldPosition(float2 uv, float depth) |
| 26 | +{ |
| 27 | + // Convert UV and depth to NDC coordinates |
| 28 | + float4 ndc; |
| 29 | + ndc.x = uv.x * 2.0 - 1.0; |
| 30 | + ndc.y = (1.0 - uv.y) * 2.0 - 1.0; // Flip Y |
| 31 | + ndc.z = depth; |
| 32 | + ndc.w = 1.0; |
| 33 | + |
| 34 | + // Transform to world space |
| 35 | + float4 worldPos = mul(ndc, matInvViewProj); |
| 36 | + worldPos /= worldPos.w; // Perspective divide |
| 37 | + |
| 38 | + return worldPos.xyz; |
| 39 | +} |
| 40 | + |
| 41 | +float CalculateShadow(float3 worldPos) |
| 42 | +{ |
| 43 | + // Transform world position to light space |
| 44 | + float4 lightSpacePos = mul(float4(worldPos, 1.0), matLightViewProj); |
| 45 | + |
| 46 | + // Perform perspective divide |
| 47 | + float3 projCoords = lightSpacePos.xyz / lightSpacePos.w; |
| 48 | + |
| 49 | + // Transform to [0,1] range for texture sampling |
| 50 | + projCoords.x = projCoords.x * 0.5 + 0.5; |
| 51 | + projCoords.y = projCoords.y * -0.5 + 0.5; // Flip Y for D3D |
| 52 | + |
| 53 | + // Outside shadow map bounds = not in shadow |
| 54 | + if (projCoords.x < 0.0 || projCoords.x > 1.0 || |
| 55 | + projCoords.y < 0.0 || projCoords.y > 1.0 || |
| 56 | + projCoords.z > 1.0) |
| 57 | + return 1.0; |
| 58 | + |
| 59 | + // Get closest depth from shadow map |
| 60 | + float closestDepth = shadowMap.Sample(shadowSampler, projCoords.xy).r; |
| 61 | + |
| 62 | + // Get current fragment depth |
| 63 | + float currentDepth = projCoords.z; |
| 64 | + |
| 65 | + // Compare depths with bias |
| 66 | + float shadow = (currentDepth - ShadowBias) > closestDepth ? ShadowIntensity : 1.0; |
| 67 | + |
| 68 | + return shadow; |
| 69 | +} |
| 70 | + |
| 71 | +float4 PS_Main(VertexOut v) : SV_TARGET |
| 72 | +{ |
| 73 | + // Sample scene color |
| 74 | + float4 color = colorTexture.Sample(colorSampler, v.UV); |
| 75 | + |
| 76 | + // Sample depth |
| 77 | + float depth = depthTexture.Sample(depthSampler, v.UV).r; |
| 78 | + |
| 79 | + // Skip skybox (depth = 1.0 or 0.0 depending on depth configuration) |
| 80 | + if (depth >= 0.9999) |
| 81 | + return color; |
| 82 | + |
| 83 | + // Reconstruct world position from depth |
| 84 | + float3 worldPos = ReconstructWorldPosition(v.UV, depth); |
| 85 | + |
| 86 | + // Calculate shadow factor |
| 87 | + float shadowFactor = CalculateShadow(worldPos); |
| 88 | + |
| 89 | + // Apply shadow to color |
| 90 | + return color * shadowFactor; |
| 91 | +} |
0 commit comments