Shadow Map Depth Issue

Started by
8 comments, last by jeffkingdev 11 years, 7 months ago
Hey guys,

Anyone know why my shadow map doesn't show much depth unless I get SUPER close to an object. Normally in shadow maps you can see the black to white, but mine only does that when I'm right next to an object, literately on top of it.

After transforming the position into worldviewprojection, I do the pos.z / pos.w as my depth value I use in my pixel shader.

Any idea why the depth value only seems to change when objects are right in front of me? I want to see a gradient for stuff in the distance.

Thanks
Jeff.
Advertisement
Could be a few things... one is that the image is actually correct, but looks like it cuts off due to most values being "white" or "almost white" due to a low near plane value.
When you display your shadowmap as greyscale, you're quantising the 24-bit value down to 8 bits. For every shade of grey, there's 65k actual values present.
N.B. half of your depth-buffer precision is given to values from [font=courier new,courier,monospace]near[/font] to [font=courier new,courier,monospace]2*near[/font].
What near plane value are you using?
Hodgman,

You might be right and that might be part of my problem. Here's some screenshots of my shadow map with various near/far values. Notice that even with the range in the most optimal setting for the scene (near: 30, far: 200), it still doesn't do a nice gradient that I see on other websites.

SHADOW MAP: (near: 0.1, far: 200)
[see attached sm-0.1-200.jpg]

SHADOW MAP: (near: 10 far: 200)
[see attached sm-10-200.jpg]

SHADOW MAP: (near: 30, far: 1000)
[see attached sm-30-200.jpg]

The tree in the upper-right is the furthest back. I would expect to see a difference between that tree and the middle ones, but it appears only the first tree is showing the depth.

Any ideas?

Here's my shadow map generation shader (fx)


/*----------------------------------------------------------------------------
*/
/*--------------------------------------------------------------------------*/
/* Register Inputs */
/*--------------------------------------------------------------------------*/
float4x4 mtxWorldViewProj : WORLDVIEWPROJECTION;

/*--------------------------------------------------------------------------*/
/* Data Structures */
/*--------------------------------------------------------------------------*/
struct VertexDataIn
{
float3 Pos3 : POSITION;
};
struct VertexDataOut
{
float4 Pos4 : POSITION;
float Depth : COLOR0;
};

//-----------------------------------------------------------------------------
// Vertex Shader: VertShadow
// Desc: Process vertex for the shadow map
//-----------------------------------------------------------------------------
VertexDataOut VertShadow( VertexDataIn IN )
{
VertexDataOut vOut = (VertexDataOut)0;
vOut.Pos4 = mul(float4(IN.Pos3,1.0), mtxWorldViewProj);
// Depth is z / w
vOut.Depth = vOut.Pos4.z / vOut.Pos4.w;
return(vOut);
}
//-----------------------------------------------------------------------------
// Pixel Shader: PixShadow
// Desc: Process pixel for the shadow map
//-----------------------------------------------------------------------------
float4 PixShadow( VertexDataOut IN ) : COLOR
{
return(float4(IN.Depth,IN.Depth,IN.Depth,1.0));
}

/*----------------------------------------------------------------------------
TECHNIQUE
*/
technique technique0
{
pass p0
{
VertexShader = compile vs_2_0 VertShadow();
PixelShader = compile ps_2_0 PixShadow ();
}
}
When interpolating values from the vertex shader outputs to the pixel shader inputs, the hardware automatically divides everything by [font=courier new,courier,monospace]vOut.Pos4.w[/font] (this is how perspective-correct interpolation works).
So by the time you get your depth value in your pixel shader, you're actually using [font=courier new,courier,monospace]vOut.Pos4.z / vOut.Pos4.w / vOut.Pos4.w;[/font]

Also, are you outputting depth as colour simply as a debugging measure, or are you using that colour texture as a shadow map? What format is your colour texture?
Hodgman,

But I'm only dividing for the output value "Depth" which shouldn't get divided further -- correct? So wouldn't the pixel shader have the original z / w? and the input variable "Depth" should not get divided by w.

vOut.Depth = vOut.Pos4.z / vOut.Pos4.w;

If that's wrong, what should it be?

Also, I am using the color texture as my shadow map. My shadow map format is D3DFMT_A8R8G8B8.

Thanks
Jeff.
You have a few problems:

  1. You need to do the perspective divide in the pixel shader, otherwise the value won't get interpolated correctly. Output both Z and W from your vertex shader, then divide before outputting the value in your pixel shader.
  2. When using a perspective projection, z/w will be highly non-linear. Typically most of your depth range ends up being in the 0.9-1.0 range. If you want to visualize a perspective depth buffer, you should either remap the value from 0.9-1.0 ( like this: depth = saturate((depth - 0.9) * 10); ), or you should convert the value back to a linear Z value like I explained in another thread.
  3. 8-bits is not enough precision for a shadow map. You would be a lot better of with D3DFMT_R32F.
MJP,

I have another question that relates to your #2 answer. I noticed some tutorials do an orthographic projection for the shadow map. Why and how does that work? That seems like it wouldn't since there's no depth. Also, related to your response, would a z/w for an orthographic projection be better to visualize/use?

Thanks,
Jeff.
Orthographic projections work just fine with shadow maps. The resulting depth value will actually be linear [0, 1] (where 0 is then near clip and 1 is the far clip) so there's no need to convert or rescale if you want to visualize it. In fact with an orthgraphic projection W is always 1.0, so you don't even need to divide by W like you do with a perspective projection.

People usually use orthographic projections for sunlight shadows, since the light direction is constant for a directional light. Perspective projections are often used for spot lights, since a perspective projection better fits the cone-shaped volume affected by a spot light.
MJP,

Thanks for that explanation. That makes a lot of sense.

Ok, I changed my shadow projection to be orthographic. I also changed my texture format to R32F. I noticed my output of the shadow map was trying to access the r,g,b values, so now, my output shader looks like:

float depth = tex2D( ShadowMap, IN.Tex2).r;
return(float4(depth, depth, depth, 1.0));

My orthographic view is from the sun's direction. However, my map is again all white. I put in a dinosaur model I had that's pretty large, so I should see a gradient on his neck showing that depth. All I see is my attached.

I did change my shadow generation shader to use just the z value instead of doing the divide (because I'm using an ortho projection) -- I did try the divide, but it did nothing else, like you said it should be 1.

My shadow generation shader now outputs it's color like this:

return(float4(depth,0.0,0.0,1.0));

Notice, the 1.0 for the alpha, if I set that to 0, I don't see anything in my shadow map. I thought that was odd, since I don't reference the alpha in the other shader.

Any clues??
Thanks again!
Jeff.
This might help too. Just to add one more note, I'm working on cascading shadow maps, so the map I'm working with is the first frustum split. When I create my orthographic projection, I have to set near/far to 0.0 and 1.0. When I tried to use min.z and max.z it doesn't work.

// this is my own matrix function
GlMatrixOrthoOffCenterLH(&mShadowProj, test.min.x, test.max.x, test.min.y, test.max.y, 0.0, 1.0);

I noticed various sites had -max.z for the near and -min.z for the far which doesn't make any sense to me. That doesn't work either. My values for min.z/max.z change, but for example, it's 450 & 485 (example values). So, I doubt those are good near/far values. Any idea what's wrong with that?

Thanks
Jeff.

This topic is closed to new replies.

Advertisement