The stencil test takes place only if there is a stencil buffer. (If there is no stencil buffer, the stencil test always passes.) Stenciling applies a test that compares a reference value with the value stored at a pixel in the stencil buffer. Depending on the result of the test, the value in the stencil buffer is modified. You can choose the particular comparison function used, the reference value, and the modification performed with the glStencilFunc() and glStencilOp() commands. void glStencilFunc(GLenum func, GLint ref, GLuint mask);
Sets the comparison function (func), reference value (ref), and a mask (mask) for use with the stencil test. The reference value is compared to the value in the stencil buffer using the comparison function, but the comparison applies only to those bits where the corresponding bits of the mask are 1. The function can be GL_NEVER, GL_ALWAYS, GL_LESS, GL_LEQUAL, GL_EQUAL, GL_GEQUAL, GL_GREATER, or GL_NOTEQUAL. If it's GL_LESS, for example, then the fragment passes if ref is less than the value in the stencil buffer. If the stencil buffer contains s bitplanes, the low-order s bits of mask are bitwise ANDed with the value in the stencil buffer and with the reference value before the comparison is performed. The masked values are all interpreted as nonnegative values. The stencil test is enabled and disabled by passing GL_STENCIL_TEST to glEnable() and glDisable(). By default, func is GL_ALWAYS, ref is 0, mask is all 1s, and stenciling is disabled.
void glStencilOp(GLenum fail, GLenum zfail, GLenum zpass);
Specifies how the data in the stencil buffer is modified when a fragment passes or fails the stencil test. The three functions fail, zfail, and zpass can be GL_KEEP, GL_ZERO, GL_REPLACE, GL_INCR, GL_DECR, or GL_INVERT. They correspond to keeping the current value, replacing it with zero, replacing it with the reference value, incrementing it, decrementing it, and bitwise-inverting it. The result of the increment and decrement functions is clamped to lie between 0 and the maximum unsigned integer value (2s-1 if the stencil buffer holds s bits). The fail function is applied if the fragment fails the stencil test; if it passes, then zfail is applied if the depth test fails and zpass if the depth test passes, or if no depth test is performed. (See "Depth Test." ) By default, all three stencil operations are GL_KEEP.
You can obtain the values for all six stencil-related parameters by using the query function glGetIntegerv() and one of the values shown in Table 10-3 . You can also determine whether the stencil test is enabled by passing GL_STENCIL_TEST to glIsEnabled().
| Query Value | Meaning |
|---|---|
GL_STENCIL_FUNC | Stencil function |
GL_STENCIL_REF | Stencil reference value |
GL_STENCIL_VALUE_MASK | Stencil mask |
GL_STENCIL_FAIL | Stencil fail action |
GL_STENCIL_PASS_DEPTH_FAIL | Stencil pass and depth buffer fail action |
GL_STENCIL_PASS_DEPTH_PASS | Stencil pass and depth buffer pass action |
Probably the most typical use of the stencil test is to mask out an irregularly shaped region of the screen to prevent drawing from occurring within it (as in the windshield example in "Buffers and Their Uses" ). To do this, fill the stencil mask with 0, and then draw the desired shape in the stencil buffer with 1. You can't draw directly into the stencil buffer, but you can achieve the same result by drawing into the color buffer and choosing a suitable value for the zpass function (such as GL_REPLACE). Whenever drawing occurs, a value is also written into the stencil buffer (in this case, the reference value). To prevent the stencil-buffer drawing from affecting the contents of the color buffer, set the color mask to zero (or GL_FALSE). You might also want to disable writing into the depth buffer.
After you've defined the stencil area, set the reference value to 1, and the comparison function such that the fragment passes if the reference value is equal to the stencil-plane value. During drawing, don't modify the contents of the stencil planes.
Example 10-1 demonstrates how to use the stencil test in this way. Two tori are drawn, with a diamond-shaped cutout in the center of the scene. Within the diamond-shaped stencil mask, a sphere is drawn. In this example, drawing into the stencil buffer takes place only when the window is redrawn, so the color buffer is cleared after the stencil mask has been created.
Example 10-1 : Using the Stencil Test: stencil.c
#include <GL/gl.h>
#include <GL/glu.h>
#include "aux.h"
#define YELLOWMAT 1
#define BLUEMAT 2
void myinit (void)
{
GLfloat yellow_diffuse[] = { 0.7, 0.7, 0.0, 1.0 };
GLfloat yellow_specular[] = { 1.0, 1.0, 1.0, 1.0 };
GLfloat blue_diffuse[] = { 0.1, 0.1, 0.7, 1.0 };
GLfloat blue_specular[] = { 0.1, 1.0, 1.0, 1.0 };
GLfloat position_one[] = { 1.0, 1.0, 1.0, 0.0 };
glNewList(YELLOWMAT, GL_COMPILE);
glMaterialfv(GL_FRONT, GL_DIFFUSE, yellow_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, yellow_specular);
glMaterialf(GL_FRONT, GL_SHININESS, 64.0);
glEndList();
glNewList(BLUEMAT, GL_COMPILE);
glMaterialfv(GL_FRONT, GL_DIFFUSE, blue_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, blue_specular);
glMaterialf(GL_FRONT, GL_SHININESS, 45.0);
glEndList();
glLightfv(GL_LIGHT0, GL_POSITION, position_one);
glEnable(GL_LIGHT0);
glEnable(GL_LIGHTING);
glDepthFunc(GL_LEQUAL);
glEnable(GL_DEPTH_TEST);
glClearStencil(0x0);
glEnable(GL_STENCIL_TEST);
}
void display(void)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
/* draw blue sphere where the stencil is 1 */
glStencilFunc (GL_EQUAL, 0x1, 0x1);
glCallList (BLUEMAT);
auxSolidSphere (0.5);
/* draw the tori where the stencil is not 1 */
glStencilFunc (GL_NOTEQUAL, 0x1, 0x1);
glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP);
glPushMatrix();
glRotatef (45.0, 0.0, 0.0, 1.0);
glRotatef (45.0, 0.0, 1.0, 0.0);
glCallList (YELLOWMAT);
auxSolidTorus (0.275, 0.85);
glPushMatrix();
glRotatef (90.0, 1.0, 0.0, 0.0);
auxSolidTorus (0.275, 0.85);
glPopMatrix();
glPopMatrix();
}
void myReshape(GLsizei w, GLsizei h)
{
glViewport(0, 0, w, h);
glClear(GL_STENCIL_BUFFER_BIT);
/* create a diamond-shaped stencil area */
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-3.0, 3.0, -3.0, 3.0, -1.0, 1.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glStencilFunc (GL_ALWAYS, 0x1, 0x1);
glStencilOp (GL_REPLACE, GL_REPLACE, GL_REPLACE);
glBegin(GL_QUADS);
glVertex3f (-1.0, 0.0, 0.0);
glVertex3f (0.0, 1.0, 0.0);
glVertex3f (1.0, 0.0, 0.0);
glVertex3f (0.0, -1.0, 0.0);
glEnd();
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0, (GLfloat) w/(GLfloat) h, 3.0, 7.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.0, 0.0, -5.0);
}
int main(int argc, char** argv)
{
auxInitDisplayMode (AUX_SINGLE | AUX_RGBA
| AUX_DEPTH | AUX_STENCIL);
auxInitPosition (0, 0, 400, 400);
auxInitWindow (argv[0]);
myinit ();
auxReshapeFunc (myReshape);
auxMainLoop(display);
}The following examples illustrate other uses of the stencil test. See Chapter 13 for additional ideas.
Capping - Suppose you're drawing a closed convex object (or several of them, as long as they don't intersect or enclose each other) made up of several polygons, and you have a clipping plane that may or may not slice off a piece of it. Suppose that if the plane does intersect the object, you want to cap the object with some constant-colored surface, rather than seeing the inside of it. To do this, clear the stencil buffer to 0, and begin drawing with stenciling enabled and the stencil comparison function set to always accept fragments. Invert the value in the stencil planes each time a fragment is accepted. After all the objects are drawn, regions of the screen where no capping is required have 0 in the stencil planes, and regions requiring capping are nonzero. Reset the stencil function so that it draws only where the stencil value is nonzero, and draw a large polygon of the capping color across the entire screen.
Overlapping translucent polygons - Suppose you have a translucent surface that's made up of polygons that overlap slightly. If you simply use alpha blending, portions of the underlying objects are covered by more than one transparent surface, which doesn't look right. Use the stencil planes to make sure that each fragment is covered by at most one portion of the transparent surface. Do this by clearing the stencil planes to zero, drawing only when the stencil plane is zero, and incrementing the value in the stencil plane when you draw.
Stippling - Suppose you want to draw an image with a stipple pattern (see "Displaying Points, Lines, and Polygons" for more information about stippling). You can do this by writing the stipple pattern into the stencil buffer, and then drawing conditionally on the contents of the stencil buffer. After the original stipple pattern is drawn, the stencil buffer aren't altered while drawing the image, so the object gets stippled by the pattern in the stencil planes.
OpenGL Programming Guide