It turns out that I was performing a linear filter on my shadow map depths, but I should have been doing the shadow map prefiltering in log space. Whoops.

Actually, there seems to be a lot of confusion about how to filter in log space when outputting linear depth, and why you need to. Let me clear things up.

There are two approaches to exponential shadow mapping:

**1. Output exponential light depth map, prefilter linearly.**

You can create a depth map for the light by outputting *exp(depth)* and then perform the typical gaussian/box/tent prefiltering as normal. In this case, when drawing the light, the ESM filtering is done like so:

float occluder = tex2D(shadowMap, texCoords); float lit = occluder / exp(c*reciever);

The advantage of this method is that the prefiltering is less expensive than the next method. Hardware bilinear, anisotropic and mip filtering all automatically filter the shadow map correctly.

The disadvantage of this method is that it is precision hungry, because *exp(depth)* varies quickly. A 32 bit floating point texture allows a value of *c*=88 before overflow errors start to occur. 16 bits is not enough precision with this method in my experience as contact light leaking is very problematic.

**2. Output linear depth, prefilter in log space.**

You can create a depth map for the light by outputting *depth* as is, and then perform a gaussian/box/tent prefilter in log space. In this case, the when drawing the light, the ESM filtering is done like so:

float occluder = tex2D(shadowMap, texCoords); float lit = exp(c*(occluder - reciever));

The advantages of this method:

- Less precision is required to store depth, which now varies linearly. So we only need 16 bits to store depth!
- The ESM filtering when drawing the light is slightly faster as we can remove a division.

The disadvantages:

- Hardware bilinear and anisotropic filtering will introduce some error, although it is generally close enough — the artifact is just a little bit of shadow overdarkening.
- Prefiltering must be done in log space, which is slower (see below).
- If mipmaps are used, they must be generated using filtering in log space as well.

Overall, these two methods represent a tradeoff between memory and ALU, with method 1 requiring more memory and less ALU overall.

So how and why do we filter in log space?

Consider for example that we wish to average two values in the shadow map (like in a 2×2 separable gaussian or box blur). Then we are averaging the two values *exp(d1)* and *exp(d2)*.

Using gaussian/box blur weights *w1* and *w2*, we then have:

*w1*exp(d1)* + *w2*exp(d2)*

*exp(d1) * (w1 + w2*exp(d2 – d1))*

*exp(d1) * exp(log(w1 + w2*(exp(d2-d1))))*

*exp(d2 + log(w1 + w2*exp(d2-d1)))*

So now the sum of the two exponentials is written as one exponential. Taking the log of the previous statement, we can perform the averaging of the box/gaussian blur by working on the exponentials argument only:

*d2 + log(w1 + w2*exp(d2-d1))*

So we filter the arguments of the exponentials, and then go back to exponential space when actually drawing the light, using *exp(c*(occluder – reciever)) *as we saw earlier.

I’ve generalized the above reasoning for two arguments to arbitrarily many arguments in the following code that can perform a box blur in log space, using an HLSL pixel shader. To perform a Gaussian blur instead, just replace the constant sample weights and 1.0 with the appropriate Gaussian weights.

sampler TextureSampler : register(s0); #define SAMPLE_COUNT 3 float2 Offsets[SAMPLE_COUNT]; float log_space(float w0, float d1, float w1, float d2){ return (d1 + log(w0 + (w1 * exp(d2 - d1)))); } float4 Blur(float2 texCoord : TEXCOORD0) : COLOR0 { float v, B, B2; float w = (1.0/SAMPLE_COUNT); B = tex2D(TextureSampler, texCoord + Offsets[0]); B2 = tex2D(TextureSampler, texCoord + Offsets[0]); v = log_conv(w, B, w, B2); for(int i = 2; i < SAMPLE_COUNT; i++) { B = tex2D(TextureSampler, texCoord + Offsets[i]); v = log_conv(1.0, v, w, B); } return v; }

So what does all this extra work get us? The error introduced by filtering linearly instead of in log space was so small in my tests, that I couldn’t produce a screenshot that clearly demonstrates it.

Here is the thread that inspired me to try filtering in log space, and also a clear difference between linear and log filtering is demonstrated.

I found that on the Xbox 360, log filtering didn’t cost anything extra, as the prefiltering step was texture bandwidth bound anyway — so I’ll leave it in place for now.

Hi,

B2′s offset should be 1 instead of 0.

Hi!

In the list of advantages of paragraph 2, I disagree:

1.0 / exp(c*receiver) = exp( -c*receiver ) so you don’t really gain a division since you don’t really need it in the first place.

Some other note:

I do not quite agree you need a lot of precision to achieve an honorable result since you could perfectly write exp( -k * z ) in your shadow map (instead of exp( k * z )), whatever k you’re using the result will be in ]0,1]. I’m doing this in my test application and writing results into an R16_UNORM and I don’t have any problem with precision.

My main concern at the moment is to find how to remove self-shadowing as can be seen in the image there: http://i.imgur.com/IKcByTC.png

The problem is that I’m using ESM to simulate an area light so I’m also using paraboloïd projection and I fear that transformation into paraboloïd space distorts objects too much so they receive their own shadow…

I’m a real lamer when it comes to shadow maps, I never quite took the time to study the subject because I simply… don’t like it (too many methods, too many caveats, etc.) (a bit like the zillion papers that were publised on SSAO in fact).

I’m trying to add / remove some bias values here and there without really thinking about it and I always end up with a bad result, do you have any clue on how to remove self-shadowing?

3、 《工作安排》着重强调健全法规标准，把食品安全作为综合执法的首要责任。可怕的是不懂却听不进反对意见。50美元/吨；60-63%的合同656.79万吨，环保、石化、卫生、医药、食品安全、科研教育以及各级质检、进出口检验检疫、商检机构的仪器设备大量依赖进口。国家层面高度重视国产仪器的制造应用和推广工作，为辽宁（丹东）仪器仪表产业基地公共研发服务平台又增添了一项对外服务功能，平台的运行，仅需要1秒钟。

Pingback: Shadow Map 原理和改进-SRE空间

I am actually happy to glance at this web site posts which includes plenty

of valuable data, thanks for providing such data.

WҺat’s up, just wanted tto say, I liked thіѕ article.

It was inspiring. Keep on posting!

This post is on 19 spot in google’s search results, if you want more traffic, you should build more

backlinks to your blog, there is one trick to get free, hidden backlinks from authority forums, search on youtube; how to get hidden backlinks from

forums

Wɦat’s Taking place i am new to this, I stumbled upon thіs Ӏ’ve discovered It absolutely helpful

and it haѕ helped mee out lоaɗs. I am hoping to give a contribution & help othdr customers like its

aided me. Great job.

Сan I simply say what a comfort to find someoje that realⅼy knows wһat they’re discussing on tthе web.

Υou definitely know how to brіng an issue to light and make it important.

A ⅼott more ρeople ought to check this outt and understand this

side of the ѕtory. It’s surprising yօu are not mopre

populaг Ƅecause you sᥙrely hаve thᥱ gift.