Stencil shadows and smooth shading

I’m doing stencil shadows (selfshadowing) and smoothshading. The problem is when a triangle becomes a shadow caster it immediatly becomes darkened instead of being smoothly shaded.

I understand why this is happening but cant figure out a way to “fix it”. I can make it less obvious by making the objects more detailed (more triangles) and have the shadow fairly light blended.

But is there some better/more clever way to fix this?

It has to do with the logic you are using to do the casting.
I dont know what exactly you are doing in your algorithm, but I think everyone uses the same principle.

Instead of using face normals, you can use the vertex normals to figure out when a triangle should be casting a shadow.

I think it was Cass who did a fix to the infinite_shadows demo. It must at nvidia’s site somewhere.

With Cass’s fix what you do is to do non-popping shadowing, first render the triangles that face the light like normal. But for ones that don’t face the light, you only shadow the fragments that are in two+ volumes. The fragments that are in one shadow you just let the standard shading equation shade them. Check out the latest infinite_shadow_volumes demo in the sdk or on the cvs site.

There is another method where you inset the volume into the model along the vertex normal a tiny bit. The Microsoft D3D shadow volume demo does this.

-SirKnight

Ok… I downloaded a infinite_shadow_volumes demo, its dated 2002-04-04. Got a red knight in a checkboard room, and the popping is there when triangles becomes backfacing from the light… I probably got the wrong version? I’m downloading the whole sdk, I’ll look for a newer version there.

Using vertex normals to figure out if a triangle is a casting one, how will this fix the popping?. Either the whole triangle is casting or it is not and so there will be popping. Or would this “delay” the popping so the triangle will be smooth shaded until all vertex normals is facing away from the light, then it become a casting one?. Hmm, wouldn’t the actual shadow become wrong?.

I dont entirely understand the “fragments that are in two or more volumes”… But sounds promising somehow . I’ll need to check the sdk.

Thanks for your replys!

He means that you draw the faces that backface the light as if they where lit. Then the normal shading makes them dark enough. You should already got those faces if you are using closed Volumes.
But, and here comes the part with two shadows, you must take care if a face recieves not only his own shadow, but also the shadow of something else. In this case the fragment lies in two+ shadows. so you would change your stencil test, increasing your reference value. This will cause faces that fall into one shadow (their own) get correctly shaded.

Lars

To toggle the popping/non-popping mode you have to press ‘g’. This method can still allow popping but only under certain cases and it’s not that bad. Using this technique really helps a lot and makes the shading look tons better.

I tried once using the vertex normals to compute the silhouette of the model wrt the light and it looked like crap. Maybe what v-man talks about is something different?

Here is a snippet of code from the draw_model function from that demo.

// to do non-popping self-shadowing,
// we render triangles that face toward the light
// like normal…
glBegin(GL_TRIANGLES);
{
for(unsigned int i=0; i < m[mindex].mod.tri.size(); i++)
{
md2::plane & p = f.triplane[i];
bool facing = 0 < ( p.a * olight[0] +
p.b * olight[1] +
p.c * olight[2] +
p.d * olight[3] );

  		if( ! facing )
  			continue;
  		for(int j=0; j < 3; j++)
  		{
  			md2::position_normal & pn = vpn[m[mindex].mod.tri[i].v[j].pn_index];
  			glNormal3f(pn.nx, pn.ny, pn.nz);
  			glVertex4f(pn.x, pn.y, pn.z, 1);
  		}
  		
  	}
  }
  glEnd();
  // but for polygons that are back-facing the light,
  // we only shadow the fragments that are in two or
  // more shadow volumes.  For fragments that are in
  // only one shadow volume (their own), we let the
  // shading equation darken them... --cass
  glStencilFunc(GL_GEQUAL, 129, ~0); // don't shadow polys that backface the light and are only in one shadow volume
  glBegin(GL_TRIANGLES);
  {
  	for(unsigned int i=0; i < m[mindex].mod.tri.size(); i++)
  	{
  		md2::plane & p = f.triplane[i];
  		bool facing =  0 < ( p.a * olight[0] + 
  			                 p.b * olight[1] +
  			                 p.c * olight[2] +
  			                 p.d * olight[3] );

  		if( facing )
  			continue;
  		for(int j=0; j < 3; j++)
  		{
  			md2::position_normal & pn = vpn[m[mindex].mod.tri[i].v[j].pn_index];
  			glNormal3f(pn.nx, pn.ny, pn.nz);
  			glVertex4f(pn.x, pn.y, pn.z, 1);
  		}
  		
  	}
  }
  glEnd();

EDIT: Disabled smilies.

-SirKnight

[This message has been edited by SirKnight (edited 03-10-2003).]

Hi,

One really easy solution is to inset the shadow volumes just a little bit in the opposite direction of the vertex normals. This way the sharp shadow edge is still there, but now it slides smoothly on the surface instead of popping.

I find this no more disturbing than the overall effect of too sharp shadows

-Ilkka

I found out that if I use a stencil base offset + 1 in the depth fail approach in combination with a negative polygon offset that makes the shadow volume include the casting geom and then render shadows only where the stencil value is >1 i get the effect that the stencil volume doesn’t shadow the front geometry or the connected backfacing geometry, but only the more distant geometry where stencil value is >1.

