Jump to content



ESM: blocky blur

  • You cannot reply to this topic
17 replies to this topic

#1 B_old   Members   -  Reputation: 158

Like
0Likes
Like

Posted 26 November 2009 - 09:56 PM

Hello, I recently gave exponential shadow mapping a try. One problem I get, is that blurring of the shadowmap results in something relatively blocky. It is not exactly that the edges are too sharp, but rather, that I can make out individual texels. I tried with both a normal blur and the log_conv variant which didn't seem to make a big difference. Playing with the overdarkening factor didn't do much either in that regard. Anything obvious that I could be making wrong? Maybe I should post a code-snippet or a picture?

Ad:

#2 PolyVox   Members   -  Reputation: 397

Like
0Likes
Like

Posted 26 November 2009 - 10:18 PM

A picture would be useful - can you show both the blurred and unblurred versions applied to your scene? Or can you even show the blurred and unblurred contents of the depth target?

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 B_old   Members   -  Reputation: 158

Like
0Likes
Like

Posted 27 November 2009 - 04:00 AM

Here is a picture of what I mean. In this case, bilinear filtering is enabled. I used a 3x3 box filter. (Why is bilinear filtering ok if I have to blur with log_conv?)
ESM blurred

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 PolyVox   Members   -  Reputation: 397

Like
0Likes
Like

Posted 27 November 2009 - 04:38 AM

Well, my initial reaction is that it looks as though you are not performing any bilinear filtering. How does it look if you turn the bilinear filtering off? Is there a difference?

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?

#5 B_old   Members   -  Reputation: 158

Like
0Likes
Like

Posted 27 November 2009 - 04:51 AM

If I use point filtering the texels look slightly more distinct. I am using a 32 float format on NV GT200. Shouldn't that work? Never had such problems with VSM. Could I be choosing some parameters wrong in the shader?

#6 PolyVox   Members   -  Reputation: 397

Like
0Likes
Like

Posted 27 November 2009 - 05:31 AM

According to this thread you should be fine.

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 B_old   Members   -  Reputation: 158

Like
0Likes
Like

Posted 29 November 2009 - 12:33 AM

I am currently not using any intermediate render target. I just render the the scene depth to a fp32 target and sample from there in a deferred lighting pass. Bilinear filtering is used in that case.
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 Rubicon   Members   -  Reputation: 289

Like
0Likes
Like

Posted 29 November 2009 - 01:59 AM

bilinear filtering is not guaranteed to work with FP rendertargets. In fact there's good likelihood it won't.

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 B_old   Members   -  Reputation: 158

Like
0Likes
Like

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...





#10 Nik02   Members   -  Reputation: 1298

Like
0Likes
Like

Posted 30 November 2009 - 01:53 AM

I believe that FP32 filtering is only guaranteed on D3D11 hardware and up, and optional on D3D10.

#11 roel   Members   -  Reputation: 152

Like
0Likes
Like

Posted 30 November 2009 - 03:21 AM

Anyway, if you want to be sure if the filtering works as it should or that it is resorting to nearest point instead, give it a try. Whatever the outcome will be, it reduces your search space for your problem a lot :)

#12 Pragma   Members   -  Reputation: 342

Like
0Likes
Like

Posted 30 November 2009 - 07:38 AM

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.

#13 PolyVox   Members   -  Reputation: 397

Like
0Likes
Like

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 B_old   Members   -  Reputation: 158

Like
0Likes
Like

Posted 01 December 2009 - 01:54 AM

Thanks for directing me to the other thread.

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 Pragma   Members   -  Reputation: 342

Like
0Likes
Like

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 B_old   Members   -  Reputation: 158

Like
0Likes
Like

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 PolyVox   Members   -  Reputation: 397

Like
0Likes
Like

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:
Log Blur

Linear 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 Olhovsky   Members   -  Reputation: 109

Like
0Likes
Like

Posted 20 July 2011 - 03:51 PM

Pragma said:

If you write linear depth and filter linearly then you just get exactly the same result as blurring a normal shadow map.
That's not the case. You get ESM filtering, but with some error introduced.



View PostPolyVox, on 01 December 2009 - 09:41 PM, said:

I must say I don't fully understand the difference between filtering in linear vs log space.
To answer that question I've written a short article that explains why and how to filter in log space:
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.






We are working on generating results for this topic
PARTNERS