Friday, January 12, 2007

ShaderX 5 demo code/exe + Shadows!

I've decided to put the source code and executable for the demo I made for the ShaderX 5 articles online and you can find it here. A screenshot:



It's probably got a few issues but otherwise should work pretty well. If you found it useful or have any comments let me know.

So like I have so many times, I'm once again pondering the ups and downs of using Shadow Volumes (SV) or Shadow Maps (SM) in Star Trader. Cass Everitt has a lot of great insights on the subject and seems to always be going back and forth on why one is better than the other. There are many considerations to take into account (and I won't list them all here), but the majority of time the main reason to use SVs is when your scene will have lots of point lights. The reasoning for this is simple, although SVs are fairly fillrate intensive, it's no where near as bad as rendering the scene six times, as would be required if using SMs.

Personally I think the entire debate is context sensitive. If you were going to make a game with huge sprawling outdoor environments, wanted easy soft shadows with ambient color, and wanted to take advantage of perforated (alpha-tested) surfaces, your best bet is to use SMs. If your game is able to take advantage of static light placement, has really expensive lit pixels, requires easy self-shadowing, and has a _lot_ of omnidirectional light sources, SVs are the best choice. One feature both techniques share is that hardware acceleration is a must, either for the depth testing in SMs or for the shadow extrusion in SVs.

So the question remains, what technique have I chosen to use on Star Trader, a game that really doesn't have too extensive a shadow need? The place that will benefit the most will be the planetary terrains, which would logically lead me to SMs. Well, although I've had a lot of good experiences using Shadow Maps, even with PSMs, Trapezoidal Shadow Maps and the numerous other Shadow Map enhancements I still think the quality is not quite there (well, relative to the expense). So what I plan to do is something I had experimented with quite a bit in the past, which is a hybrid Stencil Shadow Volume/Ambient lighting technique.

The idea behind my technique is to basically take the SV algorithm and extend it with an additional ambient pass. Ambient is one of the things that Doom 3 was lacking and I think that disappointed a lot of people. The game in general was way too dark, even for a military base on Mars. Ambient lighting raises the average brightness of the scene and not only makes it easier to see things but also hints at the indirect light bouncing thats so common to natural lighting. The technique is really simple and works as such. First the stencil buffer is filled up as normal and lit objects rendered with the standard stencil test. The stencil test is then reversed and all lit objects rendered again, but this time just once and with the ambient lighting shader.

Now a nice thing about doing ambient this way is that the ambient light contributes only one time. This is in contrast to SMs where ambient is accumulated for each light that hits a surface and so has the potential to massively over-saturate your lighting results. Another nice thing about doing your lighting this way is that you don't need to add your ambient term into the (direct) lighting equation, which has the potential to over-saturate the scene colors. If you min clamp your lighting results to the ambient level you're guaranteed to have your scene never go darker than the base ambient level. Because the ambient level is object sensitive, you can even give different parts of your scene their own levels (in a certain game I can't talk about I actually made it so different portal area's could each have their own ambient level dependant on whether they were straddling an outdoor area or not).

Another nice thing is that since you're now rejecting the (probably) expensive lit pixels on the unlit sides and instead using the (hopefully) inexpensive ambient shader, you're saving on precious GPU cycles. This really helps to cut down on the lighting costs in games like Doom 3 and Quake 4. Of course Carmack took this step to an extreme by completely culling unlit polygons (and yet it was still faster than rendering those expensive pixels)!

After I finish up getting the skeleton and anims loaded for my Collada convertor I'll probably move on to this. Alright, later!

p.s.
A third option I should mention is parabolic shadow maps but they have a number of major problems and only a marginal benefit. The benefit is that only two textures are required for an omnidirectional shadow (as opposed to six), and thus only two additional scene passes per shadowing light source. This is pretty nice considering you get all the benefits of shadow maps. The bad news however is that since ParaSMs require a parabolic warping of space to map properly to both hemispherical textures, your scene must be relatively highly tessellated to render correctly into the shadow textures. This is a big deal, since you're transferring your savings on fillrate to a pretty heavy bandwidth requirement. Add to this the well known seam between the two textures issue (which is even visible in a game I've worked on that used ParaSMs, see Marvel Ultimate Alliance), and you can see why it's basically not a great solution (or at least not a solution for every situation, or even a few occasional ones).

No comments: