## Tuesday, September 21, 2010

A Gaussian blur is one of the most useful post-processing techniques in graphics yet I somehow find myself hard pressed to find a good example of a Gaussian blur shader floating around on the interwebs. I've included below a very flexible, separable Gaussian blur shader in GLSL. The theory behind its value generation can be found in GPU Gems 3; Chapter 40 ("Incremental Computation of the Gaussian" by Ken Turkowski).

Here's the extremely simple vertex shader; the assumption being that you'll be feeding a fullscreen quad to the vertex shader such that it's 4 vertices are positioned at the corners of the viewport:
`void main() {    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;    gl_TexCoord = gl_MultiTexCoord0;}`
The fragment shader is setup using macros separated based on the blur direction (i.e., horizontal/vertical) and the blur kernel size (currently 5, 7 and 9; but this can easily be extended). Here's the fragment shader:
`uniform float sigma;     // The sigma value for the gaussian function: higher value means more blur                         // A good value for 9x9 is around 3 to 5                         // A good value for 7x7 is around 2.5 to 4                         // A good value for 5x5 is around 2 to 3.5                         // ... play around with this based on what you need :)uniform float blurSize;  // This should usually be equal to                         // 1.0f / texture_pixel_width for a horizontal blur, and                         // 1.0f / texture_pixel_height for a vertical blur.uniform sampler2D blurSampler;  // Texture that will be blurred by this shaderconst float pi = 3.14159265f;// The following are all mutually exclusive macros for various // seperable blurs of varying kernel size#if defined(VERTICAL_BLUR_9)const float numBlurPixelsPerSide = 4.0f;const vec2  blurMultiplyVec      = vec2(0.0f, 1.0f);#elif defined(HORIZONTAL_BLUR_9)const float numBlurPixelsPerSide = 4.0f;const vec2  blurMultiplyVec      = vec2(1.0f, 0.0f);#elif defined(VERTICAL_BLUR_7)const float numBlurPixelsPerSide = 3.0f;const vec2  blurMultiplyVec      = vec2(0.0f, 1.0f);#elif defined(HORIZONTAL_BLUR_7)const float numBlurPixelsPerSide = 3.0f;const vec2  blurMultiplyVec      = vec2(1.0f, 0.0f);#elif defined(VERTICAL_BLUR_5)const float numBlurPixelsPerSide = 2.0f;const vec2  blurMultiplyVec      = vec2(0.0f, 1.0f);#elif defined(HORIZONTAL_BLUR_5)const float numBlurPixelsPerSide = 2.0f;const vec2  blurMultiplyVec      = vec2(1.0f, 0.0f);#else// This only exists to get this shader to compile when no macros are definedconst float numBlurPixelsPerSide = 0.0f;const vec2  blurMultiplyVec      = vec2(0.0f, 0.0f);#endifvoid main() {  // Incremental Gaussian Coefficent Calculation (See GPU Gems 3 pp. 877 - 889)  vec3 incrementalGaussian;  incrementalGaussian.x = 1.0f / (sqrt(2.0f * pi) * sigma);  incrementalGaussian.y = exp(-0.5f / (sigma * sigma));  incrementalGaussian.z = incrementalGaussian.y * incrementalGaussian.y;  vec4 avgValue = vec4(0.0f, 0.0f, 0.0f, 0.0f);  float coefficientSum = 0.0f;  // Take the central sample first...  avgValue += texture2D(blurSampler, gl_TexCoord.xy) * incrementalGaussian.x;  coefficientSum += incrementalGaussian.x;  incrementalGaussian.xy *= incrementalGaussian.yz;  // Go through the remaining 8 vertical samples (4 on each side of the center)  for (float i = 1.0f; i <= numBlurPixelsPerSide; i++) {     avgValue += texture2D(blurSampler, gl_TexCoord.xy - i * blurSize *                           blurMultiplyVec) * incrementalGaussian.x;             avgValue += texture2D(blurSampler, gl_TexCoord.xy + i * blurSize *                           blurMultiplyVec) * incrementalGaussian.x;             coefficientSum += 2 * incrementalGaussian.x;    incrementalGaussian.xy *= incrementalGaussian.yz;  }  gl_FragColor = avgValue / coefficientSum;}` 