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 london ontario movers kijiji on 9 de dezembro de 2016 - 9:02 pm

    Here are a number of the web sites we advise for our visitors

  2. #2 by Health Facts on 10 de dezembro de 2016 - 1:06 am

    I think this is one of the most vital information for me. And i’m glad reading your article. But wanna remark on some general things, The site style is great, the articles is really excellent : D. Good job, cheers

  3. #3 by Kitchen Ideas on 10 de dezembro de 2016 - 1:06 am

    whoah this weblog is magnificent i like reading your posts. Keep up the great work! You know, many people are looking around for this information, you can aid them greatly.

  4. #4 by arcteryx sale online on 10 de dezembro de 2016 - 3:45 am

    Dear admin, thanks for providing this blog post. I found it great. Take care,
    arcteryx sale online http://www.arcteryxoutletonline.com

  5. #5 by nike air max thea on 10 de dezembro de 2016 - 4:39 am

    I have to express my admiration for your kindness for women who must have guidance on in this topic. Your special commitment to getting the solution up and down has been amazingly interesting and have always empowered girls much like me to get to their ambitions. Your entire valuable tips and hints implies a great deal to me and a whole lot more to my fellow workers. Many thanks; from everyone of us.

  6. #6 by patagonia online on 10 de dezembro de 2016 - 6:10 am

    Thanks for an idea, you sparked at thought from a angle I hadnt given thoguht to yet. Now lets see if I can do something productive with it.
    patagonia online http://www.mcadamssupplyco.com/patagonia/

  7. #7 by barbour quilted jackets women on 10 de dezembro de 2016 - 8:38 am

    I really like your wordpress web template, where did you get a hold of it through?
    barbour quilted jackets women http://www.bedcapdealers.com/barbour/

  8. #8 by arcteryx factory outlet on 10 de dezembro de 2016 - 8:40 am

    You made some good points there. I did a search on the topic and found most people will agree with your blog.
    arcteryx factory outlet http://www.unilorites.com/arcteryx/

1 114 115 116
(não será publicado)