Skip to main content
GameDev.net gamedev.net
🔒 Locked

Soft shadows - isn't there a better way?

Started by Moe Apr 4, 2002 at 8:59 PM 24 replies 19.5k views
Original Post
Moe
Moe
I have been doing a little reading up on real-time shadowing, and most peticularily, soft shadows. In some powerpoint presentation by Nvidia (GDC2002_RobustShadowVolumes.ppt - not sure where I found it, probably at www.nvidia.com somewhere) they were using the stencil buffer to tell where there should and shouldn''t be a shadow. Now I will admit that I don''t understand a lot of the math/technical stuff there. Using just one light source would give sharp edged shadows. To get smooth edge shadows, they used 12 dim lights and the same technique to get soft looking shadows. The real interesting piece is right near the end of the presentation that they said that the Doom 3 engine uses this technique. They said that they (the people who wrote the presentation) where getting 8 frames per second on a Geforce 4 Ti 4600. Now I realize that I don''t understand a lot of this, but why isn''t it possible to simply use 1 light and blur the contents of the stencil buffer to make things look soft? If the stencil buffer already has definite sharp edges, why can''t each pixel of the stencil buffer be averaged with the 9 surrounding pixels to give a softer looking shadow while only using 1 light instead of 12? If the stencil buffer was 8 bits, why couldn''t you do the original stencil testing into the last (lower) 4 bits, calculate the averaged/filtered stencil buffer into the upper 4 bits, then copy the upper 4 bits into the lower 4 bits and then clear the upper 4 bits, and rener? Ascii diagram:
  
32 bit z/stencil bufer:
[24 bits for z buffer][8 bits for stencil buffer]
_ _ _ _ | _ _ _ _
upper bits | lower bits

averaging the 9 surrounding pixels, where 9 is the darkest possible value:
0|0|0
0|0|9
0|9|9
before averaging

0|1|1
1|3|9
1|9|9
after the surrounding 9 pixels aver averaged 
  
Why isn''t this possible? Moe''s Site
SirKnight
SirKnight
This isnt possible because the stencil buffer does not work that way. Your making it sound like its a secondary framebuffer. Its not, its just a buffer which tells opengl which pixels to draw and which ones to not. When using the stencil buffer for shadow volumes, you are counting the number enters and exits for the volume at each pixel. Now in that new paper by nvidia, the places where there is no shadow will have a stencil of 0 and those pixels are the only pixels to be rendered to. If the stencil buffer contains any pixel that is non-zero it will not be rendered to. So you couldnt say if the pixels stencil value is 1 then render that pixel a little transparent and so on.

Also when they said that the new doom uses this technique, they meant stencil shadow volumes using a single light for every light source, not using a large cluster to get soft shadows. Soft shadows where present in the new doom are done another way. I heard someone say that Carmack may be using a texture shader as a filter to blur the shadows to make them soft but if thats possible im not sure how. But that isnt official so its not 100%.

You will probably want to read more about the stencil buffer.

Also keep in mind that these demos are getting a low framerate becuase they are not optimized engines and not made to run as fast as possible. They were made just to work and show off the new technique. Much much higher framerates are possible with that demo with enough optimizations.

-SirKnight
lunarss
lunarss
Yeah, the soft shadows they are talking about have been around for a long time. All they did to achieve the effect was to take 12 jittered samples of the lightsource. They just created 12 different lights that made 12 different shadow volumes at different points on a light, thus simulating an area source. There is an old demo on their site that uses the technique with 8 samples if you want to check it out. Its not fast.
Yann L
Yann L
You can do such a ''faked'' softshadow effect, if you use the alpha-buffer instead of the stencil buffer. This requires a bit of fiddeling with alpha-combiners + blending equation when rendering the shadow volumes (with masked R, G and B channels).

Then you can blur the alpha component of the frambuffer (RGB are still locked) in a separate pass, perhaps using the imaging subset.

In the last pass, you unlock RGB and draw a quad covering the whole screen with a blending equation taking into account the destination alpha (where your soft shadows are stored).

Result are nice realtime soft shadows.

/ Yann
Moe
Moe
quote:
Original post by lunarss
There is an old demo on their site that uses the technique with 8 samples if you want to check it out. Its not fast.


I know, I have the demo and it does have some problems with banding in a decent sized window.

Yann L, that sounds like a rather interesting way of doing it. Has there ever been a demo thats done soft shadows in the way that you describe?

Moe''s Site
Yann L
Yann L
quote:

