iDevGames Forums
Shadow Mapping - Self-Shadowing Z-Fighting Artifacts - Printable Version

+- iDevGames Forums (http://www.idevgames.com/forums)
+-- Forum: Development Zone (/forum-3.html)
+--- Forum: Graphics & Audio Programming (/forum-9.html)
+--- Thread: Shadow Mapping - Self-Shadowing Z-Fighting Artifacts (/thread-1792.html)

Pages: 1 2


Shadow Mapping - Self-Shadowing Z-Fighting Artifacts - Bachus - Feb 6, 2009 02:24 PM

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...


Shadow Mapping - Self-Shadowing Z-Fighting Artifacts - arekkusu - Feb 6, 2009 02:45 PM

Read Forsyth's papers on shadows, which describe using an 8 bit object ID to eliminate shadow acne.


Shadow Mapping - Self-Shadowing Z-Fighting Artifacts - Bachus - Feb 6, 2009 04:12 PM

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.


Shadow Mapping - Self-Shadowing Z-Fighting Artifacts - akb825 - Feb 6, 2009 09:24 PM

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.


Shadow Mapping - Self-Shadowing Z-Fighting Artifacts - TomorrowPlusX - Feb 7, 2009 02:37 PM

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.


Shadow Mapping - Self-Shadowing Z-Fighting Artifacts - DoG - Feb 8, 2009 09:08 AM

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.


Shadow Mapping - Self-Shadowing Z-Fighting Artifacts - arekkusu - Feb 8, 2009 11:50 AM

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.


Shadow Mapping - Self-Shadowing Z-Fighting Artifacts - TomorrowPlusX - Feb 8, 2009 12:27 PM

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 )


Shadow Mapping - Self-Shadowing Z-Fighting Artifacts - DoG - Feb 8, 2009 04:41 PM

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.


Shadow Mapping - Self-Shadowing Z-Fighting Artifacts - DoG - Feb 9, 2009 01:26 AM

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.


Shadow Mapping - Self-Shadowing Z-Fighting Artifacts - arekkusu - Feb 9, 2009 11:41 AM

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.


Shadow Mapping - Self-Shadowing Z-Fighting Artifacts - TomorrowPlusX - Feb 9, 2009 03:41 PM

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?


Shadow Mapping - Self-Shadowing Z-Fighting Artifacts - akb825 - Feb 9, 2009 08:24 PM

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.


Shadow Mapping - Self-Shadowing Z-Fighting Artifacts - arekkusu - Feb 9, 2009 08:32 PM

Or, draw a four pixel gradient with PCF and inspect the results at init time to figure out what it did.


Shadow Mapping - Self-Shadowing Z-Fighting Artifacts - DoG - Feb 10, 2009 07:25 AM

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.