Open GL Super Bible

Previous Table of Contents Next


The Accumulation Buffer

The accumulation buffer provides support for many special effects such as motion blur and depth of field. It also supports full-screen anti-aliasing, although other methods (such as multisampling) are better suited to this task.

The accumulation buffer is considerably less complex than the other buffers discussed so far. It has a single function, glAccum, that manages all accumulation buffer actions. The actions that can be performed are in Table 15-9.

Table 15-9 Accumulation Operations

Operation Description

GL_ACCUM Add scaled color-buffer values to the accumulation buffer.
GL_LOAD Load scaled color-buffer values into the accumulation buffer, replacing whatever had been there before.
GL_ADD Add a constant color to the accumulation buffer’s values.
GL_MULT Multiply color values in the accumulation buffer by a constant color (filtering effects).
GL_RETURN Copy the accumulation buffer into the main color buffer.

The normal way you use the accumulation buffer is to render multiple views into it and display the final composite scene with glAccum(GL_RETURN, 1.0).

Using the Accumulation Buffer for Motion Blur

As a coworker of ours once said, “It’s easy to make any application of the accumulation buffer look like motion blur!” The problem is akin to what happens when your hands shake as you take a picture with a camera—too much jitter will blur the image.

You’ll find that rendering motion blur is a little more complicated than just drawing a sequence of frames with the camera moving between each frame. We perceive motion blur when an object moves faster than our eyes can track it. In essence, the picture changes as the brain is “processing” the image, but the focus on the moving target is never lost. In a camera, light entering the lens exposes the film for a finite amount of time. Depending on the camera and photographer, the amount of blur seen may be small around the edges, or it could streak across the image.

When you simulate motion blur with computer graphics, it is important to remember that the current (or final) position of the object you are blurring must look more solid (or focused) than the rest of the frames. The easiest way to accomplish this is to use a larger color scaling factor when accumulating the current frame so that more of the color values from the final frame used will stand out from the rest. A typical implementation looks something like this:

/* Draw the current frame */
draw_frame(0);
/* Load the accumulation buffer with 50% of the current frame */
glAccum(GL_LOAD, 0.5);

/* Draw the last 10 frames and accumulate 5% for each */
for (i = 1; i <= 10; i ++)
{
  draw_frame(-i);
  glAccum(GL_ACCUM, 0.05);
};

/* Display the final scene */
glAccum(GL_RETURN, 1.0);

Notice that you don’t have to use glClear to initialize the accumulation buffer contents, as you do with the color, depth, and stencil buffers. Instead, most often you’ll use glAccum(GL_LOAD, s) on the first frame of the scene. The program in Listing 15-4 demonstrates motion blur on the cube and sphere.

Listing 15-4 MOTION.C: Motion blur using the accumulation buffer

/*
 * "motion.c" - A test program demonstrating the use of glAccum() for
 *              motion blur.
 */

#include <GL/glaux.h>

/*
 * These #define constants are provided for compatibility between MS
 * Windows and the rest of the world.
 *
 * CALLBACK and APIENTRY are function modifiers under MS Windows.
 */

#ifndef WIN32
#  define CALLBACK
#  define APIENTRY
#endif /* !WIN32 */

GLfloat rotation = 0.0;

/*
 * 'reshape_scene()' - Change the size of the scene...
 */

void CALLBACK
reshape_scene(GLsizei width,    /* I - Width of the window in pixels */
              GLsizei height)   /* I - Height of the window in pixels */
{
 /*
  * Reset the current viewport and perspective transformation...
  */

  glViewport(0, 0, width, height);

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPerspective(22.5, (float)width / (float)height, 0.1, 1000.0);

  glMatrixMode(GL_MODELVIEW);
}

/*
 * 'draw_scene()' - Draw a scene containing a cube with a sphere in
 *                  front of it.
 */

void CALLBACK
draw_scene(void)
{
  GLfloat       frame;
  static float   red_light[4] = { 1.0, 0.0, 0.0, 1.0 };
  static float   red_pos[4] = { 1.0, 1.0, 1.0, 0.0 };
  static float
   blue_light[4] = { 0.0, 0.0, 1.0, 1.0 };
  static float   blue_pos[4] = { -1.0, -1.0, -1.0, 0.0 };

 /*
  * Enable drawing features that we need...
  */

  glEnable(GL_DEPTH_TEST);
  glEnable(GL_LIGHTING);
  glEnable(GL_LIGHT0);
  glEnable(GL_LIGHT1);

  glShadeModel(GL_SMOOTH);

 /*
  * Clear the color and depth buffers...
  */

  glClearColor(0.0, 0.0, 0.0, 0.0);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

 /*
  * Draw the cube and sphere in different colors...
  *
  * We have positioned two lights in this scene.  The first is red and
  * located above, to the right, and behind the viewer.  The second
  * is blue and located below, to the left, and in front of the viewer.
  */

  glLightfv(GL_LIGHT0, GL_DIFFUSE, red_light);
  glLightfv(GL_LIGHT0, GL_POSITION, red_pos);

  glLightfv(GL_LIGHT1, GL_DIFFUSE, blue_light);
  glLightfv(GL_LIGHT1, GL_POSITION, blue_pos);

 /*
  * Draw the objects 11 times starting at the current rotation...
  */

  for (frame = 0.0; frame <= 11.0; frame ++)
  {
    glPushMatrix();
      glTranslatef(0.0, 0.0, -20.0);
      glRotatef(rotation - frame, 0.0, 1.0, 0.0);

      glPushMatrix();
        glTranslatef(-1.0, 0.0, 0.0);
        auxSolidSphere(1.0);
      glPopMatrix();

      glPushMatrix();
        glTranslatef(1.0, 0.0, 0.0);
        glRotatef(15.0, 0.0, 1.0, 0.0);
        glRotatef(15.0, 0.0, 0.0, 1.0);
        auxSolidCube(2.0);
      glPopMatrix();
    glPopMatrix();

   /*
    * Accumulate 50% the first time, 5% every other time...
    */

    if (frame == 0.0)
      glAccum(GL_LOAD, 0.5);
    else
      glAccum(GL_ACCUM, 0.05);
  };

 /*
  * Copy the accumulated results back to the color buffer...
  */

  glAccum(GL_RETURN, 1.0);

  auxSwapBuffers();
}

/*
 * 'rotate_objects()' - Rotate while we are idle...
 */

void CALLBACK
rotate_objects(void)
{
  rotation += 2.0;
  if (rotation >= 360.0)
    rotation -= 360.0;

  draw_scene();
}

/*
 * 'main()' - Initialize the window and display the scene until the user
 *            presses the ESCape key.
 */

int APIENTRY
WinMain(HINSTANCE hInstance,
        HINSTANCE hPrev,
        LPSTR     lpCmdLine,
        int       nCmdShow)
{
  auxInitDisplayMode(AUX_RGB | AUX_DOUBLE | AUX_DEPTH | AUX_ACCUM);
  auxInitWindow("Motion Blur");

  auxReshapeFunc(reshape_scene);
  auxIdleFunc(rotate_objects);

  auxMainLoop(draw_scene);
}

/*
 * End of "motion.c".
 */


Previous Table of Contents Next