🔒 Locked
ESM: blocky blur
Advertisement
Original Post
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?
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.
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.
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?)

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:
Sample shadow:

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 shaderoutput.pos = mul(float4(pos, 1.f), g_worldViewProj);output.lightVec = mul(float4(pos, 1.f), g_worldView).xyz;//pixel shaderreturn 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) ));
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?
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?
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?
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?
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.
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.
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.
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.
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...
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...
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.
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.
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.
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 shadowmapfloat 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.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.
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.
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.
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.
Topic Locked
This topic has been locked by a moderator. New replies are not allowed.
Sign in to reply to this topic.
Advertisement