Shadow Mapping - Self-Shadowing Z-Fighting Artifacts

Member
Posts: 269
Joined: 2005.04
Post: #1
Is there *any* good way to fix the z-fighting artifacts that happen with shadow mapping? Every article basically says to render back faces into the shadow map (which I do) and to use glPolygonOffset. Using an offset I can solve some of the artifact problems, but it doesn't solve artifacts at shadow edges and introduces new artifacts where there were none before.

An image

Is there some shadow technique that helps fix it? I've tried every combination of offset imaginable. Moving the glPolygonOffset call anywhere and everywhere. Increasing the shadow map resolution to frightening levels. Adjusting the light frustum. Etc...
Quote this message in a reply
Sage
Posts: 1,234
Joined: 2002.10
Post: #2
Read Forsyth's papers on shadows, which describe using an 8 bit object ID to eliminate shadow acne.
Quote this message in a reply
Member
Posts: 269
Joined: 2005.04
Post: #3
arekkusu Wrote:Read Forsyth's papers on shadows, which describe using an 8 bit object ID to eliminate shadow acne.

Very interesting. However, it says that the 8-bit object-ID buffers can only be used for curing acne on shadows cast from one object onto another. They can't be used for self-shadowing, which is where most of my problems lie. I assume a 16-bit per-triangle buffer can be used for self-shadowing, but the slides also say that it requires hardware support (which is limited at this time).

EDIT: Weird that I was just on that page for the Trilights and Spherical Harmonics papers, and completely missed the shadow stuff.
Quote this message in a reply
Moderator
Posts: 1,140
Joined: 2005.07
Post: #4
Try changing the values you pass in to glPolygonOffset. The optimal values will depend on things such as your units of measure, your viewport setup, etc. Once you have a decent value for glPolygonOffset, doing some filtering on the pixel shader will also help get rid of some artifacts as well.
Quote this message in a reply
Sage
Posts: 1,199
Joined: 2004.10
Post: #5
I did something really hacky but it did fix the self-shadow acne problem for me. What I did was to simply say that shadow is 100% for any fragment who's triangle normal is facing away from the light. This is to say, the planar normal, not the interpolated vertex normal.

It's a dirty trick, but it eliminated 99% of shadow acne for me.

Code:
float shadow = shadow2DProj( ShadowMap, ShadowCoord ).r;
shadow += shadow2DProj( ... ).r;
// blah blah blah

vec3 ec_normal = normalize(cross(dFdx(ecPos), dFdy(ecPos)));
float lambertian = dot(ec_normal,normalize( gl_LightSource[0].position.xyz - ecPos ));

return step( 0.004, lambertian ) * ( shadow / 8.0 );

Now, this does cause all triangles facing away from the light to be 100% in shadow. From a geometric standpoint this is correct. But for smoothly interpolated normals it can look wonky since you get a harsh triangle edge. But this is still better looking than the acne, so I'm OK with it.
Quote this message in a reply
DoG
Moderator
Posts: 869
Joined: 2003.01
Post: #6
Umm, since the back faces will always be completely dark, the z-fighting should not matter at all, as the faces don't receive any light from that source in the first place.

Also, DON'T DON'T DON'T use shadow2D*(), it just plain sucks, on some (all?) hardware you can't have linear filtering, but get some pseudo-PCF which looks like crap. You also don't really have control of bias.

Leave GL_TEXTURE_COMPARE_MODE_ARB at GL_NONE, set GL_DEPTH_TEXTURE_MODE_ARB to GL_INTENSITY, and write your own depth compare sampler instead, if you wish with true PCF.

I've found that doing the above, and turning on linear filtering, 99% of the artifacts disappeared, even though due to crap geometry I have to render both front and back faces into the shadow map.
Quote this message in a reply
Sage
Posts: 1,234
Joined: 2002.10
Post: #7
DoG Wrote:on some (all?) hardware you can't have linear filtering

It's only older ATI hardware which doesn't support PCF. Everything currently shipping supports it.
Quote this message in a reply
Sage
Posts: 1,199
Joined: 2004.10
Post: #8
DoG Wrote:Umm, since the back faces will always be completely dark, the z-fighting should not matter at all, as the faces don't receive any light from that source in the first place.

Not when the interpolation of vertex normals across a triangle causes a transition from lit to unlit...

Quote:Also, DON'T DON'T DON'T use shadow2D*(), it just plain sucks, on some (all?) hardware you can't have linear filtering, but get some pseudo-PCF which looks like crap. You also don't really have control of bias.

Leave GL_TEXTURE_COMPARE_MODE_ARB at GL_NONE, set GL_DEPTH_TEXTURE_MODE_ARB to GL_INTENSITY, and write your own depth compare sampler instead, if you wish with true PCF.

I've found that doing the above, and turning on linear filtering, 99% of the artifacts disappeared, even though due to crap geometry I have to render both front and back faces into the shadow map.

I'd love to work with you or somebody who understands this better to help me make my shadow mapping code non-sucky. For what it's worth, what I've got looks great... ( cascading shadow maps FTW )
Quote this message in a reply
DoG
Moderator
Posts: 869
Joined: 2003.01
Post: #9
True that, about the back faces, I have forgotten about normal interpolation.

I don't think I fully understand shadow mapping, and unfortunately have very little experience with stuff like PSM, et al. I was working on an automotive headlamp simulation, and that is a very degenerate case for shadow mapping, as the light shines largely *nearly* parallel to the terrain, so from a top-down view, things tend to look like crap, eg. lots of Z-fighting. But with above settings, and shadow sampler as follows, I am getting very viewable results:

