Skip to main content
gamedev.net
🔒 Locked

Shadow mapping issue

Started by Fibonacci One 8 replies 2.4k views
Original Post
Fibonacci One
Fibonacci One
I'm attempting to create shadows for my scene by rendering all of the shadow casting objects to a shadow map then using that information in the shadow receiving objects to know whether or not the object is in shadow. Sounds pretty standard so far, right? I think that my problem arrives when I am calculating the texture coordinates to use to look at the shadow map. I'll illustrate the problem with a screenshot; this is the shadow map being shown on the terrain here. (To see the image you'll probably need right click and select view image, or left click to follow the link.) The light for the scene is down behind the hill and so when I move it into projective space it actually ends up with the same coordinates as the area on the ground behind the player. So, obviously, when the sun is passing below the terrain like that I'll end up with shadows where they shouldn't be. Here's what I think is the relevant source:

float4 projectivePos = mul(dpi.worldPosition, lightViewProj);
projectivePos  = projectivePos  / projectivePos .w;
float2 depthTexCoords = projectivePos .xy * float2(0.5, -0.5) + 0.5;


Does anyone have any idea what I'm doing wrong or what else I need to do to fix this? Thanks in advance.
Schrompf
Schrompf
What depth are you comparing against when rendering the scene? This double shadow can only mean that your light depth grows in both directions. Otherwise the landscape in the back should have a negative light depth and shouldn't be shadowed at all.

To put it this way: you store the distance from some light plane in the shadow map. Then you calculate the distance from the light plane for all screen pixels and compare them to the distance stored in the shadow map. The picture you posted looks like your light base plane (where the depth is 0) lays somewhere inbetween those two shadows and the distance from this plane (the light depth) grows in both directions. So some terrain in the backside has a positive light depth and fails the shadow test, producing this fake shadow.

Try to use an orthogonal projection matrix for the sun light. The perspective division then is not needed anymore. And I guess this division produces the mirrored depth values. You do use the perspective divided Z as compare depth, do you? Try using the undivided depth instead, that should solve the issue as well.

Bye, Thomas
----------
Gonna try that "Indie" stuff I keep hearing about. Let's start with Splatter.
Fibonacci One
Fibonacci One
Ok, I think I kind of understand what you're saying here. With this screenshot I'm not actually doing any comparisions to the shadow map, I'm just rendering it directly. This is the bulk of what I'm doing there:
float4 projectivePos = mul(dpi.worldPosition, lightViewProj);projectivePos = projectivePos / projectivePos.w;float2 depthTexCoords = projectivePos.xy * float2(0.5, -0.5) + 0.5;// Read the moments from the shadow map.float4 moments = tex2D(shadowSampler, depthTexCoords);return moments.xxxx;


Now, to use an orthogonal projection matrix I just need to replace something like D3DXMatrixPerspectiveFovLH(&lightProj, D3DX_PI/4, 1.0f, 1.0f, 50.0f) into something like D3DXMatrixOrthoLH(&lightProj, ShadowMapSize, ShadowMapSize, 1.0f, 50.0f) and then get rid of the divide by w in the above code? I'm assuming there's more to it than that, since I tried it and it didn't work.

Thanks for your help.
Schrompf
Schrompf
Didn't know this is actual pixel shader source. Sorry, I expected the texture coord generation to be within the vertex shader and thought you simply left out the pixel shader part.

Without any depth comparision, it looks exactly like it should. The mirrored image should vanish as soon as you start comparing depths. That's because the scene at the mirror image has a negative depth from the light's point of view so all depth comparisions will succeed, rendering light everywhere. And that's what you want after all. Only the scene at the real image in front has a positive depth from the light's point of view. There the depth comparision will render shadow and light as intended.
----------
Gonna try that "Indie" stuff I keep hearing about. Let's start with Splatter.
AndyTX
AndyTX
Yeah that looks correct to me... just add the shadow comparison and all should be good.

One note: if you're doing variance shadow mapping you have to make sure that you're rendering receivers into the shadow map as well (i.e. the terrain). This is necessary for the interpolation and filtering to work. You also must render front faces (not back faces or midpoints).
Fibonacci One
Fibonacci One
Quote:
Original post by Schrompf
That's because the scene at the mirror image has a negative depth from the light's point of view so all depth comparisions will succeed, rendering light everywhere. And that's what you want after all. Only the scene at the real image in front has a positive depth from the light's point of view. There the depth comparision will render shadow and light as intended.

So, the problem is the way I'm calculating the depth from the lights point of view. How am I suppose to do that then? It seems to me that I should just multiply the world position by the light's view matrix and then take the length of that. However, that still gives me the same problem. Or, maybe that's right and my comparison is incorrect. Here's what I've got:

// I moved these to the vertex shader// Calculate the texture coordsfloat4 tempPos = mul(position, lightViewProj);tempPos = tempPos / tempPos.w;dpo.depthTexCoords = tempPos.xy * float2(0.5, -0.5) + 0.5;dpo.lightVec = mul(position, lightViewInv).xyz;// And in the pixel shaderfloat distToLight = length(dpi.lightVec);	// Read the moments from the shadow map.float4 moments = tex2D(shadowSampler, dpi.depthTexCoords);	// Calculate if this point is in light or shadow.float isLit = distToLight / 50.0f <= moments.x;// Calculate the final colorfloat3 resultColor = ((ndotl * isLit) + ndotl2) * color;


Quote:
Original post by AndyTX
One note: if you're doing variance shadow mapping you have to make sure that you're rendering receivers into the shadow map as well (i.e. the terrain). This is necessary for the interpolation and filtering to work. You also must render front faces (not back faces or midpoints).

Shoot, that forces me to have the receivers cast shadows too, doesn't it? I really didn't want my terrain casting shadows since it makes it obvious how jagged it can be at some places. Oh well...

Thanks to both of you. Ratings++ if I havn't already.
Schrompf
Schrompf
Don't use the length for comparision. That's fine for a point light because it radiates in all direction. For a sun like parallel lightsource with an orthogonal projection matrix you simply use the post projection depth. Due to the mathematical nature of a orthogonal projection matrix this depth is normalised to 0 .. 1, thus making an ideal candidate for storing in textures and comparing.

The trick is to find the bounding box from which you construct this othogonal projection matrix. You want it as small as possible to have maximal shadow resolution but you need it large enough that it contains at least every object that is visible at the moment. Plus some extend towards the light so that objects outside of the view can cast shadows into the scene.

This is no trivial problem. If you consider the average view distance most games today provide, your shadow map stretches over a very large area and shadow resolution is very bad. How to improve the resolution is an actual science for itsself. Leave this topic for later, the "normal" uniform shadow mapping has to be working first.

[edit]
You say the double shadow is still there? That's no miracle, the length of an vector is always positive. Use the post projection depth instead, but don't divide it by w. If you divide by w, negative depths get positive again because w is also negative where z is negative.

Bye, Thomas
----------
Gonna try that "Indie" stuff I keep hearing about. Let's start with Splatter.
AndyTX
AndyTX
Quote:
Original post by Schrompf
For a sun like parallel lightsource with an orthogonal projection matrix you simply use the post projection depth.

Actually if you're doing VSM ("moments" in the shader tips me off), post projection depth is usually a bad idea due to it being highly nonlinear: it will introduce a number of numeric stability problems. You'll want to use some sort of linearized depth, potentially the distance to the light's projection plane.

Quote:
Original post by Schrompf
You say the double shadow is still there? That's no miracle, the length of an vector is always positive.

Any "double" or "reverse" projection should be blacked out by your light extents. Do this separately - it's trivial math and relevant to non-shadowing lights as well.
jamesw
jamesw
Quote:
One note: if you're doing variance shadow mapping you have to make sure that you're rendering receivers into the shadow map as well (i.e. the terrain). This is necessary for the interpolation and filtering to work.


Could you elaborate on that? I've done VSM with only the casters into the moments map and don't remember having any issues with it.
AndyTX
AndyTX
Quote:
Original post by jamesw
Could you elaborate on that? I've done VSM with only the casters into the moments map and don't remember having any issues with it.

The interpolation actually depends on the receiver being one of the interpolants. For example if you're blurring the depth map, you can see how not rendering a receiver (and instead having infinity or 1.0 as a background colour) would change the results. Without going into too much detail, the result of VSM will simply be wrong for a receiver that was not rendered into the moments map.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.