March 4, 2006 12:35 PM
The opengl scissor test1 is used to restrict drawing to a certain part of the window. This is especially useful if you're drawing a settings or a dialog window over your current opengl rendering and you don't want to unnecessarily redraw the entire screen and then draw the settings window over it blocking most of the things you spent time rendering.

The Problem

In theory, this sounds perfect, if you want to continuously update only a part of the screen, you use the scissor test. But there's one problem that an opengl newbie (like me) can run into. Parts of the screen that you are not redrawing because you've enabled the scissor test might start flickering a lot. Especially if there was a lot of change between the previous frame and the current frame.

This flickering is due to double buffering. Normally, to avoid flickering, you draw in the back buffer, then swap the front and the back buffer. Even when you use the scissor test and update only a small part of the entire screen, you swap the front and the back buffer. The problem with this is that the back buffer might have a different version of the area outside the scissor area than the front buffer. The scissor area is continuously redrawn and hence isn't affected by this problem.

The Solution

The solution to this problem is simple: before you start redrawing only the scissor area, you must ensure that the front and back buffer have the same rendering. That's not very hard to do. There are two ways:

  1. You can draw to both the front and back buffer before calling glScissor() using glDrawBuffer(GL_FRONT_AND_BACK). The problem with this is that drawing to the front buffer causes tearing artifacts on the screen to appear defeating the purpose of double buffering in the first place.
  2. You can simply copy the front buffer to the back buffer. Here's the code to do that:
void copy_buffer()
{
  static GLint viewport[4];
  static GLfloat raster_pos[4];

  glGetIntegerv(GL_VIEWPORT, viewport);

  /* set source buffer */
  glReadBuffer(GL_FRONT);

  /* set projection matrix */
  glMatrixMode(GL_PROJECTION);
  glPushMatrix();
  glLoadIdentity() ;
  gluOrtho2D(0, viewport[2], 0, viewport[3]);

  /* set modelview matrix */
  glMatrixMode(GL_MODELVIEW);
  glPushMatrix();
  glLoadIdentity();

  /* save old raster position */
  glGetFloatv(GL_CURRENT_RASTER_POSITION, raster_pos);

  /* set raster position */
  glRasterPos4f(0.0, 0.0, 0.0, 1.0);

  /* copy buffer */
  glCopyPixels(0, 0, viewport[2], viewport[3], GL_COLOR);

  /* restore old raster position */
  glRasterPos4fv(raster_pos);

  /* restore old matrices */
  glPopMatrix();
  glMatrixMode(GL_PROJECTION);
  glPopMatrix();
  glMatrixMode(GL_MODELVIEW);

  /* restore source buffer */
  glReadBuffer(GL_BACK); 
} 

If you change the default drawing buffer or the default logic operation, you'd want to save and restore those as well.

CategoryProgramming


[1] glScissor(GLint, GLint, GLsizei, GLsizei) does all the magic.

Copyright © 2004-2011 Anirudh Sasikumar. All rights reserved.
Last Updated: March 4, 2006 1:21 PM