Spot light with shadow volumes

I want to create shadows from a spot light.
I have found a methode but i wonder if there is an another one (faster).

This is my methode:

_ I render the scene with shadows from an omni light.
_ I clear the depth buffer and stencil buffer (the depth buffer has been modified by one of my operation).
_ I render the scene in depth buffer and i compute the lighted area from the spot light using a cone and ZFAIL (light volume instead of shadow volume) with the stencil buffer.
_ I Render the scene in the color buffer outside the lighted area to clear the shadows in this area. I also render a dark quad on the screen to make the scene darken outside the light area.

Is there another methode?

Thanks.

I don’t like the “drawing a dark full screen quad to darken the shadowed region” method at all. It’s better to do something like this:

Pass 1: Render scene with no lights/shadows, just basic textures. This is your base pass to fill the z-buffer.

Pass 2: Render your shadow volumes (with appropriate stencil ops set) with either the zpass or zfail techniques depending which is better ATM.

Pass 3: Render scene again with lights in additive blending mode with stencil ops set to only update pixels that are not in shadow.

For more details see the recent shadow volume papers on nvidia’s dev site. Actually pass 3 could end up being many more passes. It just depends on how many passes it takes to render one light. If you need 2 passes per light and you have 5 lights, then that’s 10 passes “pass 3” would have to be.

-SirKnight

  1. render depth and ambient light level, writing and testing Z
  2. turn off Z writes, clear stencil, render only stencil volume for objects in light radius
  3. render color buffer in additive blend mode (ONE,ONE), lit by the spot light, only where stencil is non-shadowed, for objects in light radius

You can repeat 2) through 3) for each light in your scene.

Judicious use of the scissor test can lead to huge performance increases. Project the extent of your light volume into screen space, and set the scissor rect accordingly. This helps to minimize the fill requirements of infinite shadow volumes. Similarly, you could use EXT_depth_bounds_test, if supported, to restrict the 3rd dimension.

Acctually, with your additive blending mode, it will not be faster but the image quality will be better. I have to:

  1. Render the scene with no lights/shadows.
  2. Render the shadow volumes on the entire screen (but in depth and stencil buffer only) considering the spot light as an omni-light.
  3. Render scene again with the light (as omni-light) in additive blending mode with stencil ops set to only update pixels that are not in shadows.
  4. Clear the stencil buffer, Compute the spot light area on the models using ZFail.
  5. Render the scene again with no lights/shadows outside the light area to clear the unwanted enlightened area from step 3).

If i want to use 1 spot light + n Omni-lights + n Infinite lights,

i have to:

  1. Render the scene with spot light ilumination and shadows FIRST.
  2. For each omni-light or Infinite lights: Compute the shadow areas and render scene with the lights in additive blending mode with stencil ops set to only update pixels that are not in shadows.

But i don’t know how i can use several spot lights if i want to have a good ilumination with more brightnesss in the region enlightened by 2 lights than in the region enlightened by just 1 light.
Because, in order to display spot light shadows, i have to display the shadows from an omni-light before. And after, i have to clear the unwanted shadows outside the spot area.

I could:

  1. Render the scene with the first spot light and its shadows.
  2. Compute the omni-light shadows of the 2nd spot light and render scene with this light in additive blending mode with stencil ops set to only update pixels that are not in shadows from the omni-light.
  3. Compute the 2 spot light area on the stencil buffer and render the scene without shadows where the pixels are not in one of the spot light area.

But the spot light area of the 1st spot would be enlightened 2 times: It would be light by the 1st spot light and by the omni-light of the second spot light.

It would be great if the stencil buffer could interact with the depth buffer. I could directly render the shadow area of the spot light (only) in the stencil buffer. But it’s not possible !?

Maybe i could use accumulation buffer but it would be slow.

Or it would be great to simulate several stencil buffer and to be able to update a stencil buffer with using the other one.

There’s nothing preventing you from applying the spot light function when rendering the scene in 3), so steps 4) and 5) do not seem necessary.

Similarly, you don’t need to generate stencils as if your light was omni; if your scene management has sufficient culling smarts, you only render the things that intersect the actual light cone and the screen.

Attempting to render shadowed things after lights always ends up being wrong. Sorry that I wasn’t more specific: I’m not suggesting that my method is faster than yours; I’m suggesting it’s correct, and thus that that’s likely what you really should be doing :slight_smile:

There’s nothing preventing you from applying the spot light function when rendering the scene in 3), so steps 4) and 5) do not seem necessary.

Similarly, you don’t need to generate stencils as if your light was omni; if your scene management has sufficient culling smarts, you only render the things that intersect the actual light cone and the screen.
How can i render the things i want in the spot light area without stencil buffer? The enlightened area from the spot light is not a disc, and it’s not the projection of the cone on the 2D screen. it’s more complex.

