Jump to content
  • Advertisement
Sign in to follow this  

Deferred Shading + Cascaded Shadow Maps

This topic is 2569 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts


I have a CSM which worked with forwrard shading, but now, I have deferred shading, but I'm in troubles with it.

In the CSM class, I have a Final Matrix each split:

float texOffset = 0.5f + (0.5f / (float)m_ShadowSize);
m_TextureMatrix = Matrix(0.5f, 0, 0, 0, 0, -0.5f,0, 0, 0, 0, 1, 0, texOffset, texOffset, -(2.5f / (float)m_ShadowSize), 1);

m_FinalMatrices = new Matrix[m_NumSplits];
m_FinalMatrices[p_CurrSplit] = m_ViewProj * m_TextureMatrix;
This matrices were correct with forward shading so I think the matrices are good.

The shadow map stored in a texture atlas (now 1024x1024) 4 * 512x512 maps.

Ofc, I pass the values to the shader.

In the shader, I have an exponential depth from the cam's eye position: (the stored depth map)
[source]float depthText = tex2D(samplerDepth, Texcoord);[/source]
and recompute the world position:
[source]float4 position;
position.x = Texcoord.x * 2.0f - 1.0f;
position.y = -(Texcoord.y * 2.0f - 1.0f);
position.z = depthText;
position.w = 1.0f;
position = mul(position, invViewProj);
position /= position.w;[/source]
Then, I compute the [0;camFar] linear depth

[source]float linearZ = (2.0f * camNear) / (camFar + camNear - depthText * (camFar - camNear));
linearZ *= camFar;[/source]

Now, I try to select the split:

[source]int id = 0;

if (Depth <= splitDistances[0])
else if (Depth <= splitDistances[1])
id = 1;
else if (Depth <= splitDistances[2])
id = 2;
else if (Depth <= splitDistances[3])
id = 3;
return 1.0f;
I think it works fine, because when I put an return value inside the statements, I saw the splits correctly.

From the last version of CSM shader, I compute a float4 value, which should be my texcoord for the shadow map

[source]float4 Shadow = mul(WorldPos, shadowMatrix[id]);
Shadow.xy /= Shadow.w;
return Shadow.z < tex2D(samplerShadow, Shadow);[/source]
It should work, but everyting is black.

Can anyone help me?

PS: sorry for my bad english :)

Share this post

Link to post
Share on other sites
I was trying to solve the same problem you have when I came across this topic. I found a solution that worked for me, so this may be of some help.

Your depth comparisons look right, there's nothing much else to say there. It's likely that the problem lies in selecting the right texture matrix along with the correct texel offset in the texture atlas. I use separate render textures instead of a texture atlas. It's less efficient but easier to understand and implement. It's easier to know where you're sampling from when selecting the appropriate depth range. If you are encountering a problem, it might be better to fall back on using that approach until it works right. The world position looks correct though.

Also, your linear depth calculation looks too drawn out and can be shortened further. If your depth map pixel output is simply a 0-1 range obtained with position.z / position.w, then the following code should be sufficient to convert that to linear view space:

float linearZ = (-camNear * camFar) / (depthText - camFar);

I'm assuming that camNear and camFar are within (0... 1) and camNear is some small non-zero value. If camFar is 1, you can leave out the multiplication in the numerator.

Instead of using a texture matrix, I calculate the projected texture coordinates on the shader, based on the light's view matrix for that particular cascade split, so it's standard shadow mapping stuff so far. All the view matrices are stored in an array of float4x4's.

[font="Arial"][source lang='hlsl'][/font]

[font="Arial"] float shadowIndex = maxIndex - (
(linearZ < cascadeSplits.z) + (linearZ < cascadeSplits.y) +
(linearZ < cascadeSplits.x));

float4 shadowMapPos = mul(position, lightViewProj[shadowIndex]);
float2 shadowTexCoord = shadowMapPos.xy / shadowMapPos.w / 2.0f + float2( 0.5, 0.5 );
shadowTexCoord.y = 1 - shadowTexCoord.y;[/font]


How I choose the appropriate light view matrix should probably be explained. In this example, 4 shadow maps are used based on four different cascades. I store the far Z distance in a (0... 1) range for the first three cascades in a Vector3 and compare their depths against the depth buffer pixel in linear space. No need to compare the fourth, because all pixels will lie closer or equal to that one.

For 4 shadow maps, maxIndex is 3, and that gets subtracted by one whenever one of those statements evaluates to true. If linear Z is closer than the third split distance (cascadeSplits.z) then we know it's not in the fourth split, this will return true it subtracts one from shadowIndex. We keep subtracting if it's closer than the second and first splits, or stop when it fails one of the comparisons (if it's not closer than the third split we already know the other two tests will return false). The comparisons could be added up in any order, but I go farthest to nearest because it's easier to see what it's doing.

This calculation is error-free and gives you the the right index every time. I could just as well use if-else statements but I wanted to remove branching wherever possible.

The rest of the code is longer but it's more along the lines of typical shadow mapping stuff. The if-else statements do the linear Z comparison again, just easier to read than the boolean subtraction I did before. I could not get rid of the branching statements here due to the fact that you need to use a literal for the sampler index. LinearFilter4Samples is separate function that filters the shadow mapping manually. If you don't need to use one, just substitute it for the typical boolean comparison to see if a pixel is lit or not.

[font="Arial"][source lang='hlsl'][/font]

[font="Arial"] float shadow = 0;
[/font][font="Arial"] float shadowDepth = 0;[/font]
[font="Arial"] float occluderDepth = (shadowMapPos.z / shadowMapPos.w) - DepthBias;[/font]

[font="Arial"] if (linearZ < cascadeSplits.x)[/font]
[font="Arial"] {[/font]
[font="Arial"] shadowDepth = tex2D(shadowMapSampler[0], shadowTexCoord).r;[/font]
[font="Arial"] shadow = LinearFilter4Samples(shadowMapSampler[0], shadowTexCoord, occluderDepth);[/font]
[font="Arial"] }[/font]
[font="Arial"] else if (linearZ < cascadeSplits.y)[/font]
[font="Arial"] {[/font]
[font="Arial"] shadowDepth = tex2D(shadowMapSampler[1], shadowTexCoord).r;[/font]
[font="Arial"] shadow = LinearFilter4Samples(shadowMapSampler[1], shadowTexCoord, occluderDepth);[/font]
[font="Arial"] }[/font]
[font="Arial"] else if (linearZ < cascadeSplits.z)[/font]
[font="Arial"] {[/font]
[font="Arial"] shadowDepth = tex2D(shadowMapSampler[2], shadowTexCoord).r;[/font]
[font="Arial"] shadow = LinearFilter4Samples(shadowMapSampler[2], shadowTexCoord, occluderDepth);[/font]
[font="Arial"] }[/font]
[font="Arial"] else[/font]
[font="Arial"] {[/font]
[font="Arial"] shadowdepth = tex2D(shadowMapSampler[3], shadowTexCoord).r;[/font]
[font="Arial"] shadow = LinearFilter4Samples(shadowMapSampler[3], shadowTexCoord, occluderDepth);[/font]
[font="Arial"] }[/font]

[font="Arial"]Hope this has helped you in getting everything else working.[/font]

Share this post

Link to post
Share on other sites
Sign in to follow this  

  • Advertisement

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!