All goodies in 1, 2, 3, ..... passes?

Hi everyone!

The problem I’ve faced is known to everyone who has ever designed a 3D engine.
I want to have:

  1. stenciled shadows (I’ve already implemented it in a simple program);
  2. per-pixel attenuated lights (also implemented in a separate program);
  3. (diffuse + specular + gloss) bump mapping (I also have a demo with it!).

The trouble is: how to combine ALL THESE in a single scene, and render it with a minimum number of passes?

It’s not cool to have 2+2+3 passes per surface (…

You need to have at least one pass to fill the ZBuffer; plus N passes per light:

  • stenciled shadows are usually done in 2 pass, but can be collapsed into a single one with the double sided stencil extension.

  • if you have enough texture units, you can collapse the per-pixel attenuation with the diffuse, specular and gloss ones. You’d need a Geforce fx or a Radeon 8500+ though, and vertex/fragment programs.

So, in the worst case (one pass per effect) you need 7 passes; but in the best case if i’m not mistaken, you only need 2 per light. It all depends on your target system.


You might want to check

That might not be very up-to-date information, but it does describe implementing the lighting equation you’re talking about on a couple of different kinds of systems.

The unfortunate fact is, that you’re going to need several passes, no matter what you do, and that’s going to seriously limit the amount of geometry you can use. Depending on what you’re going to use it for, it might be a very good idea to just use per-vertex lighting and compensate the lack of bump mapping with more complex geometry. Then of course you’d have to fake shadows too…


If you target a non-DX9 card (Radeon 9200 and older; GeForce 4 and older):

-) You need at least 2 passes for the shadow volumes.

-) Using Texture Environments you need
about 2 to 3 Texture Stages for Attenuation
2 Texture Stages for Diffuse Bump Mapping with Light Color
1 to x Stages for Specular Bump Mapping (where (2^x) is the Shininess using NV_blend_suqare)
1 Stage for Texture Mapping
1 Stage for Gloss Mapping

Sure with NV or ATI extensions you can do it faster but they will run only in NV/ATI. So you have to code one NV path and one ATI path.

This also depends on the quality you want to have. But I’d recommend you to use a level of detail system. You don’t need to do the specular and the gloss bump mapping if the object is far away.

So if you want to target older graphic cards you probably have to use that much passes.

If you only target DX9 cards (Radeon 9500 and up; GeForce FX) there won’t be such problems. Here you only have to use 2 passes:

One for the shadow volumes (Those cards are able to do 2 side stencil operation, see EXT_stencil_two_side and ATI_separate_stencil)

One for the Rest. With ARB_fragment_program or GLslang you can do nearly everything you want on per-pixel basis.

Here an example:
An early design of the Quake 3 Engine uses about 10 passes:

(pass 1-4: accumulate bump map)
pass 5: diffuse lighting
pass 6: base texture (with sepcular compoment)
(pass 7: specular lighting)
(pass 8: emissive lighting)
(pass 9: volumetric/atmospheric effects)
(pass 10: screen flashes)

Think about when Quake 3 was published… So don’t be concerned about the passes. I wrote a specular bump mapping demo which had nearly the quality a demo using ARB_fragment_program . It took about 9 passes with a shininess of 32 and it was quite FASTER than the ARB_fp demo which only took one pass (testet on GeForceFX 5200).


Bear in mind that you’ll need all those passes for each light. It’s quite difficult to make sure you only have 1-2 lights per surface and producing a quality lighting setup with such constrains is close to impossible.

It’s best to support some form of lightmapping in addition to dynamic lights. One good option is to store indirect light and fill lights in lightmaps, or deluxmaps if you prefer. Only use dynamic lighting for the most important lights. It’s also wise to support non-specular materials as a special case since they’re much faster to draw.