Yann L, that sounds like a rather interesting way of doing it. Has there ever been a demo thats done soft shadows in the way that you describe?


Hmm. I''m not aware of any demos/tutorials/open engines using that technique. I implemented it in my own engine some time ago, and it works great (and it isn''t that different from the stencil buffer method either, from a code POV). It has some pitfalls though, eg. it doesn''t work in 16bit mode (no alpha buffer). And the blurring pass is somewhat critical (performance-wise). If you have HW imaging subset support, then it''s no problem at all. Otherwise, you can copy the alpha component to a texture and jitter multiple quads using it as an alpha texture. This is reasonably fast. But don''t try to blur it with the CPU, this will require a framebuffer readback, and will certainly kill your framerate.

/ Yann
Moe
Moe
The problem with using cookies (I think thats what they are called - rendering the scene from the point of view of the light, and using that texture as a sort of lightmap for the objects) is that you don''t really get self shadowing unless you do a combination of the two techniques.

You do make an interesting point AP, in that shadows shouldn''t be the only focus for a game engine.

Moe''s Site
Bad Monkey
Bad Monkey
From what I understand, the fastest current way to do a "soft shadow" is to use projective textures, and use a fairly lo-res texture, letting the texture filtering blur it to a soft shadow. I can''t really comment too much on this, as I have never implemented the technique. Can anyone else shed some light on the effectiveness/suitability of this technique? (omg that was a shitty pun )

Also, an OT comment on whether shadows would add to gameplay.

While it certainly shouldn''t be the main focus of an engine, I think that in certain genres/situations (such as the FPS) shadowing has the potential to enhance gameplay substantially... imagine how much more strategic a game could be if players couldn''t camp just around a corner (or conversely, rush someone) because their shadow gives them away to their opponents... sweet
Yann L
Yann L
quote:

The problem with using cookies (I think thats what they are called - rendering the scene from the point of view of the light, and using that texture as a sort of lightmap for the objects) is that you don''t really get self shadowing unless you do a combination of the two techniques.


That''s shadow-mapping. You get full self shadowing and it works by rendering the scene from the light POV, and saving the depth buffer as a texture (the shadowmap). It is fully hardware accelerated on GF3+, and semi-hardware on GF2. The quality *highly* depends on the resolution of the shadowmap. If the resolution is good (2048*2048 or more), then the quality is impressive. Performance is also not bad, but it burns one texture unit for the depth iterator / compare. But due to it''s very high texture resolution requirements, shadow mapping are currently only useful on small scenes.

/ Yann
Moe
Moe
Ah, so thats what shadow-mapping is. I knew that the Geforce 3/4 had some sort of hardware shadows, but I wasn''t quite sure how it worked.

If I am not mistaken, that Abes Odworld for the Xbox used shadow mapping.

Moe''s Site
Brobanx
Brobanx
Just an idea, but wouldn''t nice shadows be possible by having each "shadow" be a seperate polygon in the world? I mean, with a little bit of math it should be easy enough (and not too expensive) to calculate the edge vertices of each shadow, and just draw this poly to the screen as a black, semi-transparent surface.
python_regious
python_regious
Q3''s "fake" shadows do that I think ( there are two types remember? )

Death of one is a tragedy, death of a million is just a statistic.
If at first you don't succeed, redefine success.
coelurus
coelurus
Note that shadows can''t just be softened, there''s another problem with shadows.

Imagine a high wall with a sun shining. The shadow is quite sharp at the point where the wall is stuck in the ground. But the greater the distance you get from the object casting the shadow to the object ''getting'' the shadow, the softer the edges of the shadows become. Gee...

I haven''t noticed this effect in a game yet, I''d like to hear if anybody has made some demo on this.
Moe
Moe
yeah coelurus, I have thought of that. The people who are making No One Lives Forever 2 found some way of simulating that. But whats worse, having soft shadows or having hard edged shadows?

Moe''s Site
Yann L
Yann L
quote:

But whats worse, having soft shadows or having hard edged shadows?


You should have both. In reality, direct sunlight creates almost 100% hard shadows, since it can be viewed as directional light at a small scale. So simulating sunlight using a stencil-type method is very accurate.

The other type of diffuse light you have in reality is indirect light. This is created by the general skylight, interreflection of light on surfaces and interreflected sunlight. It produces soft shadows, and the degree of ''softness'' is variable. The best way to simulate this kind of lighting is radiosity, and (highres) lightmaps at rendertime.

Combining both methods in an engine can be very easy or extremely difficult, depending on the exact result you are expecting. If you want really realistic lighting, then you''d have to take things like wavelength, atmospheric multiple scattering, absorbtion, etc.. into account. Bye bye realtime...

/ Yann
Yann L
Yann L
quote:

thats what a shadowmap is


Nope, that''s not shadow mapping. That''s simply precalculated lightmapping. The lighting values are prestored on a map, which gets applied at rendertime by multiplying (or adding) the lighting values to the textured polygons.

Shadow mapping is different. A shadowmap is a *depth* texture, it contains depth (z) values, that represent the zbuffer from the light''s POV. Then later, while rendering your normal camera view, this depth map is applied to all faces in a secondary unit, using a special kind of projective texgen. You also interpolate a z-value at each vertex. Now at every fragment, the interpolated z-value is compared to the light''s zbuffer (the applied shadowmap), to determine if that particular pixel would be ''visible'' to the lightsource (it is lit in that case).

More information about real shadowmapping at nVidia''s site here.

/ Yann
Tom
Tom
coelurus:
You can create soft shadows fairly easily using shadow maps. You need to access the shadow surface and apply a gradual blur from bottom to top. The amount of blur should be based on how many rows of pixels you''re processing. Thus, short shadows would end up receiving less blur than tall ones.

Optimization is critical here. If your image processing code isn''t very fast, then you might as well use multiple light sources, because both techniques will be slow.
GDNet+. It's only $5 a month. You know you want it.
Yann L
Yann L
AP:
sure, most of the games out there use lightmapping, and it is definitely a good method (esp. since you can use ultra-realistic, but totally non-realtime, lighting models such as radiosity to calculate them). Lightmapping is without doubt the system that achieves the most realistic lighting of all the ways that were proposed on this thread. But it takes up lots of memory, esp. if you have very large game worlds (as in RPGs). And precalculated lightmap animations do look awesome, but they lack interactivity.

And sometimes it is better to waste some GPU cycles on a full-realtime shadow, than having non-stop cache thrashing and swapping because of your 1GB of precalc lightmap data...

But my point was more about the term ''shadowmapping''. It is often ''abused'' even by professionals. "Shadowmapping" refers to a well defined technique (the depthmap/light-zbuffer one), preculated intensity maps are lightmapping (whether they actually store light or shadow values, ie. they are added or multiplied). This ambiguity often creates confusion for newcomers in the domain: they heard of shadowmapping, ask for a tutorial/example, and you give them a *real* shadowmap example, when they actually were looking for lightmapping.

Oh well, let''s not fight about terminology

/ Yann
RAZORUNREAL
RAZORUNREAL
First off I'd like to correct whoever said that stencil shadows are very realistic for shadows from the sun. Although the sun is, for all practical purposes, infinately far away, it is still 3 degrees across (or in other words, very big). Most lights are no-where near 3 degrees across from any normal viewpoint. That means the shadows are very soft. Look at the shadow of a tree some sunny day, you will see very large penumbra's where the light goes between the leaves.

I did invent a way of using the stencil shadow method for soft shadows, but I need a floating point buffer of some sort instead of a stencil buffer. What you do is render the scene in ambient lighting, read off the depth buffer (ouch) and pass it to a shader you use for rendering the shadow volumes. The shadow volumes are the same as normal. The shader first works out how big the penumbra (soft bit) should be using the distance to the start of the shadow volume and the distance to the light. Then it compares the depth of the pixel it is curently rendering with the one in the depth buffer it has been passed as a texture. It then adds to a buffer which probably doesn't exist a number between 0 and 1. If the pixel being rendered is more than half the penumbra size in front of the stored depth 0 is added. If it is more than half the penumbra size behind, 1 is added. Anything inbetween is something inbetween. Then you do it all again for the back facing polygons only subtracting. Then when the lighting is rendered you just multiply it by 1-non existant buffer before you clamp it to 0-1. You could at least compress it into the alpha buffer (that is floating point before rendering and a byte after right?).

Man, I don't sound my age.

[edited by - RAZORUNREAL on May 12, 2004 1:49:28 AM]
___________________________________________________David OlsenIf I've helped you, please vote for PigeonGrape!
PouyaCatnip
PouyaCatnip
quote:
Original post by RAZORUNREAL
...

You''ve made a grand total of 4 posts, all of which have been necros from very old thread. Desist, damn you.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.