Gaussian Blur with Depth Preservation


This post will show a simple implementation of a gaussian blur effect with edge preservation.

Sometimes (like in fluid rendering) we need to perform a gaussian blur but we also need to still with edge preservation. The following code shows how to achieve this.

First, we need to calculate the Gaussian Blur Kernel and sent them to the GPU:

   
private float sigma;
        private float[] kernel;
        private Vector2[] offsetsHoriz;
        private Vector2[] offsetsVert;
        private int radius;
        private float amount;
        void ComputeKernel(int blurRadius, float blurAmount)
        {
            radius = blurRadius;
            amount = blurAmount;

            kernel = null;
            kernel = new float[radius * 2 + 1];
            sigma = radius / amount;

            float twoSigmaSquare = 2.0f * sigma * sigma;
            float sigmaRoot = (float)Math.Sqrt(twoSigmaSquare * Math.PI);
            float total = 0.0f;
            float distance = 0.0f;
            int index = 0;

            for (int i = -radius; i <= radius; ++i)
            {
                distance = i * i;
                index = i + radius;
                kernel[index] = (float)Math.Exp(-distance / twoSigmaSquare) / sigmaRoot;
                total += kernel[index];
            }

            for (int i = 0; i < kernel.Length; ++i)
                kernel[i] /= total;
        }
        void ComputeOffsets(float textureWidth, float textureHeight)
        {
            offsetsHoriz = null;
            offsetsHoriz = new Vector2[radius * 2 + 1];

            offsetsVert = null;
            offsetsVert = new Vector2[radius * 2 + 1];

            int index = 0;
            float xOffset = 1.0f / textureWidth;
            float yOffset = 1.0f / textureHeight;

            for (int i = -radius; i <= radius; ++i)
            {
                index = i + radius;
                offsetsHoriz[index] = new Vector2(i * xOffset, 0.0f);
                offsetsVert[index] = new Vector2(0.0f, i * yOffset);
            }
        }       

We call both funtions in Intialization time passing the parameters: blurRadius = something ranging from 2 to 15 (the Kernel width), blurAmount = blur scale parameter, textureWidth = width of the texture that will be blured, textureHeight = same for height.

Here is the shader that Performs the Gaussian Blur (the RADIUS value must be defined in Compilation time, it must have the same value used in CPU side)

#define RADIUS  15
#define KERNEL_SIZE (RADIUS * 2 + 1)
float weights[KERNEL_SIZE];
float2 offsets[KERNEL_SIZE];

float2 GBufferPixelSize; ///half pixel size of the origin render target(0.5f /width , 0.5f/height)
float2 TempBufferRes; ///destiny buffer size
float blurDepthFalloff;

sampler2D depthSampler : register(s0);
sampler2D ssaoSampler : register(s1);

struct VertexShaderInput
{
    float4 Position : POSITION0;
	float2 TexCoord : TEXCOORD0;
};

struct VertexShaderOutput
{
    float4 Position : POSITION0;
	float4 TexCoord : TEXCOORD0;
};

VertexShaderOutput VertexShaderBlur(VertexShaderInput input)
{
    VertexShaderOutput output = (VertexShaderOutput)0;

	output.Position = input.Position;
	output.TexCoord.xy = input.TexCoord + GBufferPixelSize;
	output.TexCoord.zw = input.TexCoord + 0.5f / TempBufferRes;
	return output;
}

/////////////////////////////////// bilateral

float4 PS_GaussianBlurTriple(float4 texCoord : TEXCOORD0) : COLOR0
{
    float3 color = 0;

    float depth = tex2D(depthSampler, texCoord.xy).x;	

    float s=0;
    for (int i = 0; i < KERNEL_SIZE; ++i)
	{
		float3 im = tex2D(ssaoSampler, texCoord.zw + offsets[i] );
		float d = tex2D(depthSampler, texCoord.xy + offsets[i] ).x;
		float r2 = abs(depth - d) * blurDepthFalloff;
		float g = exp(-r2*r2);
		color +=  im* weights[i] * g;
		s+=g* weights[i];
	}
	color = color/s;
	return float4(color,1);
}

