I’m implementing stencil shadows in my engine: everything works fine, but I still have some unshadowed isolated pixels appearing sometimes, normally on the edges of shadow projections. It’s a bit like having some pixels tracing a line between two triangles.
It’s sounds like a precision problem, is it?
I’m using the ancient trick to display shadows: a flat black-blendend quad over the scene after stencil-buffer filling: it’s that a problem?
What technique are you using to generate your shadow volume polygons?
I’m using the technique descibed in your article [Practical and Robust Stencil Shadows]*, but, instead of drawing first the scene with just ambient light and then adding each light, I do:
- draw the scene with everything (full lights, geometry, blah blah)
- for each light:
a) clear stencil buffer, settings stencil ops (exactly like in your article)
b) drawing volumes (silhouette projection with both caps [always])
c) instead of rendering the scene again with lights, I put a 2d black blended quad over the screen to add darkness to shadowed areas
And that works fine. No “parity” error when the camera is inside the volume, more lights sources cast additive shadows (if two shadows overlap, this region become darker). It seems that I’ve some precision problem when rendering the volumes: the pixel artifacts appear generally along a edge shared by more triangles.
I already tried:
- poligon offset —> nothing (but I eliminate some Z-fighting when rendering more lights… )
- change projection matrix —> nothing
- improve z-buffer precision —> nothing
The demo I downloaded don’t have this problem. I dunno.
I noticed that this problem appear when I draw shadow coming from a “boxed” objet, that is whit angle of 90° or more. When I draw shadows coming from, for exemple, a sphere it’s perfect.
I’ll post more screenshots later.
- btw, your work is great!
Your shadow volume has cracks because the vertices do not match perfectly, perhaps thi is a result of the original model having slight cracks.
Thanks! The one thing that jumps out at me is, do you have the far plane exactly at infinity or slightly beond it? There are occasionally some clipping artifacts that occur when you’re projecting exactly to the far clip plane.
I add two more images.
This is an overview of the scene: very simple geometry, exported from 3d studio max throught a home-made plugin. The pyramid is casting shadows over the boxes: the geometry of the pyramid is really basic (I don’t think that my problem is related to geometry, in this case).
Here is a zoom over the area under the pyramid: notice the small segment of unshadowed pixel that appears (THIS is my problem… ):
To Cass: thanks! Maybe my projection is wrong. That’s how I calculate it:
// Setup perspective:
glViewport(0, 0, x, y);
gluPerspective(45.0f, (GLfloat) x / (GLfloat) y, EXVOPENGL::nearPlane, EXVOPENGL::farPlane);
// Project to infinity:
matrix.m = -1.0f;
matrix.m = -1.0f;
matrix.m = -2.0 * EXVOPENGL::nearPlane;
// Reset matrix:
And then I add the modelview camera. Do you think I’m doing something wrong here?
Ah, I forgot to say that I have a shadow of the whole scene projected “in the void”, away at the “infinity” (that is at the end of the visible side of my viewport). This shadow is higly z-fighting. Is that a normal consequence of the reversed algorithm or it’s just the root of all my problems?
I agree with dorbie – it looks like your model has cracks in it. You need to make sure that adjacent faces are actually sharing the exact same vertex positions for the edge at which they meet.
BTW, if you haven’t already seen it, I’ve written a pretty extensive article about stencil shadows for Gamasutra:
[This message has been edited by Eric Lengyel (edited 02-20-2004).]
I had the same kind of problem with my shadow volumes. After rewriting the volume creation part of my code the cracks were gone. i had some shadow volume polygons rendered twice (the caps, i think). this caused these ugly lines of unshadowed pixels across the volume because of precision errors on the edges of the double rendered polys.
i’m pretty sure you’ve encountered the same problem.
hope this helps
[This message has been edited by Jens Scheddin (edited 02-20-2004).]
To Eric: Wow! I used your article on Gamasutra and Cass’ article to build up my solution: it’s really nice to read your answers, thanks a lot!
In facts, my shadow volumes routine is quite hold and come from an ancient tutorial found on Nehe site (about 2 years ago). I think it’s time to modify this code with somethings more recent (and Jens’ contribution seems to be really interesting). I think that my volume creation code is wrong, because all the other stuffs are working fine.
I’ll try first to modify my silhouette determination algorithm. Hope the problem is “only” here…
Thanks to all!
Yes, I would definitely use EricL’s
P’inf matrix that includes the slop
factor. If this doesn’t fix the problem, then it’s almost certainly cracks in the geometry.
Maybe you have polygon offset turned on when you render the shadow polys… I found cracks in mine when I made that mistake, but when I zeroed glpolyoffset, cracks disappeared… if you flip glpolyoffset around the other way, you’ll prolly take care of all that fighting too.
[This message has been edited by 147-2 (edited 02-22-2004).]
The problem was during the connectivity generation in the plugin I developped: when you apply a texture to a box (for exemple), 3dsmax creates more vertices to allow a correct texture UV specification for each face. That is, your box with 8 vertices and 12 faces becomes a 32 vertices/12 faces box… The problem was there: I was casting shadows on opened meshes, or creating geometrical redundant shadow volumes. Now not only my shadows look fine, but my code runs a lot faster!
[In every case, my framerate fall to ~1/4 when shadows are active, but I know that this algorithm is fillrate expensive]
Again, thanks a lot!
[This message has been edited by Bibobibobibo22 (edited 02-23-2004).]
Im having the same problem as u with the meshes from 3d studio: extra verts and a resulting opened mesh…how did u solve it?
You have to generate your shadow volume by only paying attention to position, not texture. This is basically “welding” the entire mesh, programatically, when exporting the shadow geometry.
If you also pay attention to normals (which you probably should) then you should insert degenerate quads along edges where two differently-normaled faces meet (different smoothing groups).
Hi, I resolved the problem with a procedure witch remove similar surfaces (triangles) but with different normals or UV coords (different normals too because I need to add more vertices to the geometry when two surfaces share some of them with an angle of 90°).
It’s quite easy to do. Here my solution (brute force, bad coded, but maybe useful to you):
bool xvexp::SimplifyMesh(Point3 *vertexArray, WORD numberOfVertices, XFACE *faceArray, WORD numberOfFaces, XFACE **returnedArray, WORD returnedFaces)
** ?: remove redundant surfaces
** P: array ptr, # of vertices, face ptr, # of faces, # to a new face array, # to new face count
** <: TF
// Alloc memory:
Point3 *_vertexArray = (Point3 *) exfMalloc(numberOfVertices * sizeof(Point3));
memcpy(_vertexArray, vertexArray, sizeof(Point3) * numberOfVertices);
XFACE *_faceArray = (XFACE *) exfMalloc(numberOfFaces * sizeof(XFACE));
memcpy(_faceArray, faceArray, sizeof(XFACE) * numberOfFaces);
// Find doubled vertices:
int modified = 0;
for (int c=0; c<numberOfVertices-1; c++)
for (int d=c+1; d<numberOfVertices; d++)
if (_vertexArray[c].x == _vertexArray[d].x &&
_vertexArray[c].y == _vertexArray[d].y &&
_vertexArray[c].z == _vertexArray[d].z)
if (_vertexArray[d].x != 99999.99999f)
for (int l=0; l<numberOfFaces; l++)
if (_faceArray[l].a == d) _faceArray[l].a = c;
if (_faceArray[l].b == d) _faceArray[l].b = c;
if (_faceArray[l].c == d) _faceArray[l].c = c;
_vertexArray[d].x = 99999.99999f;
// Remove unuseful triangles:
int removed = 0;
for (int c=0; c<numberOfFaces-1; c++)
for (int d=c+1; d<numberOfFaces; d++)
if (_faceArray[c].a == _faceArray[d].a &&
_faceArray[c].b == _faceArray[d].b &&
_faceArray[c].c == _faceArray[d].c)
if (_faceArray[d].a != 65535)
_faceArray[d].a = 65535;
*returnedArray = _faceArray;
*returnedFaces = numberOfFaces-removed;
exfLog("Removed %d triangles, modified %d
", removed, modified);
Your problem looks very similar to a problem I had, which turned out to render redundant shadow volume sides.
And yes, 3Dmax meshes suck.
I would like to add that if you render all your scene with an infinity-projection, you can render the front cap bit-excact and do not need a polygon offset.