(I understand additive blending method is better than mine. And it also partially solve an another problem i have :slight_smile: )

You can use the zpass and zfail algorithms with the alpha channel if you so desire, though I’ve heard it’s slower generally, but it’s an option.

Otherwise, the only method I know if without using the stencil buffer is to use shadow mapping which works fairly well for spot lights.

Why don’t you use addative blending if it solves so many problems? :slight_smile: (btw, if you’re worried about cost, rendering all the light contributions into the frame buffer and then modulating it later is cheaper and seems to work pretty well, as long as you don’t want to light translucent surfaces).

You seem to mean something by “spot light” that I don’t understand. Why would you render the scene as omni light? Spot lights are supported in OpenGL. If you want per-pixel spot lighting, you can do so with projected texture lighting. All of this can be done without re-drawing anything.

You can use the zpass and zfail algorithms with the alpha channel if you so desire, though I’ve heard it’s slower generally, but it’s an option.

How? Do you mean that i could render the stencil shadows from each lights in different textures with alpha channel, and display the addition of each textures on the entire screen?
But i want to display shadows without aliasing and from small models (rod, wire…). The alpha texture will add some aliasing problems ?

Why don’t you use addative blending if it solves so many problems?
I will try it, but my priority is to solve the problem of the use of several spot lights.

You seem to mean something by “spot light” that I don’t understand. Why would you render the scene as omni light? Spot lights are supported in OpenGL. If you want per-pixel spot lighting, you can do so with projected texture lighting. All of this can be done without re-drawing anything.
I must use an omni-light to render shadows from a spot light because i cannot cut the shadow volumes in order to keep it in the spot light area.

Using the alpha channel (of the current framebuffer) for figuring volue intersection would be no more aliased than using the stencil buffer (I would just go with the stencil buffer personally… more straight forward).

The way shadowing is normally solved using addative blending is as follows.

  1. Depth only pass
  2. For each light:

a) Render the shadows into the stencil buffer using zpass or zfail (extruded from a point if omni or spot, or in a direction if direction light) (requirs two passes if you’re not using two sided stencil technique).

b) Render the objects again (rejecting undesired stencil state) and ADD in the color contribution of the light (diffuse modulated, specular, etc.)

  1. Repeat (2) per light…

Done…

This is the normal way accurate shadowing is done. Perhaps we are misunderstanding you…

Sorry, i have misunderstood the last post of jwatte :slight_smile:
I thought he was not thinking about the additive blending method but about a method which do not use stencil buffer (i have found a very short paper about it for PlaySation2 a long time ago, but i have never understood how they simulate the count of the volumes).

I will try to be clearer:
:slight_smile: I can use shadow volume with the dark quad method.
:slight_smile: I can use shadow volume with the additive blending method.
:slight_smile: I am currently using the dark quad method, but i will modify my program to use the additive blending method.
:slight_smile: I can render shadows from one or several omni-lights and directionnal lights in the same time.
:slight_smile: I can render shadows from One spot light and several omni-lights and/or directinnal lights in the same time.
:frowning: But , I cannot render shadows from several spot lights in the same time.

Why?
I cannot directly compute the shadow area (or enlightened area) from the spot light on the stencil buffer. Because I can’t cut my shadow volumes to disply them just in the cone of the spot.

Note:
With spot lights, shadows are inside and outside the spot area. We can find light and shadows in the spot light area (but we can also have lights without shadows or shadows without lights) and we can only find shadows out of the spot area.

My soultion for one spot is to:
_ Pre-compute shadows from an omni-light (in the same position as the spot light) in the stencil buffer and display it in the color buffer. The shadows are displayed in the entire screen.
_ Compute the spot light area on the stencil buffer. Clear the color buffer outside the spot light area (i display the scene without lights on it).

?..
I must do that because i can update the color buffer when i want, and using several state of the stencil buffer. But i can’t write the omni-light shadow area on the stencil buffer and, after, modify the same stencil buffer to clear the unwanted enlightened area on the stencil buffer.

To display shadows from one spot light and sevaral omni-light and/or directinnal lights:
_ I render shadows from the spot light. In the color buffer, i have the scene with the spot light shadows inside and outside the spot area.
_ And after i render shadows from the other lights.

Why?
Because to render shadows from the spot light i need to render a second time some parts of the color buffer in order to clear unwanted enlightened areas. => To not erase the shadows from the other lights, i must render the shadows from spot ligh first.

What’s the problem with several spot lights?
_ With my method, i need to render a second time some parts of the color buffer in order to clear unwanted enlightened areas. If i do that for the second spot light, it will also clear shadow from the first spot light.

