There are different ways to perform blur and this is one of the most common way to do it in a shader. It’s a two step method with first a horizontal blur and then a vertical blur. By splitting the work in two directions (two passes) you can save a lot of computation.

The method can be divided in the following parts:

- Render the scene you want to blur to a texture (could be downsampled)
- Render a screen aligned quad with the horizontal blur shader to a texture
- Render a screen aligned quad with the vertical blur shader to the screen or texture depending on what you want to use it for

The following image shows how the blur works when splitted up in two directions.

Here’s the horizontal blur shader.

Vertex Shader (GLSL) . This shader screen align a quad with width 1. Any method to render a screen aligned quad will work. So you’re free to use other shaders.

varying vec2 vTexCoord;
// remember that you should draw a screen aligned quad
void main(void)
{
gl_Position = ftransform();;
// Clean up inaccuracies
vec2 Pos;
Pos = sign(gl_Vertex.xy);
gl_Position = vec4(Pos, 0.0, 1.0);
// Image-space
vTexCoord = Pos * 0.5 + 0.5;
} |

Fragment Shader (GLSL)

uniform sampler2D RTScene; // the texture with the scene you want to blur
varying vec2 vTexCoord;
const float blurSize = 1.0/512.0; // I've chosen this size because this will result in that every step will be one pixel wide if the RTScene texture is of size 512x512
void main(void)
{
vec4 sum = vec4(0.0);
// blur in y (vertical)
// take nine samples, with the distance blurSize between them
sum += texture2D(RTScene, vec2(vTexCoord.x - 4.0*blurSize, vTexCoord.y)) * 0.05;
sum += texture2D(RTScene, vec2(vTexCoord.x - 3.0*blurSize, vTexCoord.y)) * 0.09;
sum += texture2D(RTScene, vec2(vTexCoord.x - 2.0*blurSize, vTexCoord.y)) * 0.12;
sum += texture2D(RTScene, vec2(vTexCoord.x - blurSize, vTexCoord.y)) * 0.15;
sum += texture2D(RTScene, vec2(vTexCoord.x, vTexCoord.y)) * 0.16;
sum += texture2D(RTScene, vec2(vTexCoord.x + blurSize, vTexCoord.y)) * 0.15;
sum += texture2D(RTScene, vec2(vTexCoord.x + 2.0*blurSize, vTexCoord.y)) * 0.12;
sum += texture2D(RTScene, vec2(vTexCoord.x + 3.0*blurSize, vTexCoord.y)) * 0.09;
sum += texture2D(RTScene, vec2(vTexCoord.x + 4.0*blurSize, vTexCoord.y)) * 0.05;
gl_FragColor = sum;
} |

And here’s the vertical blur shader.

Vertex Shader (GLSL) (the same as for the blur in horizontal direction)

varying vec2 vTexCoord;
// remember that you should draw a screen aligned quad
void main(void)
{
gl_Position = ftransform();;
// Clean up inaccuracies
vec2 Pos;
Pos = sign(gl_Vertex.xy);
gl_Position = vec4(Pos, 0.0, 1.0);
// Image-space
vTexCoord = Pos * 0.5 + 0.5;
} |

Fragment Shader (GLSL)

uniform sampler2D RTBlurH; // this should hold the texture rendered by the horizontal blur pass
varying vec2 vTexCoord;
const float blurSize = 1.0/512.0;
void main(void)
{
vec4 sum = vec4(0.0);
// blur in y (vertical)
// take nine samples, with the distance blurSize between them
sum += texture2D(RTBlurH, vec2(vTexCoord.x, vTexCoord.y - 4.0*blurSize)) * 0.05;
sum += texture2D(RTBlurH, vec2(vTexCoord.x, vTexCoord.y - 3.0*blurSize)) * 0.09;
sum += texture2D(RTBlurH, vec2(vTexCoord.x, vTexCoord.y - 2.0*blurSize)) * 0.12;
sum += texture2D(RTBlurH, vec2(vTexCoord.x, vTexCoord.y - blurSize)) * 0.15;
sum += texture2D(RTBlurH, vec2(vTexCoord.x, vTexCoord.y)) * 0.16;
sum += texture2D(RTBlurH, vec2(vTexCoord.x, vTexCoord.y + blurSize)) * 0.15;
sum += texture2D(RTBlurH, vec2(vTexCoord.x, vTexCoord.y + 2.0*blurSize)) * 0.12;
sum += texture2D(RTBlurH, vec2(vTexCoord.x, vTexCoord.y + 3.0*blurSize)) * 0.09;
sum += texture2D(RTBlurH, vec2(vTexCoord.x, vTexCoord.y + 4.0*blurSize)) * 0.05;
gl_FragColor = sum;
} |

And this is a scene without blur.

And this is the same scene but with gaussian blur.

You can tweak the blur radius to change the size of the blur and change the number of samples in each direction.

Cost for separable blur shader : 9+9 = 18 (number of texture samples)

Cost for shader if blured in one pass: 9*9 = 81 (number of texture samples)

So splitting up in two directions saves a lot.

The gaussian weights are calculated accordingly to the gaussian function with standard deviation of 2.7. These calculations were done in the excel document found [2].

Here’s a description of blur shaders and other image processing shaders in DirectX:

[1] http://ati.amd.com/developer/shaderx/ShaderX2_AdvancedImageProcessing.pdf

More info about calculating weights for separable gaussian blur:

[2] http://theinstructionlimit.com/?p=40