Variance Shadow Mapping depth offset

Started by
7 comments, last by AndyTX 17 years, 8 months ago
Hi all, My current VSM implementation uses D3DFMT_G16R16F for the 16 floating point filtering on geforce 6 & 7. Then the shadow map is blurred with a 5x5 gaussian filter before the variance calculation during projection. The shadows look great, they are very soft. The problem is that the blurring causes the shadow to shrink, as the depths that would not have caused a pixel to be in shadow are weighted in around the shadow edges. This is causing many things that should be shadowed to not be, specifically character's faces when they are facing away from the light. Now I can turn down the blurring and the problem goes away, but that defeats the purpose of using VSM. I had might as well go back to PCF if I can't blur the shadow map. Has anyone run into this problem? Can anyone think of a possible solution for this problem? No amount of adding a depth bias or tweaking the VSM epsilon has helped. Also, I am confident that the gaussian filter is working as expected because it is used in many post processing effects with no problems.
Advertisement
BTW I suspect that most people implementing VSM don't notice this because the shadow color is the same as the backface color, so they blend together. But when you need the shadow to cast on all the backfaces this becomes a problem.
This is the expected behaviour if you're just blurring uniformly over the screen. Uniform blurring is useful as a simple way to hiding aliasing and soften the overall "look", but it does not create perceptually correct shadows. In particular, there is no "contact hardening" so shadows that are smaller in area will simply disappear (even if they are close to the occluder) as you have noted.

The solution is to use some kind of plausable soft shadowing combined with VSM, as hinted about in the paper and GDC slides. One pretty straightforward solution is to use "Percentage Closer Soft Shadows" (PCSS) to do the blocker search, and then use VSM to accelerate the PCF portion. The necessary piece of info to do this is that you can easily vary the filtered area (and thus control shadow softness per-pixel) by using an LOD bias (or derivatives) on the texture lookup.

There are other options as well, but the above is probably the cheapest way to get perceptually correct soft shadows and still be quite efficient. The blocker search will slow you down a bit, but usually it does not require a huge number of samples. There are probably ways to accelerate it somewhat using the extra info from the VSM as well...

Anyways I hope that helps.
Thank you very much for the suggestions. I'm not sure why I didn't realize that was the expected behavior of uniform blurring, but its clear to me now. I'm looking into implementing the blocking search and the penumbra estimation, but I'm already at the limit of my shadow budget so I'm trying to come up with a hack first. Let me know what you think about this in addition to the blurred moments that I'm already calculating:

1) Keep the original, unblurred shadow buffer and pass it into the projection shader

2) Calculate the variance twice, the blurred and unblurred. These can really be calculated together since the variance calculation can be vectorized.

3) interpolate between the results based on the distance between the current pixel and the unblurred depth in the shadow map

This would seem to give correct hard self-shadowing on characters, which is crucial, and still have soft shadows on the ground. I anticipate strange artifacts in the interpolation, so this could be a pretty fast transition.
We've thought about the approach you describe. The problem is that you can't handle the outer penumbra this way. But you can always give it a try - this might be an acceptable loss.

Also note that the vsm algorithm won't give correct results on backfaces. Luckily it isn't hard to just black them out yourself (your shader should do this anyway!)
"Math is hard" -Barbie
As Will mentioned, you'll have to be careful with the outer penumbra... otherwise you'll still get a discontinuity at the projected edge of the uninterpolated depth map. Probably the best immediate solution to this is to bias your VSM so as to only have inner penumbra regions.

Anyways it may be a good soltuion - certainly let us know how it goes! Note that if you don't need a full gaussian blur, you can just use LOD bias (as mentioned previously) and save memory (don't need a blurred VSM) and the entire blur pass. Also you can properly adjust the kernel size based on your estimated blocker depth. It may or may not look as good as what you suggest, but it's easy to test, so probably worth a try.
Speaking of VSM, Does anyone have a solid implementation in HLSL? All the examples I've tried dont look very good, and feature lots of problems, from overly multiplied shadow results to artifacts resembling bias problems.
Quote:Luckily it isn't hard to just black them out yourself


Unfortunately I don't have access to a per-pixel normal because of the way the projection pass is being handled =( Otherwise this wouldn't be an issue and I would be happy with the current VSM implementation.

Thanks to all for the help! If I get a chance I will implement the blocker search or the LOD bias solution and let you all know how it goes.
Quote:Original post by Matt Aufderheide
Speaking of VSM, Does anyone have a solid implementation in HLSL? All the examples I've tried dont look very good, and feature lots of problems, from overly multiplied shadow results to artifacts resembling bias problems.

The Fx Composer example is in HLSL (naturally) and is all the code you should need (other than the setup and rendering of the shadow map that is, which is pretty similar to standard shadow mapping).

This topic is closed to new replies.

Advertisement