How to optimize blur shader for soft shadows?

Started by
5 comments, last by turanszkij 6 years, 2 months ago

Hi, I'm on Rastertek series 42, soft shadows, which uses a blur shader and runs extremely slow.

http://www.rastertek.com/dx11tut42.html

He obnoxiously states that there are many ways to optimize his blur shader, but gives you no idea how to do it.

The way he does it is :

1. Project the objects in the scene to a render target using the depth shader.

2. Draw black and white shadows on another render target using those depth textures.

3. Blur the black/white shadow texture produced in step 2 by 

a) rendering it to a smaller texture

b) vertical / horizontal blurring that texture

c) rendering it back to a bigger texture again.

4. Send the blurred shadow texture into the final shader, which samples its black/white values to determine light intensity.

 

So this uses a ton of render textures, and I just added more than one light, which multiplies the render textures required.

 

Is there any easy way I can optimize the super expensive blur shader that wouldnt require a whole new complicated system?

Like combining any of these render textures into one for example?

 

If you know of any easy way not requiring too many changes, please let me know, as I already had a really hard time

understanding the way this works, so a super complicated change would be beyond my capacity. Thanks.

 

*For reference, here is my repo, in which I have simplified his tutorial and added an additional light.

 

https://github.com/mister51213/DX11Port_SoftShadows/tree/MultiShadows

 

Advertisement

The downside with this technique is that you also have light leaking in depth discontinuities because the blur is performed in screen space. And if you use bilateral blur to correct it, it is no longer a separable blur. 

The usual way to sample soft shadows is PCF filtering. When you sample the shadow map to determine if the surface is in shadow or not, you can take multiple taps of the shadow map from neighboring texels and average the shadow terms gathered from all of them. Neighbor texels can be taken in several shapes eg just regular grid or poisson disk, each of them having advantages/disadvantages.

An other shadow mapping algorithm would be Variance Shadow Maps, which can be pre-blurred by a separable filter in shadow map space instead of screen space.

Also, I wouldn't worry about the number of render targets all that much.  Doom/GTA5 use in the range of 70 per frame based on analysis.  It's more how they're used and the shaders used on them.

Also, you're not just copying and pasting/retyping these tutorials I hope?  You're looking up each function, studying each concept as much as possible before moving on?  Otherwise it's unlikely to stick with you and is more or less a waste of time considering what it could be.

"Those who would give up essential liberty to purchase a little temporary safety deserve neither liberty nor safety." --Benjamin Franklin

5 hours ago, Mike2343 said:

Also, I wouldn't worry about the number of render targets all that much.  Doom/GTA5 use in the range of 70 per frame based on analysis.  It's more how they're used and the shaders used on them.

Also, you're not just copying and pasting/retyping these tutorials I hope?  You're looking up each function, studying each concept as much as possible before moving on?  Otherwise it's unlikely to stick with you and is more or less a waste of time considering what it could be.

Of course not - to combine any two of his tutorials is so unimaginably complex that copy pasting would never work.

Even if I copy paste, I'm forced to then go back through it line by line and digest it just to get it working. I wish I could copy paste them!

9 hours ago, turanszkij said:

The downside with this technique is that you also have light leaking in depth discontinuities because the blur is performed in screen space. And if you use bilateral blur to correct it, it is no longer a separable blur. 

The usual way to sample soft shadows is PCF filtering. When you sample the shadow map to determine if the surface is in shadow or not, you can take multiple taps of the shadow map from neighboring texels and average the shadow terms gathered from all of them. Neighbor texels can be taken in several shapes eg just regular grid or poisson disk, each of them having advantages/disadvantages.

An other shadow mapping algorithm would be Variance Shadow Maps, which can be pre-blurred by a separable filter in shadow map space instead of screen space.

1. Re: Variance, if I blur each shadow in shadow map space wouldnt it be even slower, because the blur effect takes 4 render targets, and there are 3 shadows, so it would be better to blur them all to one texture after the shadowing is done, as I am doing now?

2. Regarding PCF filtering, I tried doing that but it did absolutely nothing. Here is the topic regarding that on GameDev.net. Maybe I didnt sample the shadows right? Could you please explain, how do you "take multiple taps of the shadow map from neighboring texels"? How do you know which texels are nearby to a given texel? Do you just randomly apply an offset to the texCoord value inside the Pixel shader?

 

44 minutes ago, mister345 said:

Of course not - to combine any two of his tutorials is so unimaginably complex that copy pasting would never work.

Even if I copy paste, I'm forced to then go back through it line by line and digest it just to get it working. I wish I could copy paste them!

1. Re: Variance, if I blur each shadow in shadow map space wouldnt it be even slower, because the blur effect takes 4 render targets, and there are 3 shadows, so it would be better to blur them all to one texture after the shadowing is done, as I am doing now?

2. Regarding PCF filtering, I tried doing that but it did absolutely nothing. Here is the topic regarding that on GameDev.net. Maybe I didnt sample the shadows right? Could you please explain, how do you "take multiple taps of the shadow map from neighboring texels"? How do you know which texels are nearby to a given texel? Do you just randomly apply an offset to the texCoord value inside the Pixel shader?

 

Not randomly, there's patterns and etc. Here's a nice tutorial instead of explaining here.

As for variance shadow maps, they're very fast at low resolutions but grow badly with it, 4x the res = 4x the cost. They also have lightleak which to fix you need to go down some infinite hole of ever longer papers. I'd definitely stick with PCF first and try playing around with an offset biases to clean up artifacts.

8 hours ago, mister345 said:

2. Regarding PCF filtering, I tried doing that but it did absolutely nothing. Here is the topic regarding that on GameDev.net. Maybe I didnt sample the shadows right? Could you please explain, how do you "take multiple taps of the shadow map from neighboring texels"? How do you know which texels are nearby to a given texel? Do you just randomly apply an offset to the texCoord value inside the Pixel shader?

Here is my PCF filtering code with multiple taps for soft shadow:


SamplerComparisonState sampler_cmp_depth;
Texture2DArray<float> texture_shadowArray_2d;
  
// ...

float sum = 0;
float samples = 0.0f;
static const float range = 1.5f;
for (float y = -range; y <= range; y += 1.0f)
{
  for (float x = -range; x <= range; x += 1.0f)
  {
	sum += texture_shadowarray_2d.SampleCmpLevelZero(sampler_cmp_depth, float3(shadowUV + float2(x, y) * light.shadowKernel, light.shadowIndex), realDistance).r;
	samples++;
  }
}
float shadow *= sum / samples;

The sampler is a comparison sampler state, with GREATER_EQUAL comparison func, but that depends on your shadow depth map range. I use a reversed depth map, which means near values are 1, far values are 0. 

I am using a texture array, which expects a float3 sampling coordinate as the second parameter, the Z being the selection of the texture slice. You can just leave that parameter as a float2 with regular Texture2D.

light.shadowKernel is a float meaning how wide the kernel will be. I set this to 1.0f / SHADOWMAP_RESOLUTION value. This means the kernel will be sampled with a pixel-wide grid offset.

SampleCmpLevelZero is a sampling function that can be called on SamplerComparisonState only. The last parameter expects a reference value and comparisons will be made to this internally, and filtered according to the sampler filtering. I use a D3D11_FILTER_COMPARISON_MIN_MAG_LINEAR_MIP_POINT filtering for this. My code has the realDistance reference value which is the current world pixel depth projected by the shadow matrix.

This double loop will sample a 5x5 PCF grid. You can make this loop depend on a constant buffer value and make it adjustable by the application to control how soft you want your shadows.

Good luck!

This topic is closed to new replies.

Advertisement