Code:
uniform float shadowBias; // 1.002 seems to be a good starting point

float sampleShadowXY(sampler2D shadowMap, vec2 shadowCoord, float fragDepth)
{
    // shadow2DProj doesn't actually return the depth with hardware PCM enabled, eg GL_LINEAR filtering on the depth texture
    float shadowDepth = texture2D(shadowMap, shadowCoord).r;
    float depth = (fragDepth)/(shadowDepth);
    return depth > shadowBias ? 0.0 : 1.0;

}

float sampleShadow(sampler2D shadowMap, vec4 shadowCoord)
{
    vec2 nCoord = shadowCoord.xy/shadowCoord.w;
    float fragDepth = shadowCoord.z/shadowCoord.w;

    return sampleShadowXY(shadowMap, nCoord, fragDepth);
}

One major problem with the builtin shadow2DProj() is that it returns only 0.0 or 1.0, so you don't have the kind of control over the bias as you do manually, even though you could theoretically achieve the same effects via a polygon offset, doing that properly on diverse hardware seems more difficult. shadow2D() seems to be plagued of similar problems, I wasn't able to get that to work as advertised, either.
Quote this message in a reply
DoG
Moderator
Posts: 869
Joined: 2003.01
Post: #10
arekkusu Wrote:It's only older ATI hardware which doesn't support PCF. Everything currently shipping supports it.

On the Nvidia 8600M I am usually on, I can't tell, as the occlusion reported by shadow2DProj() doesn't seem to be correct, eg with PCF I'd have expected a value in the range 0..1, but I get either 0.0 or 1.0 exactly. Could of course be a peculiar bug with the particular shader I have, similarly how 'discard' doesn't seem to work.
Quote this message in a reply
Sage
Posts: 1,234
Joined: 2002.10
Post: #11
DoG Wrote:On the Nvidia 8600M I am usually on, I can't tell

So, take a minute to prove it to yourself:

1) launch OpenGL Shader Builder.
2) New fragment shader.
3) modify the default shader to use a shadow sampler, like so:
Code:
uniform sampler2DShadow tex;
uniform float r;

void main()
{
    gl_FragColor = shadow2D(tex, vec3(gl_TexCoord[0].xy, r));
}
4) in the Textures tab, drag in some fairly lo-res texture, like /Library/User Pictures/Animals/Butterfly.tif, and set the type to SHADOW_2D.
5) in the Symbols tab, animate the "r" uniform.
6) view the Render tab.

You should see bilinear PCF on the resulting shadow comparison. That is, it will look like any other zoomed in blurry texture. On hardware that doesn't support PCF, like the Radeon X1600, you'll see hard diagonal edges instead.
Quote this message in a reply
Sage
Posts: 1,199
Joined: 2004.10
Post: #12
That is somewhat reassuring arekkusu; I have the x1600, and am using shadow2D simply because it made sense logically to do so. But since I've found hardly any material in fairly thorough googling on how to achieve high quality pcf, and since apparently my GPU won't do it anyway, I've just done the old average-8-jittered-samples thing.

Forcing the software renderer in your example I see the PCF...

Also, @DoG:
Your code uses a ternary to return 1 or 0 after the texture2D lookup. So how do you get anything but hard edges? What am I missing?
Quote this message in a reply
Moderator
Posts: 1,140
Joined: 2005.07
Post: #13
I say you can keep using shadow2D, as long as you're smart about it. If you're reasonably sure that hardware PCF is possible, then you can use it to your advantage. If not, then you need to set it to use nearest neighbor sampling and sample each point for your filter manually. Assuming you can find out what video card the host is running at runtime, it shouldn't be too difficult to have the 2 different code paths.
Quote this message in a reply
Sage
Posts: 1,234
Joined: 2002.10
Post: #14
Or, draw a four pixel gradient with PCF and inspect the results at init time to figure out what it did.
Quote this message in a reply
DoG
Moderator
Posts: 869
Joined: 2003.01
Post: #15
TomorrowPlusX Wrote:Also, @DoG:
Your code uses a ternary to return 1 or 0 after the texture2D lookup. So how do you get anything but hard edges? What am I missing?

That code does indeed just get you a hard shadow, but using that as the basis for a PCF works reliably. If I understand things right, my method gives you a simply linearly filtered shadow depth texture, which gets rid of the block artifacts, and you can additionally add PCF in the shader via your method to get rid of artifacts that occur on faces nearly parallel to the light.
Quote this message in a reply
Post Reply 

Possibly Related Threads...
Thread: Author Replies: Views: Last Post
  2d shadow blending problems tesil 1 5,864 Mar 17, 2011 10:12 AM
Last Post: Skorche
  iPhone 3GS Shadowing Bug Bersaelor 6 10,717 Dec 28, 2010 04:08 PM
Last Post: jarodl
  OpenGL ES 2.0, 2D Alpha Transparency Artifacts Macmenace 3 8,209 Mar 28, 2010 11:18 PM
Last Post: AnotherJake
  Shadowing bug TomorrowPlusX 6 5,139 Feb 1, 2010 12:00 PM
Last Post: DoG
  Replacing edges with degenerate quads (for shadow volumes) Coyote 9 8,197 Jan 15, 2010 07:08 PM
Last Post: Coyote