Variance shadow map (or VSM) Convolution shadow map (or CSM) and Exponential shadow map (or ESM) all have light leaking problem. On papers of VSM this problem is referred as "light bleeding". Here I assume "light bleeding" and "light leaking" are the same problem.

A screen shot of Exponential shadow map is below:


First the reason why we have light leaking problem on shadow map retrieving methods is that we are trying to estimate information of places under the shadow map while the shadow map hold non of information of them.
There can be two types of penumbra region with respect to the shadow map. I call them inner penumbra and outer penumbra. Inner penumbra is penumbra region that is not visible from shadow map. Outer penumbra is penumbra region that is visible from shadow map.

A physically correct shadow strength calculation should be like this:

The problem is on "A and C" part with VSM and ESP.
The calculation of VSM is like this:
float bOuterPenumbra = ( depthOnShadowMap >= actualDepth );This will make "A" part brighter than "C" part, which is opposite to the correct shadow model. This will make stronger shadow on "C" part and softer shadow on "A".
float brightness = variance / ( variance + pow( actualDepth - depthOnShadowMap, 2 ) );
float finalBrightness = max( bOuterPenumbra, brightness );
Note that VSM does not count the outer penumbra region into the calculation. In order words VSM is working with inner penumbra region not with outer penumbra region.
ESM does the smiliar calculation way of VSM. It reduced light leaking problem but now it causes contact point soften problem. It is because ESM considers depth values only regardless how much the depth values are interpolated. By this reason, the light leaking problem is moved from "C" part to "A" part so that it appears to be soften contact points.
To address this light leaking problem, we need to give the softness on the outer penumbra rather than the inner penumbra. With respect to the fact that the shadow map holds no information of inner penumbra region, it makes more sense to apply shadow softening on the outer penumbra region.
The calculation will be like this:
const float e = 2.71828183;There are some problems on this approach.
const expConst = -5.0;
const bool bUnderShadow = ( actualDepth > depthOnShadowMap );
const float brightness = ( bUnderShadow ? 0.0 : ( 1 - saturate( pow( e, expConst * actualDepth ) / pow( e, expConst * expFromExpMap ) ) ) );
One of them is that we cannot use "second depth rendering" trick on shadow map rendering phase. Second depth rendering is to render back face on the shadow map rather than front face. It is very helpful to reduce the shadow swimming problem and "surface acne" problem. Since the softness calculation relies on the front face depth, we can no longer use second depth rendering.
Another problem is that we must hold the original shadow map in order to differentiate inner penumbra and outer penumbra. When we want to pre-filter the shadow map as ESM does, we need to fetch texels on both.
I have some of solutions in my mind and I will post more details after I get enough test results.
3 comments:
You write "pow(e, b)" with e=2.2718... It's worth noting that GLSL and HLSL have the exp() intrinsic for this :)
Also, you can simplify your brightness formula to ( 1 - saturate(exp(expConst * (actualDepth - expFromExpMap))).
Post a Comment