Bug in Microsoft DirectX shadowmap sample?
In the shadow map sample provided by Microsoft I've noticed an issue where shadows are not properly projected when thin geometry is projected at high angles, see here the shadows being projected, notice the poles from the lights are not projected: http://imgur.com/QwOBa.png
And in this screenshot we see things from the lights perspective, not ethe poles are clearly visible: http://imgur.com/k2woZ.png
So two questions really, is this an actual bug or a limitation with shadow mapping and if it's a bug how can I fix it?
The source is directly from the Microsoft DirectX Sample Browser 'ShadowMap' sample from July 2004, the sample browser is the latest August 2009 one.
Looks like a bug
To fix it you'll have to figure out what's wrong ;)
Most likely the poles aren't being drawn into the shadowmap. You can verify this by looking at the actual shadow map depth buffer. Check and see that the draw calls for the poles exist - if they don't, then for some reason they're being culled. If they do exist, figure out why they're not writing depth - bad render state (depth write off?), wrong shader? or what
Do they *ever* draw? Say, if you move the light?
To fix it you'll have to figure out what's wrong ;)
Most likely the poles aren't being drawn into the shadowmap. You can verify this by looking at the actual shadow map depth buffer. Check and see that the draw calls for the poles exist - if they don't, then for some reason they're being culled. If they do exist, figure out why they're not writing depth - bad render state (depth write off?), wrong shader? or what
Do they *ever* draw? Say, if you move the light?
Yes they do actually draw when the light is not being shown at them from a given distance), the screenshot I showed is a bit dramatic.
In this one you see it is drawing fine: http://imgur.com/R2wEC.png
In this one the only thing that has changed is the distance of the light from the cones cylinders, notice the shadows are no longer touching the end of their respective cylinders: http://i.imgur.com/1bfSh.png
And the further the light gets from the objects it's rendering the less of them we end up seeing, this is independent of the projection matrices near/far points, it always has this effect which has led me to beleive it's happening somewhere in the shadowmap calculations:
I'm not very well versed in shadow maps so I'm not sure what's going wrong here, SHADOW_EPISLON is defined as 0.000005f
I've tweeked the code a bit and found if SHADOW_EPSILON is given a smaller value the issue becomes less prevalent (i.e. the light source can be moved farther away from the geometry its casting shadows against without the shadow detaching). However if I make SHADOW_EPSILON 0 or very close to 0 (like 0.000001f) I end up with artifacts on the screen: http://imgur.com/N3zAN.png
In this one you see it is drawing fine: http://imgur.com/R2wEC.png
In this one the only thing that has changed is the distance of the light from the cones cylinders, notice the shadows are no longer touching the end of their respective cylinders: http://i.imgur.com/1bfSh.png
And the further the light gets from the objects it's rendering the less of them we end up seeing, this is independent of the projection matrices near/far points, it always has this effect which has led me to beleive it's happening somewhere in the shadowmap calculations:
//-----------------------------------------------------------------------------// Pixel Shader: PixScene// Desc: Process pixel (do per-pixel lighting) for enabled scene//-----------------------------------------------------------------------------float4 PixScene( float2 Tex : TEXCOORD0, float4 vPos : TEXCOORD1, float3 vNormal : TEXCOORD2, float4 vPosLight : TEXCOORD3 ) : COLOR{ float4 Diffuse; // vLight is the unit vector from the light to this pixel float3 vLight = normalize( float3( vPos - g_vLightPos ) ); // Compute diffuse from the light if( dot( vLight, g_vLightDir ) > g_fCosTheta ) // Light must face the pixel (within Theta) { // Pixel is in lit area. Find out if it's // in shadow using 2x2 percentage closest filtering //transform from RT space to texture space. float2 ShadowTexC = 0.5 * vPosLight.xy / vPosLight.w + float2( 0.5, 0.5 ); ShadowTexC.y = 1.0f - ShadowTexC.y; // transform to texel space float2 texelpos = SMAP_SIZE * ShadowTexC; // Determine the lerp amounts float2 lerps = frac( texelpos ); //read in bilerp stamp, doing the shadow checks float sourcevals[4]; sourcevals[0] = (tex2D( g_samShadow, ShadowTexC ) + SHADOW_EPSILON < vPosLight.z / vPosLight.w)? 0.0f: 1.0f; sourcevals[1] = (tex2D( g_samShadow, ShadowTexC + float2(1.0/SMAP_SIZE, 0) ) + SHADOW_EPSILON < vPosLight.z / vPosLight.w)? 0.0f: 1.0f; sourcevals[2] = (tex2D( g_samShadow, ShadowTexC + float2(0, 1.0/SMAP_SIZE) ) + SHADOW_EPSILON < vPosLight.z / vPosLight.w)? 0.0f: 1.0f; sourcevals[3] = (tex2D( g_samShadow, ShadowTexC + float2(1.0/SMAP_SIZE, 1.0/SMAP_SIZE) ) + SHADOW_EPSILON < vPosLight.z / vPosLight.w)? 0.0f: 1.0f; // lerp between the shadow values to calculate our light amount float LightAmount = lerp( lerp( sourcevals[0], sourcevals[1], lerps.x ), lerp( sourcevals[2], sourcevals[3], lerps.x ), lerps.y ); // Light it Diffuse = ( saturate( dot( -vLight, normalize( vNormal ) ) ) * LightAmount * ( 1 - g_vLightAmbient ) + g_vLightAmbient ) * g_vMaterial; } else { Diffuse = g_vLightAmbient * g_vMaterial; } return tex2D( g_samScene, Tex ) * Diffuse;}
I'm not very well versed in shadow maps so I'm not sure what's going wrong here, SHADOW_EPISLON is defined as 0.000005f
I've tweeked the code a bit and found if SHADOW_EPSILON is given a smaller value the issue becomes less prevalent (i.e. the light source can be moved farther away from the geometry its casting shadows against without the shadow detaching). However if I make SHADOW_EPSILON 0 or very close to 0 (like 0.000001f) I end up with artifacts on the screen: http://imgur.com/N3zAN.png
Oh weird. So this is some pretty serious imprecision. What is the bit depth of the shadow map? 16bit? 24? 32? One option to help this would be to increase the bit depth
The epsilon there is to offset the depth checked in the shadow map to avoid numerical error, which results in inconsistent self-shadowing, or "surface acne", which is exactly the artifact you show in your last screenshot
What are the near and far planes set to for the shadow camera (not the main camera)? It's possible the depth range is way too big, or the near plane is way too close.
You can also try writing a linearized depth in the shadow map to help make the distribution of precision more uniform, and make the epsilon behave better when the light is far away
The epsilon there is to offset the depth checked in the shadow map to avoid numerical error, which results in inconsistent self-shadowing, or "surface acne", which is exactly the artifact you show in your last screenshot
What are the near and far planes set to for the shadow camera (not the main camera)? It's possible the depth range is way too big, or the near plane is way too close.
You can also try writing a linearized depth in the shadow map to help make the distribution of precision more uniform, and make the epsilon behave better when the light is far away
Imprecision was my first thought too, however the depth map is rendering to a D3DFMT_R32F texture which as far as I know is the standard texture format to use when rendering a depth map,
Where ShadowMap_SIZE is 512, it is also 512 in the shader as SMAP_SIZE
The sample also creates a depth stencil..
I'm not sure if this is releveant to anything specifically.
The projection matrix is created as follows: D3DXMatrixPerspectiveFovLH( &g_mShadowProj, g_fLightFov, 1, 0.01f, 100.0f );
I found that if I did make the near plane larger, say 1, the issue went away however the surface acne (as seen here: http://imgur.com/N3zAN.png) appeared. If I made it something like 0.05f it minimized the occurence of the issue but it still happens, just at slightly larger distances.//
// Create the shadow map texture V_RETURN( pd3dDevice->CreateTexture( ShadowMap_SIZE, ShadowMap_SIZE, 1, D3DUSAGE_RENDERTARGET, D3DFMT_R32F, D3DPOOL_DEFAULT, &g_pShadowMap, NULL ) );
Where ShadowMap_SIZE is 512, it is also 512 in the shader as SMAP_SIZE
The sample also creates a depth stencil..
V_RETURN( pd3dDevice->CreateDepthStencilSurface( ShadowMap_SIZE, ShadowMap_SIZE, d3dSettings.d3d9.pp.AutoDepthStencilFormat, D3DMULTISAMPLE_NONE, 0, TRUE, &g_pDSShadow, NULL ) );
I'm not sure if this is releveant to anything specifically.
The projection matrix is created as follows: D3DXMatrixPerspectiveFovLH( &g_mShadowProj, g_fLightFov, 1, 0.01f, 100.0f );
I found that if I did make the near plane larger, say 1, the issue went away however the surface acne (as seen here: http://imgur.com/N3zAN.png) appeared. If I made it something like 0.05f it minimized the occurence of the issue but it still happens, just at slightly larger distances.//
This is a typical biasing issue. Adding a bias avoids shadow acne, but setting it to high results in the issue that you're seeing. The common remedies are:
1. Use some sort of dynamic bias based on the slope of the receiving triangle
2. Render backfaces into the shadow map (only works if your geometry has backfaces)
3. Tweak the bias based on the scene
Getting good results may require a combination of all 3.
1. Use some sort of dynamic bias based on the slope of the receiving triangle
2. Render backfaces into the shadow map (only works if your geometry has backfaces)
3. Tweak the bias based on the scene
Getting good results may require a combination of all 3.
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement