Open GL Super Bible

Previous Table of Contents Next


Drawing into the Stencil Buffer

Once you have enabled the GL_STENCIL_TEST attribute with glEnable, you’ll still need to set up how the stencil buffer operates. By default, it does nothing, allowing drawing to occur anywhere on the screen without updating the stencil buffer. To make stenciling work effectively, however, we need to put values into the stencil buffer. The glStencilFunc and glStencilOp functions handle this interaction.

The glStencilFunc function defines a comparison function, reference value, and mask for all stencil buffer operations. The valid functions are in Table 15-7.

Table 15-7 Stenciling Functions

Function Description

GL_NEVER The stencil test always fails (no drawing occurs).
GL_LESS Passes if the reference value is less than the stencil value.
GL_LEQUAL Passes if the reference value is less than or equal to the stencil value.
GL_GREATER Passes if the reference value is greater than the stencil value.
GL_GEQUAL Passes if the reference value is greater than or equal to the stencil value.
GL_EQUAL Passes if the reference value is equal to the stencil value.
GL_NOTEQUAL Passes if the reference value is not equal to the stencil value.
GL_ALWAYS The default; stencil test always passes (drawing always occurs).

Coupled with the stencil function is the stencil operation, defined with glStencilOp. Valid operations are in Table 15-8.

Table 15-8 Stenciling Operations

Operation Description

GL_KEEP Keep the current stencil buffer contents.
GL_ZERO Set the stencil buffer value to 0.
GL_REPLACE Set the stencil buffer value to the function reference value.
GL_INCR Increment the current stencil buffer value.
GL_DECR Decrement the current stencil buffer value.
GL_INVERT Bitwise invert the current stencil buffer value.

Normally a mask image is used to outline the area in which drawing is to take place. Here is an example of drawing a mask image into the stencil buffer:

glStencilFunc(GL_ALWAYS, 1, 1);
glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);

Then you would issue drawing commands that store a value of 1 in the stencil buffer. To draw using the stencil buffer mask, do the following prior to drawing the scene:

glStencilFunc(GL_EQUAL, 1, 1);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);

Because this operates with all OpenGL drawing functions including glBitmap, you can use the stencil buffer to create many special “hole” effects for animations! Listing 15-3 contains a version of DEPTHCUT.C called STENCILCT.C that uses the stencil buffer instead of the depth buffer to cut away the middle of the cube.

Following is the heart of this program, which uses the functions described above:

glStencilFunc(GL_ALWAYS, 1, 1);
glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);

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

Once the stencil image is drawn, we draw the cube wherever the sphere was not drawn:

glStencilFunc(GL_NOTEQUAL, 1, 1);    /* Draw where sphere isn’t */
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);

...

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

Listing 15-3 STENCILCT.C, a stencil buffer example

/*
 * "stencilct.c" - A test program demonstrating the use of glStencilFunc()
 *                 and glStencilOp() to cut away the middle of a cube.
 */

#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 */

/*
 * '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)
{
  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_STENCIL_TEST);
  glEnable(GL_LIGHTING);
  glEnable(GL_LIGHT0);
  glEnable(GL_LIGHT1);

  glShadeModel(GL_SMOOTH);

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

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

 /*
  * Draw the sphere that will be cutting away parts of the cube...
  */

  glStencilFunc(GL_ALWAYS, 1, 1);
  glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);

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

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

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

 /*
  * Draw the cube...
  *
  * 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.
  */

  glStencilFunc(GL_NOTEQUAL, 1, 1);
  glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);

  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);

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

  auxSwapBuffers();
}

/*
 * '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_STENCIL);
  auxInitWindow("Stenciling");

  auxReshapeFunc(reshape_scene);
  auxMainLoop(draw_scene);
}

/*
 * End of "stencilct.c".
 */


Previous Table of Contents Next