I have had a lot of fun over the past few weeks changing my shadowing system from a single Variance Shadow Mapping system to a Expoential Parallel Split Shadow Mapping one. I will detail what I have learned, post some code snippets, resources and my results.
My first implementation of shadow mapping was done using the standard method that everyone learns, where you render the scene from the point of view of the light, storing depth values and use that combined with some sort of a Percentage Close Filtering. A good example is from a book that I recommend to anyone who wants to learn about direct x Programming: Introduction to 3D Game Programming with Direct x 10 by Frank D Luna. At this time, he has a Direct x 11 book out, so get that! If you want the code used in his book for standard shadow mapping, it is freely available at http://www.d3dcoder.net/d3d10.aspx Download PartII, open chater 13 for the source.
After implementing the standard approach, I learned a lot, which allowed me to move to implementing Variance Shadow maps. I will not do what other web sites do and post a bunch of math, or other garbage –I will speak plainly! Variance Shadow Maps (VSM) will give results that cannot be attained in real time using the standard shadow mapping. VSM’s do have a downside: light bleeding. Light bleeding is where light can bleed into a shadow creating some artifacts, check out some pictures here. The good thing, is that light bleeding can be controlled with some extra parameters used in the shadowing formula. There is a 2x increase in video ram for VSM’s but, the results are astonishing. The increase in vram is not a problem: 16 megs vs 32 megs of ram is nothing to be concerned over when video cards are into the Gigabytes of ram now. The most popular site to see a demo is http://www.punkuser.net/vsm/ but, I would not recommend trying to learn from the code posted — it is very cryptic. For an good example, check out the Nvidia Direct x SDK download the SDK, install it, and check out the VSM example.
The next step is a small one, Exponential Shadow Maps (ESM’s). Unfortunalty, there is not much information on ESM’s on the net; there are a couple of papers, and no examples at all (unless you really dig, I will explain in a bit). Why is there so little information on the internet? I do not know, maybe because ESM’s are relatively new? A good book to get that covers ESM’s is ShaderX 6, in Chapter 4.3. If you are researching ESM’s, do not use this link. The code is wrong.. wrong .. wrong! There is a link to a zip file that the author posts, it is some kind of a model file, but it contains shaders too! So, I wanted to see what was going on and I opened the file up in a text editor. First, I will post some code from the link (it is wrong, I will explain)
float2 occluder = tex2D(ShadowSampler, front).x; float depth_scale = 20.0f; float receiver = depth_scale * myDepth - light_shadow_bias; float shadow = exp(over_darkening_factor * ( occluder * depth_scale - receiver )); shadow = saturate(shadow);
Inside the zip file, I found this
float over_darkening_factor : CHANNELVALUE1< string UIName = "Over darkening factor"; string UIWidget = "slider"; float UIMin = 1.0f; float UIStep = 0.01f; float UIMax = 50.0f; > = 1.0f;
So, the min value of the overdarkening is 1, and the max is 50!? The depth_scale is also 20! Somehow, he may have got the technique to work for the scene, but it is wrong. Here is the correct code for your shader
receiver should be the world space distance to the light from the pixel. Overdark can go from 0 to 1 and it controls how soft the shadow will appear; the smaller the value the more soft the shadow.
float occluder= shadowtexture.Sample( linearsample, float2(LightTexCoord) );
float overdark =.05f;
float lit = exp(overdark* ( occluder – receiver ));
lit = saturate(lit);
The only difference between ESMs and VSMs is that ESMs use a single float32, instead of two float32s and the code to calculate the shadowing is listed above, instead of using Chebychev’s inequality. There is NO DIFFERENCE between ESMs and VSMs except the code for calculating the shadow (listed above). A log blurring can be added as well, to increase the look of the shadows, but it is not necessary. ESM’s are faster, use less ram than VSMs and lightbleeding seldom occurs –they are superior to VSMs in every way!
A single shadow map will only cover so much of the view, which leads to a need to use multiple shadow maps. On comes Parallel Split shadow maps(PSSMs)! Firstly, do not use cascading shadow map techniques, there are obvious discontinuities between shadow map layers that cannot be avoided. Second, DO NOT USE THE GEOMETRY SHADER as suggested in GPU gems 3! This will cause the framerate to decrease by 50%. The geometry shader is only for modifying/creating small amount of geometry. If you attempt to use it on your scene to select specific render targets are in the GPU gems 3 example, you will not be happy. In addition to the speed decrease , using the gemoetry shader, will lead to an increase in memory because a depth buffer will need to be created for each render target split. It is funny, GPU gems 3 lists chapter 10 as the example for Parallel Split shadow maps, but chapter 8 (listed as summed table VSMs) has a superior example of PSMs. Nvidia has not released GPU gems 3 source code yet, but if you can somehow get your hands on it, check out Chapter 8 –a clean example of PSSMs.
In the end, I implemented EPSSMs –nice acronym, isn’t it?
Here are some pictures.
This picture shows the splits in the shadow maps. I have four splits of 512 x 512 shadow maps with a 5 x5 box blur. As you can see, the effect is ok.

Same scene, with a 1024 x 1024 5×5 box blur. Not bad for a 12% decrease in frame rate.

The next is the same scene with a 2048×2048 size for each shadow map. The shadow quality is near perfect now.

The next picture is a 7×7 gausian blur. As you can see, it leaves some jaggies.

This is a 7×7 box blur, which gives better results than the guasian blur — go figure.

This is a picture showing how long shadows can be produced with PSMs. Pretty neat isn’t it?

-Scott
Recent Comments