3D Filter Blurring

Blurring a texture in 3D (OpenGL or DirectX) applications is a common way to achieve various effects like glow. Nowadays, blurring is almost always done by pixel shaders. But sometimes applications should also run on older hardware or integrated graphics cards with no shader interface. The idea is to use the bilinear texture filtering feature of the graphics card.

It works as follows:

  1. Bind your Texture which you want to blur to one texture unit.
  2. Now blend the texture four times to a back buffer (framebuffer/texture) without scaling, but with shifted texture coordinates (-0.25px and 0.25px in each direction).
  3. Copy the back buffer to the texture.
  4. Repeat steps 2+3 as often you want. Steps 2+3 are one rendering pass.

Note: Everything is done in orthographic mode. Your texture coordinates have to be clamped and the texture filtering must be set to linear.

I implemented these steps using OpenGL. The results are superb. Here is the C function:

  1. void blur_tex (GLuint tex, int passes)
  2. {
  3.     int i, x, y;
  4.  
  5.     glEnable (GL_BLEND);
  6.     glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  7.     glBindTexture (GL_TEXTURE_2D, tex);
  8.     while (passes > 0) {
  9.         i = 0;
  10.         for (x = 0; x < 2; x++)
  11.         {
  12.             for (y = 0; y < 2; y++, i++)
  13.             {
  14.                 glColor4f (1.0f,1.0f,1.0f,1.0 / (i+1));
  15.                 glBegin (GL_TRIANGLE_STRIP);
  16.                     glTexCoord2f (0 + (x-0.5)/WIDTH, 1 + (y-0.5)/HEIGHT); glVertex2f (0, 0);
  17.                     glTexCoord2f (0 + (x-0.5)/WIDTH, 0 + (y-0.5)/HEIGHT); glVertex2f (0, HEIGHT);
  18.                     glTexCoord2f (1 + (x-0.5)/WIDTH, 1 + (y-0.5)/HEIGHT); glVertex2f (WIDTH, 0);
  19.                     glTexCoord2f (1 + (x-0.5)/WIDTH, 0 + (y-0.5)/HEIGHT); glVertex2f (WIDTH, HEIGHT);
  20.                 glEnd ();
  21.             }
  22.         }
  23.         glCopyTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, WIDTH, HEIGHT, 0);
  24.         passes--;
  25.     }
  26.     glDisable (GL_BLEND);
  27. }

Screenshots

4 times OpenGL blur versus GIMP's Gaussian Blur (r=4)

(Please click on the thumbnails to see larger versions.)

Can you see a difference between OpenGL and GIMP?

Benchmarking

I benchmarked with an NVIDIA GeForce 6800GS and an Athlon XP 2200+ using Linux 2.6.20 and the official NVIDIA drivers.

The texture has the size 512x512 and the rendering code always finishes with glFlush() and glFinish() before taking the times.

benchmark

Calling the function 512 times takes 287ms. This is equal to 0.56ms/pass or 1783fps@1pass.

The resulting image quality is nearly as good as GIMP's gaussian blur and the algorithm is more than fast enough for practical purposes.

Doing some more fun...

If you change the function to

  1. void blur_tex_zoom (GLuint tex, int passes)
  2. {
  3.     int i;
  4.  
  5.     glEnable (GL_BLEND);
  6.     glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  7.     glBindTexture (GL_TEXTURE_2D, tex);
  8.     while (passes > 0) {
  9.         for (i = 0; i < 2; i++)
  10.         {
  11.             glColor4f (1.0f,1.0f,1.0f,1.0 / (i+1));
  12.             glBegin (GL_TRIANGLE_STRIP);
  13.                 glTexCoord2f (0 - (i*0.5)/WIDTH, 1 + (i*0.5)/HEIGHT); glVertex2f (0, 0);
  14.                 glTexCoord2f (0 - (i*0.5)/WIDTH, 0 - (i*0.5)/HEIGHT); glVertex2f (0, HEIGHT);
  15.                 glTexCoord2f (1 + (i*0.5)/WIDTH, 1 + (i*0.5)/HEIGHT); glVertex2f (WIDTH, 0);
  16.                 glTexCoord2f (1 + (i*0.5)/WIDTH, 0 - (i*0.5)/HEIGHT); glVertex2f (WIDTH, HEIGHT);
  17.             glEnd ();
  18.         }
  19.         glCopyTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, WIDTH, HEIGHT, 0);
  20.         passes--;
  21.     }
  22.     glDisable (GL_BLEND);
  23. }

you will get a zoom blur (imagine exploring a starfield...). If you change the function a second time you will get a radial blur:

  1. void blur_tex_radial (GLuint tex, int passes)
  2. {
  3.     int i;
  4.  
  5.     glEnable (GL_BLEND);
  6.     glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  7.     glBindTexture (GL_TEXTURE_2D, tex);
  8.     while (passes > 0) {
  9.         for (i = 0; i < 2; i++)
  10.         {
  11.             glPushMatrix ();
  12.             glLoadIdentity ();
  13.                 if (i == 1)
  14.                 {
  15.                     glTranslatef (WIDTH/2, HEIGHT/2,0);
  16.                     glRotatef (1, 0, 0, 1);
  17.                     glTranslatef (-WIDTH/2, -HEIGHT/2,0);
  18.  
  19.                 }
  20.                 glColor4f (1.0f,1.0f,1.0f,1.0 / (i+1));
  21.                 glBegin (GL_TRIANGLE_STRIP);
  22.                     glTexCoord2f (0.0f, 1.0f); glVertex2f (0, 0);
  23.                     glTexCoord2f (0.0f, 0.0f); glVertex2f (0, HEIGHT);
  24.                     glTexCoord2f (1.0f, 1.0f); glVertex2f (WIDTH, 0);
  25.                     glTexCoord2f (1.0f, 0.0f); glVertex2f (WIDTH, HEIGHT);
  26.                 glEnd ();
  27.             glPopMatrix ();
  28.         }
  29.         glCopyTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, WIDTH, HEIGHT, 0);
  30.         passes--;
  31.     }
  32.     glDisable (GL_BLEND);
  33. }

Everbody likes examples

Here you can see radial and zoom blur.

(Please click on the thumbnails to see larger versions.)

License

You have to accept the following license if you use one of the code snippets.

Copyright (c) 2007 Ulrich Mierendorff

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.