Latest ArticlesEngine Architecture #1
Static Shadows #1
Paint on Mesh
A Tale of the Internet
Static Shadows #1 Sep 07, 2006
In my dynamic shadows article, I discussed stencil shadows and shadow maps. While both those methods have limitations, they are general purpose methods that allow the light and shadow casting objects and the viewer to move arbitrarily. In this article I'll discuss one of the many ways to do static shadows. By static I don't mean completely static, just they they are more limited than what I consider dynamic methods.
Static Projected shadowsThis is a very old trick. Let's say you have a lantern with a flame shining through patterned edges. Something like stencil shadows will look absurdly sharp. So you can just bake the lantern shadows into a texture that's projected from the light. Since you can edit the texture, you can get quite nice soft shadows this way. The size of the texture naturally inreases as it progresses further from the light, so the shadow will be softer the further away from the caster it is in a realistic manner. You can even add extra softness if desired by blending between two textures based on distance from the light. Despite its simplicity, it can still look very nice for its uses.
Dynamic shadows from the light need to be turned off for the static shadow casters, but can remain on for any other objects in the light's influence. I allow static shadow groups to be set up for each light, by just selecting and adding objects. Objects in the group won't cast dynamic shadows, and will be used to compute the static shadows.
ComputingYou can just make the texture yourself for some things, but being able to calculate static project shadows at the click of a button in the level editor definitely expands usability. You can then play with positioning, or adding/subtracting objects from the static shadow group. I've also added tweaking of texture attributes, such as bluriness( done with a render-to-texture gaussian blur. )
In my editor each light has an edit field for static projection name. So you can reuse a single texture for multiple lights such as instances of the same torch, and they can either be computed on load, or saved out to a file in the level directory and edited if desired, and loaded from disk.
The code for computing static shadows can be based on the code for rendering from a light's view for the shadow mapping depth texture. The main difference is we clear to white then actually render the color( probably a flat shadow color ) as well as the depth. Other than that it's pretty much exactly the same. Of course if you don't do any post-processing you'll have an ugly blocky shadow, so the whole point is to filter it to look better. The most important thing to do is blurring, if you have HDR or Depth Blur you probably have an adjustable blur shader you can reuse.
Often for this type of use the static shadow casters will be in only one direction, so you'll only have to project in the direction of the shadow, everything else will be off the edges and thus clamped to white.
Since this is just projecting a static texture, it's easy to add the ability for transparent objects to cast a colored transparency instead of a black shadow by rendering them with a flat shaded texture instead of the shadow color.
LimitationsThere are two important constraints. The first is that are that any time the light moves all objects in its static shadow group must move with it, to remain in the same position relative to the light. We can still easily make a shadow for something like a spinning fan this way, just by grouping the light and the fan, then animating the group to spin. (This will move the light too unless it's centered to be on the axis of rotation.)
The second constraint is that objects between the shadow caster and the light will still be shadowed, after all we're just projecting from the light. There are two ways around it. The first is only create static shadows for objects that are very close to the light. Stencil shadows usually look pretty bad in this situation, and shadow maps aren't always much better. Luckily this is where static shadows work best. Here's a simple little example screenshot of using this type of shadowing.
Another way around this constraint is to render the static shadow objects into a depth buffer from the light's point of view like with shadow maps, then in the shader only turn on the shadow texture behind the depth buffer.