Access to triangle normals, rather than vertex

I’ve implemented cascading shadow maps, and they by and large are looking great. As per usual, I’m rendering my depth passes using the back faces of the occluders.

Now, I’ve got a shadow casting terrain, and I see a fair amount of shadow acne on some of the back faces. Specifically, I’m seeing shadow acne on triangles for which at least one vertex normal is facing the light, and the other vertex normal(s) face away. E.g., triangles which are partially illuminated, and partially shadowed. Triangles where the varying normal is interpolated across illumination boundaries.

I’ve experimented with polygon offset to alleviate this, but frankly that’s a little akward, since with an offset large enough to fix the problem, small objects begin to show acne on their illuminated faces. So that’s not really a tenable solution.

I was thinking about best approaches to solve this, and realized that there’s a pretty simple solution, and I was hoping somebody here could help me with it.

First: shadowed triangles are by definition facing away from the light.

Second: triangles with acne are partially facing and partially facing away from the light, owing to the interpolation of their vertex normals.

So it occurred to me that if the triangle’s normal ( as opposed to vertex normal ) faces away from the light, I could zero out illumination all together. E.g., in the fragment shader, in pseudocode:


float illumination = ...
if ( triangle faces away from light ) illumination = 0;

The trouble is, I don’t know the best way to get the actual triangle normal in my shaders. I know that I could pass triangle normals as vertex attributes ( or as gl_SecondaryColor ) but I’m already pushing my little GPU pretty hard, and was hoping there might be some uniform or attribute already available to GLSL that gives me the triangle normal. Or perhaps there’s some way to determine the triangle normal in the vertex ( or fragment ) shader from other data.

If I could simply knock illumination to zero for triangles facing away from the light, my shadow acne woes would dissapear completely. Sure, I’d get a hard boundary between illuminated fragments and shadowed, but correct shadowing does that, anyway.

Here’s a screenshot of my terrain showing the shadow acne. I’ve bumped up the specular to make it a little more obvious…

And here’s another pic for detail:

You can get that normal only with custom geom.shaders . Or manually unwelding vertices (produces many vertices, with vtxNormal=triNormal, and NumVertices=NumTris*3), or hope that by specifying flat-shading, the driver will somehow optimally do things right for you.

A 4th way maybe is to find a neat mathematical use of dFdX/dFdY

I was thinking about the dfdx/dfdy option, but to be honest, I’m not certain if it would do the trick. I’m probably just going to submit triangle normals per-vertex as an attribute array.

I wanted to avoid the extra vertex processing overhead ( as well as an extra varying… my shaders are beginning to tax my x1600 ) but if this is what it takes, this is what it takes.

Regarding flat shading, what does it mean to GLSL? My guess is that GL_FLAT vs GL_SMOOTH mean nothing once you stop using fixed function.

with GLSL 1.30 (or 1.20 with EXT_gpu_shader4) you can control how varying/inout variables are handed to the next stage. you can specify the following:

out flat vec3 flat_normal;

so you have flat shading over the primitives.

Most such problems are not any problem with the rendering unless you get it really really bad (in which case i suggest increasing the resolution of the shadowmap), shadowmap artifacts are mostly unavoidable, especially as in your case you have either backfacing polygons or high angle to lightsource polygons.
You can only try to hide them as best as you can.

So instead of trying to wrangle the shadowmap you should combine the diffuse term with the shadow even for the specular highlights since they really are two important sides to the same coin.
(specularity is a simplified reflection of the lightsource, and light can’t reflect from a surface if it cant reach the surface)

To obtain the normal of a triangle you can do the following:

Vertexshader:
varying vec3 ec_pos;

void main(void)
{

// transform vertex into eyespace
ec_pos = (gl_ModelViewMatrix * gl_Vertex).xyz;

}

// then in the fragment shader do:
varying vec3 ec_pos;

void main(void)
{

// face normal in eyespace:
vec3 ec_normal = normalize(cross(dFdx(ec_pos), dFdy(ec_pos));

}

This method is faster than employing geometry shaders, at least on my GF8800GTS.

The resolution of the shadow map isn’t really the problem, cascading shadowmaps has pretty much taken care of that ( though I’m experimenting with light-space shadow maps ). The issue is acne, which is a result of depth precision, not resolution. I do trim my near and far clipping planes pretty well, but it’s not good enough!

Actually, I already zero the specular when the lambertian lighting component is <= 0. That was one of the first things I did when I switched from stencil shadow volumes to shadow mapping.

The issue comes from single triangles having lit and unlit vertices…

This works! You’re my hero, thank you very much! The acne is almost completely gone… fantastic.

Even better, I’ve learned about the dFdx/dFdy functions.

This topic was automatically closed 183 days after the last reply. New replies are no longer allowed.