technique GAUSSTriple
{
    pass p0
    {
	VertexShader = compile vs_3_0 VertexShaderBlur();
        PixelShader = compile ps_3_0 PS_GaussianBlurTriple();
    }
}

//////////////////////////////////////

float4 PS_GaussianBlurSingle(float4 texCoord : TEXCOORD0) : COLOR0
{
    float color = 0;
    float depth = tex2D(depthSampler, texCoord.xy).x;	

	float s=0;
    for (int i = 0; i < KERNEL_SIZE; ++i)
	{
		float im = tex2D(ssaoSampler, texCoord.zw + offsets[i] ).x;
		float d = tex2D(depthSampler, texCoord.xy + offsets[i] ).x;
		float r2 = abs(depth - d) * blurDepthFalloff;
		float g = exp(-r2*r2);
		color +=  im* weights[i] * g;
		s+=g* weights[i];
	}
	color = color/s;
	return float4(color,0,0,1);
}

technique GAUSSSingle
{
    pass p0
    {
		VertexShader = compile vs_3_0 VertexShaderBlur();
        PixelShader = compile ps_3_0 PS_GaussianBlurSingle();
    }
}

I included two version of the pixel shader, one for Color Texture and another for float point textures (used in fluid rendering for example).
The blurDepthFalloff is a artistic/adjustment parameter
The DepthTexture is a texture containing the depth of the Scene (in z/w form). See this article for more details about how to generate it.

To render the Scene (Post Effect) we use Render a Quad using the previous shader and the avaluated parameters.

            rHelper.PushRenderTarget(RenderTarget2D);
            effect.Parameters["blurDepthFalloff"].SetValue(blurDepthFalloff);
            effect.Parameters["weights"].SetValue(kernel);
            effect.Parameters["offsets"].SetValue(offsetsHoriz);
            effect.Parameters["GBufferPixelSize"].SetValue(new Vector2(1f / ImageToProcess.Width, 1f / ImageToProcess.Height));
            effect.Parameters["TempBufferRes"].SetValue(destinySize.Value);
            rHelper.Textures[0] = rHelper[PrincipalConstants.DephRT];
            rHelper.Textures[1] = ImageToProcess;
            SamplerState s0 = rHelper.SetSamplerState(SamplerState.PointClamp, 0);
            SamplerState s1 = rHelper.SetSamplerState(ImageSamplerState, 1);
            rHelper.RenderFullScreenQuadVertexPixel(effect);

            rHelper.PopRenderTarget();

            effect.Parameters["offsets"].SetValue(offsetsVert);
            rHelper.Textures[1] = RenderTarget2D;
            rHelper.RenderFullScreenQuadVertexPixel(effect);

            rHelper.SetSamplerState(s0, 0);
            rHelper.SetSamplerState(s1, 1);

Quite Simple !
The Gaussian Blur with edge preservation is not Separable (we cant perform a Y and a X pass independentely) but for performance we normally dont care about this.

,

  1. #1 by http://www.silvercode.com/misc/file/415.html on 28 de abril de 2017 - 4:14 am

    Our headset was just as spectacular since in picture. Information technology appeared promptly. I’d recommend choosing a towel over every single coating as you hit that it out w / your steam iron. It doesn’t steam away along with only a steamer. That iron was required. It is very fine, when you do not trust personally with the towel then vapor iron, after that consume this up to a professional. Beautiful sound.

  2. #2 by w88.com on 28 de abril de 2017 - 4:52 am

    What i do not realize is in truth how you are no longer actually much more well-liked than you might be right now. You’re so intelligent. You already know therefore considerably in terms of this topic, made me in my view consider it from numerous various angles. Its like men and women are not fascinated until it?¦s one thing to accomplish with Girl gaga! Your personal stuffs great. At all times care for it up!

  3. #3 by w88 link vao on 28 de abril de 2017 - 5:23 am

    I loved as much as you’ll receive carried out right here. The sketch is attractive, your authored material stylish. nonetheless, you command get got an shakiness over that you wish be delivering the following. unwell unquestionably come more formerly again since exactly the same nearly a lot often inside case you shield this hike.

1 251 252 253
(não será publicado)