Stenciling for Shadow Volumes

Started by
3 comments, last by Neutrinohunter 16 years, 1 month ago
I seem to getting understuck with this stupid algorithm, I fix one thing and it causes a problem somewhere else, so I am asking here for some pointers on things I may be doing wrong. I get weird stencilling artefacts such as these (below), where stencil seems to overwrite or polygons overlap and add colour together which is not what I want. Any insight into what could be causing this would be gratefully appreciated, I feel like I've pretty much tried everything and if I have to look at the Robust... Paper/Demo/Source Code again I think I will scream! Thanks Neutrinohunter
Advertisement
From the looks of the artifacts, my first guess would be that you're not drawing the shadow volume with the right backface culling mode. You have to be careful how you construct your extrusion polygons from silhouette edges (proper winding order).

Determining silhouette edges is, as you most probably know, done by inspecting the two faces that share a particular edge, and if one face is facing the light and the other is facing away, then it's a silhouette edge. Now, you have to keep track of which of the two faces is facing the light, and then use the vertex order of THAT face to build the extrusion quad. This ensures that the extrusion quad you build will have the proper winding order.

Don't fret. See this sample (OpenGL) or this sample (Dx9). The code in them is particularly very simple and easy to follow and straight to the point. Good luck.
Sorry, can I just clarify with you with what you mean.

Generally, the algorithm is:

Draw Scene
Draw Siloheutte Edges to Infinity
Draw Front and Back Faces, one is local and one is infinity depending on whether they are back facing or not.
Draw Scene

And are you saying that when I get the list of siloheutte edges (coloured neon blue in the pics),I need to take into account the order which I build my quads?

So If I have an edge and draw something like follows (this is all off the top of my head, I'm not at my test computer at the moment :P)

for (each siloheutte edge)
Edge e = getEdge(i);
glBegin(GL_QUADS)
renderVertex(e.getVertex(0);
renderVertex(e.getVertex(1));
renderVertexv(e.getVertex(1),1);
renderVertexv(e.getVertex(0),1);
glEnd();

The order of which I specify the edge here is important for the winding of the polygon? And if so how would I check using an edge? Draw Both or hold some information somewhere to check what it connects to (most apps I've seen use a SetConnectivity() function to do this).

If this seems unclear, I'll try to explain what I mean, its quite a long post lol.

NB, The problem does seem to be with the siloheutte edges, it seems if I turn them off, I get no visibile artefacts, but obviously no shadow either ;)

Thanks, for the links. I've actually looked at both and I did use the extrusion technique which is used in the codesampler example, but now I use the NV_DEPTH_CLAMP extension and homogeneous coordinates now, also in that example all the coordinates are precalculated, whereas mine is dynamic so its a bit more complicated.

Thanks
Neutrinohunter
To tell you the truth, I don't know OpenGL programming (I'm a direct3D dude). Nevertheless, I think this still applies. Anyway, it's not enough to determine which edges are silhouette edges, you should also consider the vertex order of these edges. Consider this:
struct edge_t{    unsigned v1, v2;    int t1, t2; // the triangles that share this edge. (-1 possible).};struct triangle_t{    unsigned v1, v2, v3;};// two edges are equal if they reference the same two vertices,// regardless of the vertex order.bool edgeEquals( const edge_t& a, const edge_t& b ){ if( a.v1 == b.v1 && a.v2 == b.v2 ) return true;  if( a.v1 == b.v2 && a.v2 == b.v1 ) return true;  return false;}void DrawShadowVolume(){    for( unsigned e=0; e<mesh.silhouetteEdges.size(); e++ )    {        edge_t edge = mesh.silhouetteEdges[e];        // Since this is a silhouette edge, one of the faces that share it        // is facing the light, and the other is facing away. Determine        // which one is facing the light.        triangle_t frontTri;        if( facesTheLight( mesh.triangles[ edge.t1 ] ) )             frontTri = mesh.triangles[ edge.t1 ];        else             frontTri = mesh.triangles[ edge.t2 ];        // These are the vertices that we will use to construct        // the extrusion quad.        unsigned v1, v2;        // Now use the vertex order from the front-facing triangle.        // Each triangle has three edges. We need to check which one        // of these three edges this silhouette edge is.        edge_t e1( frontTri.v1, frontTri.v2 );        edge_t e2( frontTri.v2, frontTri.v3 );        edge_t e3( frontTri.v3, frontTri.v1 );        if( edgeEquals( edge, e1 ) )       { v1 = e1.v1; v2 = e1.v2; }        else if( edgeEquals( edge, e2 ) )  { v1 = e2.v1; v2 = e2.v2; }        else                               { v1 = e3.v1; v2 = e3.v2; }        // Now construct the extrusion quad.        vertex_t quad[4];        quad[0] = mesh.vertices[ v1 ];        quad[1] = mesh.vertices[ v2 ];        quad[2] = localLightPos + EXTRUSION_DISTANCE * (mesh.vertices[v1]-localLightPos );        quad[3] = localLightPos + EXTRUSION_DISTANCE * (mesh.vertices[v2]-localLightPos);        // At this point, the quad has proper winding order.    }}


Hopefully you will be able to understand what I mean now. If your mesh is not triangulated then I don't know how to fix it. Of course, this code is slow, but should be enough to help you determine if the problem is actually the winding order. Hope this helps.
Thanks, for your help. It was a winding problem, seems to the vain of my problems as I have inconsistent models some CW and some CCW!!!

An update of my progress has got the box working with and without rotation and am attempting to do the same with the smaller primitives.

I do get a weird artefact during animation which is probably due to the switch between in and out of shadow. A quad consisting of two triangles, one flickers and one stays fine. Mmm. I would take a screenshot but it disappears the moment I pause the animation!

Is there a way to not give a damn about winding and get this algorithm working without it, it seems quite stupid that this could be the cause of many problems.

Neutrinohunter

This topic is closed to new replies.

Advertisement