🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

Issues with (gaussian) blur transparency spilling (Direct3D11/HLSL)

Started by
4 comments, last by DKrz 2 years ago

Hello, I have an issue I'm trying to solve that I have with blurring.

First here's how I do it:

  1. Render all normal meshes to render target 1 (which is cleared with alpha 1.0 e.g. not transparent)
  2. Render all meshes in need of blur to render target 2 (cleared with alpha 0.0, thus has transparency)
  3. Grab render target 2 and render it to a full screen quad using the gauss blur shader (horizontal/vertical pass) and using render target 3 (also has transparency) to store the final blur buffer
  4. Bind render target 1 and render fs quad with render target 3 as a texture so it overlays blurred objects on top of normal objects (I also use the same depth target for RT1 and RT2)

Now this is where the issues start.

As you can see the normally rendered sphere causes a “gap” halo in a blurred sphere behind it. This happens only after I do the blurring.

This is how RT2 looks before the blur and would fit perfectly in the whole scene, no gaps:

and this is how RT2 looks after the blur:

as you can see - the transparent areas spill into the otherwise non-transparent areas adding transparency to edges where it's not needed.

Here's my shader:

cbuffer data : register(b0)

{

float2 dir;

};

cbuffer gauss : register(b2)

{

// 0 = kernel size, 1 - 9 = kernel weights

float gaussData[10];

}

Texture2D tex2D : register(t0);

SamplerState smplr : register(s0);

static const int r = (int)gaussData[0]; // kernel

float4 main(float2 tex : TEXCOORD, float4 screenCoord : SV_POSITION) : SV_TARGET

{

// approximation for 1920x1080

static const float2 pixelStep = { 0.0012, 0.0024 };

// init colors

float4 accColor = tex2D.Sample(smplr, tex) * gaussData[1]; // core pixel weight

// sample horizontal or vertical depending on direction

for (int i = 1; i <= r; i++)

{

accColor += tex2D.Sample(smplr, tex + float2(pixelStep.x * i * dir.x, pixelStep.y * dir.y * i)) * gaussData[i + 1]

+ tex2D.Sample(smplr, tex - float2(pixelStep.x * i * dir.x, pixelStep.y * dir.y * i)) * gaussData[i + 1];

}

return accColor;

}

Can you suggest the solution / improvement?

Kernel weights are generated using the gaussian distribution formula in the program itself and do add up to 1, no matter how many are used.

I also have an additional issue with darker colors spilling onto the normal objects, probably due to blurring resulting in dark grey colors with high enough alpha:

Advertisement

Why does render target 2 have the occlusion from the closer planet? Based on your description there should be no occlusion, unless you are sharing the depth buffer? You'll only avoid the halo artifacts if you can remove the occlusion in the blurred render target. I would use a separate depth buffer for each pass, or clear the depth between passes.

To fix the other issue with dark color bleed, I think you want to use additive blending in the compositing pass rather than the usual linear interpolation blending:
RT1.rgb + RT2.rgb*RT2.a
instead of
RT1.rgb*(1-RT2.a) + RT2.rgb*RT2.a

Aressera said:

Why does render target 2 have the occlusion from the closer planet? Based on your description there should be no occlusion, unless you are sharing the depth buffer? You'll only avoid the halo artifacts if you can remove the occlusion in the blurred render target. I would use a separate depth buffer for each pass, or clear the depth between passes.

To fix the other issue with dark color bleed, I think you want to use additive blending in the compositing pass rather than the usual linear interpolation blending:
RT1.rgb + RT2.rgb*RT2.a
instead of
RT1.rgb*(1-RT2.a) + RT2.rgb*RT2.a

I do share the depth buffer - but how do I make RT2 respect the depth of RT1 meshes if it has an independent depth of its own? won't it just get blended over the RT1 meshes?

Or will the solution be to write a mixing shader that has two depth buffers as Texture2Ds, compares depth between both and determines whether to draw a pixel from RT1 or RT2? Or am I missing something?

DKrz said:
Or will the solution be to write a mixing shader that has two depth buffers as Texture2Ds, compares depth between both and determines whether to draw a pixel from RT1 or RT2? Or am I missing something?

Pretty much this, but it depends on what you are trying to do (depth of field?). For real DOF you need to do a blur of the whole image with kernel size changing based on depth. You could also try blurring the whole image at a few different kernel sizes (e.g. powers of 2), then composite those images together, interpolating based on the relationship of depth and focal length.

Aressera said:

DKrz said:
Or will the solution be to write a mixing shader that has two depth buffers as Texture2Ds, compares depth between both and determines whether to draw a pixel from RT1 or RT2? Or am I missing something?

Pretty much this, but it depends on what you are trying to do (depth of field?). For real DOF you need to do a blur of the whole image with kernel size changing based on depth. You could also try blurring the whole image at a few different kernel sizes (e.g. powers of 2), then composite those images together, interpolating based on the relationship of depth and focal length.

OK, it works, I render to two separate buffers and two separate depths - then combine them together using a fullscreen quad to get both complete depth and proper gapless blurring.

As for additive blending - I probably should use linear interpolation when RT2 Z is less than RT1 Z and use additive blending when RT2 Z is infinite (aka 1.0)?

Because doing additive blending with anything behind the RT2 mesh will result in color addition from RT1 pixel even if RT2 alpha equals 1.0 and RT2 pixel with Z = 1.0 means there's no RT2 mesh present but blur tail may still be there and will be fine with being added probably

This topic is closed to new replies.

Advertisement