Dynamic Shadows Jul 22, 2006
This article is a comparison of the two most common dynamic shadowing methods, stencil shadows, and shadow mapping. I support both in Kingdoms Creator. There are other partially dynamic or completely static shadowing methods, but I'll leave discussion of those for a future article. This isn't a how-to implement article, so I'm assuming that the reader is familiar with how each method works. I sometimes refer to shadow mapping as projected shadowing, since it's based on a projected depth texture.
Quality Comparison
The hard edges of stencil shadows seem to have gotten a bad rap. I can understand why, since real-world shadows are rarely that crisp.
Standard projected shadows on the other hand, have precision limited by the resolution of the depth texture, and with standard bilinear filtering often look blocky. Their quality is a lot more adjustable though, as we can change the size of their depth texture, and soften their edges in a pixel shader. But as quality increases, speed decreases. Soft-edged shadows slow down your pixel shaders, and more accurate shadows that vary edge softness with distance from the occluder are even slower. Both methods have self-shadowing issues. Self-shadowing stencil shadows will cause polygons to be completely shadowed as soon as the triangle is back-facing to the light source, regardless of how the normals used to calculate lighting face. (This doesn't matter for flat objects, since the triangle normals and lighting normals match up.) Shadow Mapping requires offsetting the depth to avoid precision issues that could cause all triangles to shadow themselves, but offsetting the depth means the shadow doesn't start exactly at the right place. The stencil self-shadowing issue is generally more noticeable. For both methods, the quality problems are less noticeable the more lights there are. This is because you can see the shadows less clearly, due to the extra light and competing shadows. So they both benefit in this way from faster graphics cards. For some lights & scenes I don't even notice the difference visually between the two methods. Projected shadows will still render the shadowed pixels. This means they don't have to be black, so you can have color in shadows, or do things like adding some automatic back-lighting on self-shadowed characters. Speed Comparison
Wouldn't it be nice if we could just say one of the two was faster? But as usual it depends on a huge number of factors. Here are a few things I found true for my program:
If you make use of the higher quality softening available with shadow mapping, it'll slow down your pixel shaders significantly. Newer graphics cards are quite fast for rendering stencil shadows. Perhaps it is a Doom 3 effect. Doom 3 uses stencil shadows, and is used in benchmarks, so graphics card manufacturers had an added incentive to make stencil shadows fast. On level architecture, for me stencil shadows are noticeably faster. Most walls don't have a huge poly count so edge calculations are quick. Of course they also have certain slow cases, like a light shining through cell bars( because each bar extends a shadow volume that can cover the whole screen if you're looking across it. ) I've found projected shadows to often be a speed win on characters, although they can be made slower if you set their quality very high. Most characters have a large number of polygons and poly counts are increasing. Stencil shadows, even if they're slower, are still reasonable speed-wise on 7,500 triangle characters. Luckily, shadow visibility optimizations can be applied in about the same way to both methods. If a shadow is determined to be outside the view frustum or occluded, we don't need it. So for stencil shadows we don't perform the edge calculation or render the edge volume. For projected shadows we don't render it into our projected depth texture. Projected shadows give you the ability to put multiple lights in a single pixel shader pass. Newer cards especially have enough texture inputs to take advantage of this. This is especially useful with a per-pixel displacement shader, since you have to do the expensive displacement and occlusion mapping once per pass. Ease-of-Use Comparison
I find stencil shadows can sometimes be a real pain. They're more prone to bugs, especially since bugs can come from models not being perfectly enclosed. A lot of specific optimization is required to have fast stencil shadows: edge lists, edge calculation, shadow volume clipping, etc. The basic optimizations for rendering the shadow map depth texture are often the same as those for rendering the normal scene. Although it's also good to have dynamic quality adjustments to get high shadow detail only in the places you need it. Omni-directional lights with projected shadows require a cubemap and thus extra implementation as well as being slower than single direction lights. With Stencil Shadows we don't need to alter any of the shaders used to render objects. With projected shadows, you must have a version of each shader that does the shadowing. I found that once I had a shader template system, this wan't really a concern, since the extra shaders could be auto-generated. Conclusion
Shadow mapping seems to be getting more popular. Even though I've already spent the time to create an optimized implementation of stencil shadows, I do sometimes wish I could drop stencil shadows completely, because of the complication they add and the requirement to have an enclosed mesh will always be an annoyance. However projected shadows have their own different quality issues and speed issues, so I'll probably keep using both. Because lights are done additively in extra passes, the two approaches are easy enough to combine. I currently don't allow combining the methods on a per-object basis in a single pass, although this is a possibility.
|