But you could…
_ Render the scene and shadows from omni-lights which would be in the same position as all the spot lights.
_ Compute the spot light areas from all spots on one stencil buffer.
_ Render the scene a second time, without shadows, outside all the spot light’s areas in order to clear unwanted enlightened area.

No, because…
If two spot lights light the same area, the intensity of the light should be greater in it. And i can’t to that with the method above.

The fullscreen quad method is fundamentally wrong in that it draws light everywhere, and then tries to remove it again where there is shadow. To better mimic the behaviour of real light, you should determine which areas are in shadow, and not draw anything there in the first place.

Let me repeat that: if something is not lit, don’t draw it. Shadowing is the first step in the rendering process, not the last! You keep saying you want to “remove” light from the framebuffer, but it should have never gotten there in the first place!

If you can render one spotlight this way, you can also render two or more. Just add the results together. A spotlight can be treated exactly like an omni point light in terms of shadowing. The only difference is that you typically use a projected texture to define the spotlight “cone”. You could start culling stuff outside of the cone for efficiency’s sake, but this is not a requirement for correctness (which should be your primary concern right now). The important thing is that even without shadowing, anything that lies outside of the spotlight cone should be unlit (your projected texture should take care of this).

– Tom

I don’t understand your method.
How can the projected texture solve my problem ?
What sould it project ? A disc ? Why ?

Actually,

I think i have maybe understood how to render shadow from several light with shadow volume + projected texture. But… using the quad method.

I would be some thing like that:
_ I render the scene with light and without shadows on the color buffer.
_ I compute the shadow areas from the spot light as it was an omni-light in the stencil buffer.
_ i project a dark disc on the screen from the spot point of view using the stencil buffer. I have never tested, but it should prject the texture only in the areas i want in the stencil buffer ? It would display shadows only in the spot area.
_ i project a texture with a disc with apha=0 and with a dark background, but without using stencil buffer. It will display shadows everywhere, outside the spot area.

And i can do the same thing with several spot.

But i don’t know how i can do the same thing with the additive blending method.

Forget shadowing for a while. If you render your scene with a single spotlight, without shadowing and with no ambient, what do you see outside of the spotlight cone? If the answer is anything other than “black”, something’s wrong.

The typical way to implement spotlights is to use a projected texture of a fuzzy white blob. The light itself is a regular omni point light, but by modulating it with the projected texture, you can model the spotlight cone. Because the borders of the texture are black, it also automagically guarantees that everything outside the cone is invisible.

If you think source code might help, I have several relevant demos:
http://www.delphi3d.net/listfiles.php?category=4

– Tom

Originally posted by greg2:

Why?
I cannot directly compute the shadow area (or enlightened area) from the spot light on the stencil buffer. Because I can’t cut my shadow volumes to disply them just in the cone of the spot.

Why dont you try to find silhouette from spotlight and clip it on spot light frustum, than close clipped wedges. You should get nice pyramidal-like shadow volume.

  

Light frustum:

    +-------------------+
    | /---\             |  
    |/     \            |  
    |I      \           |  
   /|I       \          |  
  / |I        \         |  
 /  |I         \        |  
----|-----------        |  
    |                   |  
    +-------------------+


You have to clip silhouette and add wedge marked with (I).

Or, you can try to use shadow volume reconstruction (based on shadow map).

yooyo

Yooyo, finding shadow volumes for a spotlight is exactly the same as finding shadow volumes for an omni point light, which he can already do. As I mentioned before, you could do some culling to save performance, but you don’t have to do it to get correct results.

– Tom

Tom Nuydens, do you think about using the texture projection method at 100% for spot light, or do you think about using a mix between shadow volumes and texture projection.
The problem of texture projection is that i would have to separate the shadow caster and receiver and there’s also the problem of aliasing.

I think i can mix the 2 methods (shadow volume + projected texture)and have good resluts using a projected texture over the enlightened scene to simulate shadows (see my last post).

Now, i think i know how to render shadows from several different lights (spots, omni and directinnal). Great! :slight_smile: But for closed models only :frowning:

This method works only by adding dark shadows over an enlightened scene.
This method do not works with the shadow volume additive blending method (which adds good lights insted of adding flat shadows).

And for me, it’s a problem because i have discovered that the shadow volume additive blending method attenuates one of my other big problem: Self-Shadows (shadows over the Nonenlightened faces).
I use open models. With closed models, self-shadows are cleared by the near caps. But with open models, the near caps cannot clear the self-shadows.
If i use a dark quad to display the shadows over the scene, after, i can use a post process in the color and stencil buffer to render again the area of self-shadow without shadows (just the shadows created by the OpenGL light).
If i use the additive blending method, i can do the same thing.

But this post process can be done just one time because it clears some part of the last color buffer.
With several lights and the additive blending methode i can forget the problem of self-shadows because it will be attenuated by each light contributions (but not always, if my lights are all in the same place).