This makes the algo very fast. You don’t need to adjust the stencil volume. Just use a polygon offset and base the stencil volume of >1 for rendering. I call the method for stencil offset shadow volumes.

All the shading on the round smooth corners are made by the built in light equation so the shadows get very smooth.

Wow.

Hmm… Nothing happens when I press ‘g’… Or maybe I’m blind… But I think I get it by reading the comments in that code snippet… Thanks!..

I think they use the technic of insetting the shadow volumes in the flightsim ‘Strike fighters’… I was wondering how they did it there… It looks better than having popping. Think I’ll try that too…

Thanks!

Anybody reading this thread might just take it for granted that rendering back faces where the shadow count is 1 just solves silhouette artifacts.
It does not. It merely shifts the problem from convex edges to concave ones. You could argue that convex edges are much more common
than concave, I would argue that the artifact looks far worse on concave edges, it looks wrong not just bad.

I think any solution to eliminate silhouette artifacts must be sensitive to whether edges are convex or concave.
Off the top of my head, I can’t think of anything that isn’t complicated and CPU intensive.

I just twigged as to a possible solution to your problem. When I first did my stencil shadows I noticed the same problem as you. What I did to over come it was I rendered the “near object end cap” of the volume using the rear facing polys (I reversed the vertice order - you could just turn off back face culling I guess).

This worked a treat - except for when your object was touching another object. In my case this was pretty obvious and showed as a ring of bright area where my sphere was penetrating the tile it was sitting on. But this may not be a problem for you.

rpgc,

this is probably the most straighforward approach. knackered posted a link a week ago about some guy discovering this.

Can someone explain to me the insetting approach?
I dont see how this solves the problem in question.

The insetting approach means, that for each shadow volume vertex you set

shadowVertex = shadowVertex-vertexNormal*k

, where vertexNormal is the normal of the vertex the shadow vertex is created from. k is just a scale, this can be either a constant or specified per-vertex to disable insetting for sharp edges.

The result of doing this is, that triangles no longer pop in and out of the shadow, instead the shadow edge (still sharp though) slides along the triangle. You have to be careful with the amount you inset, otherwise you’ll get artifacts. Typically you inset the vertice very little.

-Ilkka

Originally posted by JustHanging:
shadowVertex = shadowVertex-vertexNormal*k

This will only fix the artifacts on the lit side of the mesh, rather than the self shadowing of the dark side of the mesh (or am I misunderstanding the problem?).

I’m also using the insetting approach, but be warned that it can seriously mess up self-shadowing.

Y.

What problems are you getting on the lit side? Well I guess the problem is where the lit side meets the dark side, I think we’re talking about the same problem.

Insetting solves the problem by replacing the ugly pop-up effect with a less ugly sliding shadow edge. It doesn’t create the same result as the other methods proposed here. When insetting the volumes the place where the shadow volume intersects the actual geometry is no longer tied to the model’s triangle edges.

It’s kind of hard to explain.
Make yourself a demo, draw a rectangle on a paper. Then put a ruler on that paper (representing the shadow volume’s edge) so that it meets one of the rectangle’s corners and rotate the ruler around the corner. The ruler will never intersect the edge connected to the vertex, the edge is always on one side of the ruler or the other. This is what causes the pop-up effect with shadow volumes.

Now, move the ruler a little so you’re rotating it around a point near the corner, slightly inside the rectangle. Now the intersection point of the ruler and the edge slides smoothly. No pop-up. Got it?

-Ilkka

Originally posted by JustHanging:
What problems are you getting on the lit side?

Turns out it’s not due to the shadow volume at all. I just checked my code (I wasn’t insetting my volumes and thought I’d try it to see if it helped with the popping) and I’m not rendering the end cap using the lit polys (I’m also not using the method I mentioned above because my objects touch and it causes problems).

This is the issue I am seeing that I thought the insetting would fix.

It looks like my ambient mesh is poking through (z-fighting) with the lit mesh. I can’t for the life of me think of what would be causing that (damn I thought I had it solved…) as nothing significant changes between the passes - and it only happens on the sphere - not the map itself.

The method I posted above (using the back faces as the end cap) allows the away faces to be rendered with just the GL lighting, so you get the nice looking, graduated light gradient - rather than the harsh light to dark boundary created by the shadow volume.

Using insetting does reduce the popping in my code. The arrow in the picture points to the effect I get (the mesh generating the shadow is different to they actual mesh so I get some wierd artifacts). When the camera is in it’s normal position you don’t really notice it unless you look really carefully.

[EDIT] - Helps if you type the URLS correctly.

[This message has been edited by rgpc (edited 03-17-2003).]

The problem was floating point precision. My sphere rolls using Quaternions and it was the inaccuracy of converting them to Axis/Angle that caused the problem (it was OK about 80% of the time but you’d get these annoying flickers).

So I’ll just shutup now.

rgpc,
was you post in reply to mine? If so, I don’t think you understood what I was saying. I don’t have any problems with my shadows, I was pointing out that lighting backfaces simply results in lit backfaces which looks bad on concave silhouette edges, you can’t correct that with anything so simple. There was even a discussion about it in this forum quite some time ago.