PDA

View Full Version : Spot light with shadow volumes



greg2
06-07-2004, 10:03 PM
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.

SirKnight
06-08-2004, 01:22 PM
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

jwatte
06-08-2004, 02:03 PM
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.

plasmonster
06-08-2004, 04:23 PM
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.

greg2
06-09-2004, 01:56 AM
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.

jwatte
06-09-2004, 05:57 PM
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 :-)

greg2
06-09-2004, 11:36 PM
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 :) )

Adruab
06-10-2004, 08:42 AM
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? :) (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).

jwatte
06-10-2004, 01:01 PM
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.

greg2
06-10-2004, 01:06 PM
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.

greg2
06-10-2004, 01:14 PM
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.

Adruab
06-10-2004, 02:55 PM
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.)

3) Repeat (2) per light...

Done....

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

greg2
06-11-2004, 12:26 AM
Sorry, i have misunderstood the last post of jwatte :)
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:
:) I can use shadow volume with the dark quad method.
:) I can use shadow volume with the additive blending method.
:) I am currently using the dark quad method, but i will modify my program to use the additive blending method.
:) I can render shadows from one or several omni-lights and directionnal lights in the same time.
:) I can render shadows from One spot light and several omni-lights and/or directinnal lights in the same time.
:( 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.

Tom Nuydens
06-11-2004, 01:21 AM
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

greg2
06-11-2004, 01:44 AM
I don't understand your method.
How can the projected texture solve my problem ?
What sould it project ? A disc ? Why ?

greg2
06-11-2004, 02:11 AM
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.

Tom Nuydens
06-11-2004, 04:05 AM
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

yooyo
06-11-2004, 04:05 AM
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

Tom Nuydens
06-11-2004, 04:12 AM
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

greg2
06-11-2004, 06:30 AM
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! :) But for closed models only :(

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).

Tom Nuydens
06-11-2004, 09:23 AM
Originally posted by greg2:
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 wasn't talking about shadowing at all. The reason you would use a projected texture is so that no light would fall outside of the spotlight cone. This is a completely separate problem from shadowing, which you should still do using stenciled shadow volumes.

You're thinking of rendering shadow casters into the texture. That's not what I meant -- the texture is simply a white blob on a black background, like so: http://www.delphi3d.net/misc/images/spot.gif

If you render using a normal omni-directional point light, and then modulate that with the projected texture, you'll have all the light that your spotlight contributes to the scene, except that the light will pass through objects instead of casting a shadow.

With this in mind, the algorithm is now exactly the same as for omni lights:

Render Z-buffer and ambient light;
For each light:
1) Clear stencil and render shadow volumes;
2) Render light to unshadowed regions using additive blending;

The only difference between omni and spot lights is that in step two, you multiply the light contribution with the color from the projected texture.

-- Tom

Adruab
06-11-2004, 10:42 AM
The issue of closed model vs open model plagues shadowing (shadow mapping is really the only thing that handles this elegantly). You MUST have topologically consistent models to use most forms of accelerated stencil-shadow rendering (gpu accelerated, etc.).

Self shadowing should work better for addative blending (you don't have to worry about z-fighting issues as much). Go with the addative blending Tom and others have described, with the projective texturing if you're using spot lights.

There are many demos (Tom's are good...), papers (gamedev.net is pretty good), and books (Real Time Rendering is awesome) that describe lighting and what shadowing is trying to simulate. I'd suggest reading those, since they will probably help clear somethings up (why addative blending works so much better, etc.).

jwatte
06-11-2004, 07:29 PM
OK, so it's pretty clear that you want to render fewer times, to render the scene faster, and you've already made the trade-off to use the fundamentally broken "darkening quad" mechanism.

I'm sorry, I don't know how to make that work better for spot lights. Because it's already fundamentally broken, I don't know of anyone who is spending any time on actually researching it.

Madoc
06-12-2004, 01:09 AM
My, that's a confusing thread.
So, the problem is that greg2 can't apply a darkening quad that will have effect everywhere outside the spotlight's cone...?

The obvious solution is to actually add light in the shadowing pass and modulate with a spotlight texture or use GL vertex spotlights. What jwatte has been saying from the start. That will definitely look best but of course requires an additional pass of the lit geometry.

But, greg2 doesn't seem particularly keen on the soft spotlight effect and will be content with a hard projector (ala stencil shadow). In this case, he can use the darkening quad, and the solution is overly simple:

-Clear the stencil buffer to 1
-Draw spotlight volume inverting face winding ("antishadow")
-Draw other shadow volumes normally
-Render quad where stencil > 1 (as usual)

Et voila'. Wasn't that easy?

greg2
06-12-2004, 09:08 AM
Ok.
I will take the time to think about all that.
Thanks.