fewer stencil clears for shadow volumes

Here is an idea that should enable to render multiple lights with shadow volume without clearing the stencil buffer for every light (at least in some cases).
The point is to use some bits in the stencil buffer to identify the effects of various lights.
We assume:
-Zpass method
-viewer outside the shadow volumes
-s is the number of stencil bits
-n the number of “light bits”: we will see that we can treat 2^n lights without clearing the stencil buffer. The light bits are those on the left.
-m the number of “normal bits”: they will be used like in normal Zpass (of course s=n+m):2^mis the effective range of the buffer
-for light number i (i in [1,2^n]) we define A(i)=(i-1)*2^m

Now, you have probably found the idea: for each light, the relevant range of stencil buffer will be in [A(i),A(i+1)-1]. The following just describes the stencil settings to make things work.

Let’s consider a particular pixel, S is its stencil value.

Fill Zbuffer (and ambiant)

for light i:

draw shadow volumes with:
*front pass: if(S<A(i)+1)
*back pass: S–

draw normal geometry, with stencil test:
S<=A(i) (not the usual ==)

next light…

A few comments:
The trick is in the front pass: every pixel covered by a shadow volume directly jumps in the range of the current light and gets incremented if it is not already in this range. If it is, we just increment as usual.
Note that given the assumptions, the back pass can’t make the stencil value decrease outside of the range of the light.

I hope I’ve made it clear (I didn’t mention a few subtleties), tell me what you think about this (especially if I’m wrong).


Nobody is interested discussing how to reduce stencil clears? Maybe those are not as critical as I think?

draw shadow volumes with:
*front pass: if(S<A(i)+1)
*back pass: S–

Could you please post the OpenGL code that accomplishes this stencil operation?

You’ve got it: Id had forgotten that stencil test is before Z test…
But anyway, has anyone heard of any kind of technique to reduce stencil clears? There must be one way…

Forgive my lack of understanding but why do (would) you clear the stencil buffer between lights?

What is the reason for doing this?

Does this result in “lit shadows”? ie. If a shadow area from light 1 is lit by light 2 it’s less shadowed.

Or is this some peculiarity of the zPass (I’m only familiar with the zfail)?

You have to clear the stencil buffer to 0 for every light to reset the shadow volume enter/exit counter. If you don’t do this between lighs (applies to both zpass and zfail method), you will have incorrect counter values in the stencil buffer from the previous light. In other words, when it comes to rendering the geometry again that is supposed to be lit by the current light source (after having rendered the shadow volume) some of the stencil values might still be leftovers of the shadow volumes of the previous light source. Additionally, some counter values for the shadow volumes of the current light are thrown off. Therefore, the fragments corresponding to these incorrect stencil values will be incorrectly lit (or not lit, if the count was thrown off) by the current light source.

But if you do…

  1. Ambient Only
  2. Shadow Buffer (Light1)
  3. Lit
  4. Clear Stencil
  5. Shadow Buffer (Light2)
  6. Lit

Don’t you “scrub out” your shadowed regions created in 2.?

Wouldn’t it be OK to just go…

  1. Ambient Only
  2. Shadow Buffer (Light1)
  3. Shadow Buffer (Light2)
  4. Lit

Certainly in my simple test it appeared to be OK. Im Using the nVidia “Robust Shadows” technique so the shadow volumes from one light source should not affect the volumes from another (ie. Shadow overdraw).

You would only get correct results if light 1 and light 2 are identical (same position or direction)

The method proposed here seem to suggest to subdivide the stencil buffer for each light, but it leads to complications and old GL can’t help. Neither can fp at the moment.


Suppose you have one red light, and one green light. Areas that are in shadow from the red light should be green if they’re not in shadow from the green light, and vice versa.

You can avoid clearing the stencil as often if you can limit the amount of enter/exit counting you’re prepared to accept. For example, you can slice 8 bits of stencil into 2 4-bit values; for the second light, you increment/decrement by 16. However, that can end up being extremely messy, and is “probably” not worth it.

Shadow buffers. So nice. Except for omni lights.

Originally posted by jwatte:
for the second light, you increment/decrement by 16

What settings do you use to incr/decr by more than 1?

We use some “box packing” technique that renders lights with the small nonoverlapping screen rectangles without clearing the stencil buffer.
If we can’t find a non overlapping light anymore we clear the stencil buffer and start over again.
This can save some clears…


The ones that were discussed but not actually implemented maybe a year or two ago? (Drat :wink:

If you use convex stencil volumes, you can of course use REPLACE with a single bit per light, which allows you 8 lights. But most stencil volumes aren’t.