ESM: blocky blur
#1 Members - Reputation: 158
Posted 26 November 2009 - 09:56 PM
#2 Members - Reputation: 397
Posted 26 November 2009 - 10:18 PM
Also, are you performing bilinear filtering?
Be sure to give Marco Salvi's demo a try as it gives some idea of what to expect.
#3 Members - Reputation: 158
Posted 27 November 2009 - 04:00 AM

Maybe I should note, that I don't apply the blur in a separate pass but rather just before I do the lighting. That works quite well with (E)VSM.
Here is some code, just to make sure.
Generate shadow:
//vertex shader
output.pos = mul(float4(pos, 1.f), g_worldViewProj);
output.lightVec = mul(float4(pos, 1.f), g_worldView).xyz;
//pixel shader
return length(input.lightVec);
Sample shadow:
//pixel shader
//depth is the blurred (or unblurred) ESM-sample, shadowProj.z is the "reciever"
return saturate(exp(1.f * (depth - shadowProj.z + 0.008f) ));
#4 Members - Reputation: 397
Posted 27 November 2009 - 04:38 AM
Are you using a relatively simple format for your shadow map so that you can perform bilinear filtering in hardware, of are you using a more complex format (such as 32-bit floating point) which might require you to perform bilinear filtering via a shader?
#6 Members - Reputation: 397
Posted 27 November 2009 - 05:31 AM
But presuambly you are performing your blur using some kind of intermediate render target? Do you have bilinear filtering enabled on that? Also, make sure that the size and format match?
I have to admit, I'm guessing a little now.
Otherwise try implementing the bilinear filtering manually using the shader code from the ESM example?
#7 Members - Reputation: 158
Posted 29 November 2009 - 12:33 AM
Could there be some problem, by not using an intermediate target for the blurring? As I said, this approach worked fine with VSM.
Thanks for the input so far.
#8 Members - Reputation: 289
Posted 29 November 2009 - 01:59 AM
This is probably what you are seeing in your tests, but even if it works for you and you have some other problem, you shouldn't be relying on filtering these formats for a general release.
#9 Members - Reputation: 158
Posted 29 November 2009 - 07:30 PM
Quote:
Original post by Rubicon
bilinear filtering is not guaranteed to work with FP rendertargets. In fact there's good likelihood it won't.
According to the thread, which PolyVox hinted at, lack of bilinear filtering of fp-targets shouldn't be a problem on DX10+ class hardware. That's good enough for me.
I suspect I am doing a mistake elsewhere.
The range of the values stored in the shadowmap should be somewhere between 0 and the range of the light? Not (usually) from 0 to 1 and no exp() trickery at this point?
I than can take a bilinearly filtered sample, measure the difference to the reciever, exp() that and basically have my shadow? Do some blurring beforehand if I feel like it...
#13 Members - Reputation: 397
Posted 30 November 2009 - 11:24 AM
Quote:
Original post by Pragma
I think you need to put the exponential in the shadow generation phase instead of in the sampling phase. Filtering in exp-space is very different from filtering in linear space.
I had a thread about this recently: http://www.gamedev.net/community/forums/topic.asp?topic_id=554280. It seems either way is acceptable - you can write linear depth and filter in log space or you can write exponential depth and filter in linear space.
But it still looks like a point vs linear interpolation issue to me...
#14 Members - Reputation: 158
Posted 01 December 2009 - 01:54 AM
According to that thread I can take my working EVSM implementation and just replace the very last step, where I calculate the final shadow value. Is that about right?
I did that and the ESM variant has some strange blockiness again. But this time more akin to what happens when you just blur a normal shadow map. I think this is strange.
#15 Members - Reputation: 342
Posted 01 December 2009 - 07:21 AM
Quote:
Original post by PolyVox
It seems either way is acceptable - you can write linear depth and filter in log space or you can write exponential depth and filter in linear space.
It looks to me (based on his code and the screenshot) like he's writing linear depth and filtering in linear space. If you write linear depth and filter linearly then you just get exactly the same result as blurring a normal shadow map.
#16 Members - Reputation: 158
Posted 01 December 2009 - 09:09 AM
Quote:
Original post by Pragma
It looks to me (based on his code and the screenshot) like he's writing linear depth and filtering in linear space. If you write linear depth and filter linearly then you just get exactly the same result as blurring a normal shadow map.
The screenshot was taken with linear depth written but the blur was done in log-space.
//index has to do with different cascades of the shadowmap
float blur(float2 coord, float index)
{
float accum;
const float c = 1.f / 9.f;
accum = log_conv(c, g_shadowMap_dir.Sample(g_shadowSampler, float3(coord + g_shadowFilterKernel_dirw[0].xy, index)).x, c, g_shadowMap_dir.Sample(g_shadowSampler, float3(coord + g_shadowFilterKernel_dirw[1].xy, index)).x);
for (uint i = 2; i < g_shadowFilterSize; ++i)
{
accum = log_conv(1.f, accum, c, g_shadowMap_dir.Sample(g_shadowSampler, float3(coord + g_shadowFilterKernel_dirw[i].xy, index)).x);
}
return accum;
}
Since then I have tried to substitute the last part of my working EVSM code with something like saturate(overDarkening * (moments.x - exp(reciever))); and that gave me something that looked like if I would be blurring a normal shadowmap.
#17 Members - Reputation: 397
Posted 01 December 2009 - 09:41 PM
Quote:
Original post by PragmaIf you write linear depth and filter linearly then you just get exactly the same result as blurring a normal shadow map.
I must say I don't fully understand the difference between filtering in linear vs log space. In Marco Salvi's demo he writes linear depth and filters in log space as follows:
float log_conv ( float x0, float X, float y0, float Y )
{
return (X + log(x0 + (y0 * exp(Y - X))));
}
However, you can replace the convolution with a linear version as follows:
float log_conv ( float x0, float X, float y0, float Y )
{
return (x0 * X + y0 * Y);
}
If you do this the result is almost the same. You do see the image change, but unless you can comapre the two images you wouldn't know one was wrong:
Log Blur:
Linear Blur:

It seems to me that the magic of ESM (i.e. obtaining the soft edges) does not come from the way it is blurred, but from the fact that the comparison of occluder and receiver depths is replaced by the expression:
exp(over_darkening_factor * ( occluder - receiver ));
But if anyone can shed any more information on the difference between blurring in log vs linear space then I'm interested to hear it.
#18 Members - Reputation: 109
Posted 20 July 2011 - 03:51 PM
Pragma said:
PolyVox, on 01 December 2009 - 09:41 PM, said:
http://www.olhovsky....tering-in-hlsl/
The short version is that you typically output exp(depth) into your shadow map, which allows mathematically correct hardware filtering.
However you can also just output linear depth (i.e. output depth as is) and then perform prefiltering in log space, and allow a small error in hardware filtering. This allows you to use 16 bits for the ESM instead of 32 bits.


















