Like I’ve said before, Deferred Rendering is the new buzz term these days. The basic advantage is that you can render a lot of dynamic light sources that uniformly affects the scene. You can write one that can use a single render target by making multiple passes or just use MRT (multiple render targets) straight up.
If you are thinking bout writing one I can offer some advice:
1. When you write to your depth buffer, most tutorials will suggest the z/w method. However, there are better techniques such as the ones employed by Blizzard / Crytek which makes use of view space rays. MJP wrote an excellent blog on this method here (which is what I do). You can also try accessing the hardware depth buffer directly as well.
2. Not all video cards support render targets that can have non-matching bit depths. So, what many folks do is go either 64 bit or 32 bit. Currently, we render all of our targets to 32 bit depth. So, the depth buffer is D3DFMT_R32F. Every other render target is D3DFMT_A8R8G8B8 except for Normals, which gets D3DFMT_A2B10G10R10. In the Starcraft 2 tech paper, Blizzard goes D3DFMT_A16B16G16R16F (64 bit depth) which is what I do for high quality settings
3. Some people resort to bit packing and such. MJP writes a post which goes over the method here:
Packing multiple values into a single component is pretty ugly in SM3.0 and below, because you don't have integer ops. So essentially what you need to do is use floating point math to determine the final integer value of a component, and then divide that value by the max integer value to normalize back to the [0,1] range. So for instance lets say you wanted to pack two [0,1] values into an 8-bit integer. The lower 4 bits is 0 - 127, so you would simply multiply by 127 to get the integer value. The upper 4 bits is 128-255, so you would multiply by 127 and add 128 to get the value of the upper 4 bits. Then you would add (equivalent of bitwise OR) the two values to get your final 0-255 value. Then you would divide that sum by 255 to get the final [0,1] value to be written out to the render target.
float Pack8Bits(float val0, float val1)
float lower = val0 * 15.0f;
float upper = val1 * 15.0f * 16.0f;
float sum = lower + upper;
return sum / 255.0f;
To adjust for 16-bit you would just expand the range to 0 - 65535 instead.
float Pack16Bits(float val0, float val1)
float lower = val0 * 255.0f;
float upper = val1 * 255.0f * 256.0f;
float sum = lower + upper;
return sum / 65535.0f;
[Richard] Note, you will also have to unpack these values in the shader if you go this route.
4. During the deferred lighting pass, you ‘accumulate’ all of the light contribution from all of the lights in the scene. Thus, you can get by with just 1 shadowmap which will get reused during rendering the shadowmaps for each shadow-casting light. Below is an explanation I posted once:
What I do is use a light accumulation buffer (see deferred rendering). During the lighting phase I do this:
– For every light in the scene, render from its perspective into a shadowmap
– Now switch to the deferred point/spotlight shader. While rendering the light (using alpha blending), sample the shadowmap and project the shadows into the light accumulation buffer
From what I gather, most techniques seem to involve having all of the lights contribute to a light accumulation buffer and then later, this render target is composited into the final scene