Jump to content
  • Advertisement
Sign in to follow this  
JNT

Issues with stencil shadows/shadow volumes in software renderer

This topic is 2526 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi all,

I know this topic has been discussed to death by now, but I just have to bring it up once again.

I successfully implemented stencil shadows using shadow volumes (like Doom 3) in my software renderer yesterday, and while awesome it does have certain issues. There is of course the infamous inverted-shadows-when-inside-a-shadow problem, but I am confident that I can solve this on my own. The more prominent issues are what I believe to be precision issues. While I never get cracks between my polygons when rendering a model normally, I quite often get cracks in my shadow volume polygons. I am also experiencing shadow acne (shadows where they are not supposed to be, and no shadows where they are supposed to be). These issues go a bit over my head, but I think they relate to floating point precision (I am using 32-bit floats for everything). Any ideas about these issues? I was also thinking the precision issues might be due to clipping the volumes against the near clipping plane.

Another thing I would like to talk about is how slow stencil shadows are on the CPU. I mean, they look great IMHO, but they do not seem like a proper real-time solution for a software renderer unless you optimize it to the point of being ridiculous. Basically, I would like to create a game/demo that is set in space and uses stencil shadows. Do not get me wrong, I am well aware that stencil shadows are not intended for large environment use, but I somehow have the idea that I would only need to use a single light source at a time, and only render self-shadows on the spacecraft.

Are there any alternatives to stencil shadows that still can produce razor sharp shadows, but are still viable for real-time use?

(I'll post some images once I have access to my Windows-machine).

Share this post


Link to post
Share on other sites
Advertisement
Here are some images from my progam:
stencil1.png

stencil2.png
I really have no clue what's going on in the images above. These situations only seem to occur when the shadow volumes creep close to the near clipping plane (or perhaps they are already being clipped).
stencil3.png
The last image shows that some pixels are drawn as not being in shadow despite clearly being inside the shadow volume.

Share this post


Link to post
Share on other sites

Nice 3D Dragon!

Just to be clear, that model is *not* mine. I just use it for testing purposes.

Share this post


Link to post
Share on other sites
There is of course the infamous inverted-shadows-when-inside-a-shadow problem
I assume this means you're using the "z-pass" variant of the technique? The "z-fail" variant doesn't suffer from this pitfall, and is only a very minor change from the traditional technique.
For completeness, perhaps you should post a high-level overview of your particular stencilling algorithm, including how you generate the volumes, what the rendering steps are, the stencil mode for each step, etc...
While I never get cracks between my polygons when rendering a model normally, I quite often get cracks in my shadow volume polygons. I am also experiencing shadow acne (shadows where they are not supposed to be, and no shadows where they are supposed to be).[/quote]To test this assertion, you could try rendering your stencil volumes to a colour buffer, with front-faces shaded green, and back-faces shaded red. The final result should contain absolutely no red pixels.
Are your rules for determining whether a polygon covers a pixel well-defined, and should they in theory produce water-tight meshes?
These issues go a bit over my head, but I think they relate to floating point precision (I am using 32-bit floats for everything). Any ideas about these issues?[/quote]Does this include your depth buffer? What values are used the represent the near plane depth and far plane depth?
I was also thinking the precision issues might be due to clipping the volumes against the near clipping plane.[/quote]To cut this possibility out of the equation, try to set up a test case where the camera can't possibly intersect with the volume.

Another thing I would like to talk about is how slow stencil shadows are on the CPU. I mean, they look great IMHO, but they do not seem like a proper real-time solution for a software renderer unless you optimize it to the point of being ridiculous. Basically, I would like to create a game/demo that is set in space and uses stencil shadows. Do not get me wrong, I am well aware that stencil shadows are not intended for large environment use, but I somehow have the idea that I would only need to use a single light source at a time, and only render self-shadows on the spacecraft.[/quote]Stencil shadows are still in use today, but are only a 'win' in quite limited circumstances.
The most expensive cost with stencil shadows is rasterising the volume - simply because all of the 'air' between caster and receiver is being rasterised, often several layers thick!
So - having many lights that only cast shadows a short distance could potentially be faster than a single light that casts shadows an infinite distance.

For example, in the game I'm currently working on, we use stencil shadows for humanoid self-shadowing, because we only have to have them cast a short distance - about one arm-span's length - which minimises the number of pixels that need to be rasterised (i.e. minimises the size of the volume mesh in screen-space).

Share this post


Link to post
Share on other sites
?I assume this means you're using the "z-pass" variant of the technique? The "z-fail" variant doesn't suffer from this pitfall, and is only a very minor change from the traditional technique.?For completeness, perhaps you should post a high-level overview of your particular stencilling algorithm, including how you generate the volumes, what the rendering steps are, the stencil mode for each step, etc...?

Most probably, yes. But I'm not particularly worried about the inverted shadows, since they are so well documented on the Internet. I'm fairly confident I can handle that problem on my own time.

My steps are basically (only assuming a single light in the scene):
1) Set every entry in the depth buffer to std::numeric_limits<float>::max() (clear the depth buffer)
2) Assuming no ambient light, write black to the color buffer (clear the color buffer )
3) Render entire scene to depth buffer
4) Set the entire stencil buffer to 0 (the stencil buffer is simply my 8-bit alpha channel on my color buffer)
5) Generate shadow volume
5.1) Transform light to object space (inv(objToWorld))
5.2) Determine what edges are outline edges and add them to a list
5.2.1) For every triangle in mesh, if triangle is backfacing in relation to light step through all 3 triangle edges
5.2.2) For every edge, add edge to list, however if edge is already in list then remove it from list
5.3) Allocate a vertex buffer twice the size of the number of edges in list
5.4) Write all of the original vertex positions in the first half of the buffer, write all of the original vertex values + (normalized(vertex pos - light pos)*10000.f). NOTE: 10000.f is just an arbitrary, large number for projection.
5.4) Transform entire buffer to world space
5.5) Create a list of faces by storing indices for the vertex buffer
5.6) Render face list, for every face:
5.6.1) Set renderer to only render frontfaces
5.6.2) If interpolated vertex Z value is less than stored depth in depth buffer, decrement stencil
5.6.3) Set renderer to only render backfaces
5.6.4) If interpolated vertex Z value is less than stored depth in depth buffer, increment stencil
5.7) Render shadow volume top cap and end cap (use only triangles that are facing the light), same procedure as rendering the other shadow faces
6) Render to color buffer
6.1) Do lighting at vertex level
6.2) For every pixel in triangle; skip drawing if stencil is not 0 or if interpolated vertex Z value not equal to stored depth

As you can see, I'm not very gentle with some of these floating point things, such as the far clipping value (1) and making the assumption that two floating point values equate between rasterizer calls (6.2). Oh, and I actually only perform clipping on the near plane. I don't have a far clipping plane since I just let the depth test decide if a pixel should be drawn.


To test this assertion, you could try rendering your stencil volumes to a colour buffer, with front-faces shaded green, and back-faces shaded red. The final result should contain absolutely no red pixels.?

I did try this just moments ago, and somehow it doesn't feel like cracks in the filling convention anymore. It's more like z-fighting. The strange part is that, even when there is something visibly wrong on the color buffer, the projected shadow looks fine.

stencilsfilled.png

You should note that when drawing the volumes to the color buffer I re-enabled depth writing. That's the correct way to debug right?


To cut this possibility out of the equation, try to set up a test case where the camera can't possibly intersect with the volume.?

I think the third image in my second post shows such a case. No weird shadow lines.

?Does this include your depth buffer? What values are used the represent the near plane depth and far plane depth??

Yes, even the depth buffer is 32 bits. Should I bump that up to 64? The far clipping plane is simply std::numeric_limits<float>::max(), and the near clipping plane is 0.01f. Note that I only perform "real" clipping on the near plane. On the far plane I'm only doing a depth test.


So - having many lights that only cast shadows a short distance could potentially be faster than a single light that casts shadows an infinite distance.?

Going to keep that in mind. Thanks!

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!