Apr 23, 2010

Soft shadow with shadow map...

There were several new innovative shadow map techniques that promised soft shadow. There are Variance shadow map, and Exponential shadow map.

They are using 32bit floating point surfaces and they assumes they work with linear hardware filter. Unfortunately PlayStation3 does not support linear nor bilinear filter for 32bit floating point surface. How am I supposed to get soft shadow?My final choice was a little bit modified version of Percentage Closer Filter, which means I fall back to the bottom line. I modified it to mimic the bilinear filter.

I am not quite happy because I cannot get any benefits from new hardware accelerations. But it seems to work fairly good.

*PS: The screenshot it from here.


pixelstoomany said...


You can implement bi-linear filtering in software. I have ran some tests with ESM on PS3 in this sense a few years ago and it wasn't that much slower than a point filtered implementation.

For instance if you already have a function that performs ESM point filtering on FP32 textures, a bi-linear filtering implementation would look like this:

float ESMBilinearSample(in float2 textureCoords, in float receiverDepth)
float2 unnormCoords = textureCoords * mShadowMapSize.xx;

const float a = frac(unnormCoords.x - 0.5f);
const float b = frac(unnormCoords.y - 0.5f);
const float i = floor(unnormCoords.x - 0.5f);
const float j = floor(unnormCoords.y - 0.5f);

float sample00 = ESMPointSample(float2(i, j) / mShadowMapSize.xx, receiverDepth);
float sample01 = ESMPointSample(float2(i, j + 1) / mShadowMapSize.xx, receiverDepth);
float sample10 = ESMPointSample(float2(i + 1, j) / mShadowMapSize.xx, receiverDepth);
float sample11 = ESMPointSample(float2(i + 1, j + 1) / mShadowMapSize.xx, receiverDepth);

return (1 - a)*(1 - b)*sample00 + a*(1-b)*sample10 + (1-a)*b*sample01 + a*b*sample11;

If you don't have to support large depth ranges you might simply switch to FP16 texturing (which PS3 can natively bi-linearly filter) and log-filtering.


Jay said...

Thank Marco.
I was surprised by your informative comment.

I haven't thought of the combination between ESM and manual bilinear filter.

Your code looks very similar to what I implemented yesterday.

The last bilinear calculation can be a little bit simplified according to wikipedia: http://en.wikipedia.org/wiki/Bilinear_filtering

It can be like this:

const float2 percents = frac( unnormCoords + 0.5 );

const float u0 = lerp( sample00, sample01, percents.x );
const float u1 = lerp( sample10, sample11, percents.x );

return lerp( u0, u1, percents.y );

For the FP16 surface, I still see some problems.

PS3 does not support one channel FP16 surface but support F_W16Z16Y16X16. It seems like I have no way to use just one channel of FP16.

I'm very glad to hear the other programmer's experience. :D