Presentation on theme: "Buffers © Jeff Parker, 2013."— Presentation transcript:

Objectives Introduce additional OpenGL buffers Buffers in General
Alpha Blending Accumulation Buffers Stencil Buffers We may not get through all the material tonight Hope to give you a taste of what you can do

Buffer Define a buffer by Spatial resolution (n x m) and its Depth (or precision) k, the number of bits/pixel Purpose pixel Angel: Interactive Computer Graphics 4E © Addison-Wesley 2004

OpenGL Frame Buffer We have seen the first three buffers below We will focus on two more tonight Accumulation Buffer Accumulate – add up Stencil Buffer Single out - mark Angel: Interactive Computer Graphics 4E © Addison-Wesley 2004

Writing in Buffers Conceptually, we can consider all of memory as a large two-dimensional array of pixels We read and write rectangular block of pixels Bit block transfer (bitblt) operations The frame buffer is part of this memory memory source frame buffer (destination) writing into frame buffer Angel: Interactive Computer Graphics 4E © Addison-Wesley 2004

Bit Writing Modes Source and destination bits are combined bitwise 16 possible functions (one per column in table) XOR replace OR Angel: Interactive Computer Graphics 4E © Addison-Wesley 2004

XOR mode Recall from Chapter 3 that we can use XOR by enabling logic operations and selecting the XOR write mode XOR is especially useful for swapping blocks of memory such as menus that are stored off screen If S represents screen and M represents a menu the sequence S  S  M M  S  M swaps the S and M Angel: Interactive Computer Graphics 4E © Addison-Wesley 2004

The Pixel Pipeline OpenGL has a separate pipeline for pixels Writing pixels involves Moving pixels from processor memory to the frame buffer Format conversions Mapping, Lookups, Tests Reading pixels Format conversion Angel: Interactive Computer Graphics 4E © Addison-Wesley 2004

Buffer Selection OpenGL can draw into or read from any of the color buffers (front, back, auxiliary) Default to the back buffer Change with glDrawBuffer and glReadBuffer Note that format of the pixels in the frame buffer is different from that of processor memory and these two types of memory reside in different places Need packing and unpacking Drawing and reading can be slow Angel: Interactive Computer Graphics 4E © Addison-Wesley 2004

Examples Examples that write to frame buffer Mandelbrot RayTracer
void display() { glClear(GL_COLOR_BUFFER_BIT); glDrawPixels(n,m,GL_COLOR_INDEX, GL_UNSIGNED_BYTE, image); glFlush(); }

Bitmaps OpenGL treats 1-bit pixels (bitmaps) differently from multi-bit pixels (pixelmaps) Bitmaps are masks that determine if the corresponding pixel in the frame buffer is drawn with the present raster color 0  color unchanged 1  color changed based on writing mode Bitmaps are useful for raster text GLUT font: GLUT_BIT_MAP_8_BY_13 OpenGL maintains a Raster Position, where new character will be placed Angel: Interactive Computer Graphics 4E © Addison-Wesley 2004

An Interactive Introduction to OpenGL Programming
Alpha Blending

An Interactive Introduction to OpenGL Programming
Alpha Blending It is possible to blend colors together The gives the appearance of translucent materials We blend the current destination with the source

An Interactive Introduction to OpenGL Programming
Alpha Test It is possible to blend colors together The gives the appearance of translucent materials We blend the current destination with the source Can also use alpha levels to reject pixels

An Interactive Introduction to OpenGL Programming
Alpha Blending It is possible to blend colors together The gives the appearance of translucent materials We blend the current destination with the source The results are order dependant glEnable(GL_BLEND) glBlendFunc(source_factor, destination_factor)

An Interactive Introduction to OpenGL Programming
Alpha Blending Two routines - Both use the alpha channel static void drawLeftTriangle(void) { /* draw yellow triangle on LHS of screen */ glBegin (GL_TRIANGLES); glColor4f(1.0, 1.0, 0.0, 0.75); glVertex3f(0.1, 0.9, 0.0); glVertex3f(0.1, 0.1, 0.0); glVertex3f(0.7, 0.5, 0.0); glEnd(); }

An Interactive Introduction to OpenGL Programming
Which first? void display(void) { glClear(GL_COLOR_BUFFER_BIT); if (leftFirst) { drawLeftTriangle(); drawRightTriangle(); } else { glFlush();

Issue: Average in Place
An Interactive Introduction to OpenGL Programming Issue: Average in Place We want to average three numbers: 1, 1, and 97. If average 1 and 97, we get 49 If we average 1 and 49, we get 25 However, if we average 1's first, and then average in the 97, we get 49 Right answer is ( )/3 = 33 In the first case, we divide 97 by 4: in the second by 2. Want to divide by 3.

Solution: Accumulation Buffer
We may want to combine multiple pixels to arrive at a result Wish to avoid blending, as it is order dependant We would rather compute a true average. But if I average the values of 4 pixels, I run the risk of overflow I could divide before addition, but then I loose precision The Accumulation Buffer is designed for such purposes Stores a floating point number for each color value in pixel buffer Protocol Clear the buffer Add to it with a scaling factor Copy results back to frame buffer

Applications of Accumulation Buffer
We can combine images Or perform image processing on image Some applications Motion blur Antialiasing Depth of Field Soft Shadows Next week

Motion Blur SIGGRAPH 1996 motionblur.c Render the ball multiple times, moving the ball between passes

Using Accumulation Buffer
void redraw(void) {... switch(rendermode) { case NONE: render(0.f, 0.f, 0.f); break; case FIELD:... glClear(GL_ACCUM_BUFFER_BIT); for (i = 0; i < max; i++) { render(dx * i, dy * i, dz * i); glAccum(GL_ACCUM, 1.f/max); } glAccum(GL_RETURN, 1.f); Clear buffer Add into accumulation buffer Copy result to frame buffer

Render void render(GLfloat dx, GLfloat dy, GLfloat dz) { ... glPushMatrix(); glTranslatef(-80.f + dx, -60.f + dy, -420.f + dz); glCallList(SPHERE); glPopMatrix(); glTranslatef(-20.f, -80.f, -600.f); glCallList(CONE); Note that we waste time reimaging everything, when only the sphere moves

Convolutions It is common in image processing to use convolutions We can use a convolution to find edges, as with the Sobel filter below

Convolutions We can view the game of life as an application of convolution Count the neighbors of a pixel

Convolutions SIGGRAPH 1996 convolve.c This is a version of the cone and sphere picture after applying the Sobel edge detector We are looking for edges as x increases { -.5f, 0.f, .5f, -1.f, 0.f, 1.f, -.5f, 0.f, .5f}; Note that we waste time reimaging everything, when only the sphere moves

Convolutions void redraw(void) { glClearAccum(curmat->bias, curmat->bias, curmat->bias, 1.0); glClear(GL_ACCUM_BUFFER_BIT); convolve(render, curmat); glViewport(0, 0, winWidth, winHeight); /* !?! */ glAccum(GL_RETURN, curmat->scale); glutSwapBuffers(); }

Convolutions void convolve(void (*draw)(void), Filter *mat) { ... for(j = 0; j < jmax; j++) { for(i = 0; i < imax; i++) { glViewport(-i, -j, winWidth - i, winHeight - j); draw(); glAccum(GL_ACCUM, mat->array[i + j * imax]); }

Filters /* Filter contents and size */ typedef struct { GLfloat scale; /* 1/scale applied to image */ GLfloat bias; /* for biasing images */ int rows; int cols; GLfloat *array; } Filter;

Sobel Filter void sobel(Filter *mat) { static GLfloat sobel[] = {-.5f, 0.f, .5f, -1.f, 0.f, 1.f, -.5f, 0.f, .5f}; resize(mat, 3, 3); memcpy(mat->array, sobel, sizeof(sobel)); mat->scale = 2.f; mat->bias = 0.f; } We usually use Sobel filters in pairs This version detects vertical edges

Resize /* doesn't re-initialize matrix */ void resize(Filter *mat, int rows, int cols) { if (mat->rows != rows || mat->cols != cols) { free(mat->array); mat->array = (GLfloat *)realloc(mat->array, rows * cols * sizeof(GLfloat)); } mat->rows = rows; mat->cols = cols;

Extra Free! /* doesn't re-initialize matrix */ void resize(Filter *mat, int rows, int cols) { if (mat->rows != rows || mat->cols != cols) { free(mat->array); /* 1) Already done by realloc 2) Free before using? Ouch! */ mat->array = (GLfloat *)realloc(mat->array, rows * cols * sizeof(GLfloat)); } mat->rows = rows; mat->cols = cols;

realloc The realloc() function tries to change the size of the allocation pointed to by ptr to size, and returns ptr. If there is not enough room to enlarge the memory allocation pointed to by ptr, realloc() creates a new allocation, copies as much of the old data pointed to by ptr as will fit to the new allocation, frees the old allocation, and returns a pointer to the allocated memory.

Depth of Field SIGGRAPH 1996 field.c How can we get the effect of the depth of field?

Image for(j = min; j < max; j++) { for(i = min; i < max; i++) { dx = scale * i * FRUSTNEAR/focus; dy = scale * j * FRUSTNEAR/focus; glMatrixMode(GL_PROJECTION); glLoadIdentity(); glFrustum(-FRUSTDIM + dx, FRUSTDIM + dx, -FRUSTDIM + dy, FRUSTDIM + dy, FRUSTNEAR, FRUSTFAR); glMatrixMode(GL_MODELVIEW); glTranslatef(scale * i, scale * j, 0.f); render(); glAccum(GL_ACCUM, 1.f/count); } glAccum(GL_RETURN, 1.f);

Jitter What is happening? We tweak the eye point and the direction the eye is looking For items at the right distance (focal plane) there is no movement

Fragment Testing Not every fragment becomes a pixel. Some make it to frame buffer, but are overwritten Some never make it to the frame buffer For a fragment to make it to the frame buffer, it must pass a number of tests The tests that a fragment must pass include: scissor test - an additional clipping test alpha test - a filtering test based on the alpha color component stencil test - a pixel mask test depth test - fragment occlusion test Each controlled by glEnable() capability. If a fragment passes all enabled tests, it is then blended, dithered and/or logically combined with pixels in the framebuffer. Angel: Interactive Computer Graphics 4E © Addison-Wesley 2004

Scissor Test Useful to create viewports Define a rectangle in screen space, enable Scissor Test Pass 1: Clip every pixel that doesn't fit in the rectangle Pass 2: Set up a different rectangle, and re-image Angel: Interactive Computer Graphics 4E © Addison-Wesley 2004

Updating Buffers For some of these tests, there is no change to state scissor test - an additional clipping test alpha test - a filtering test based on the alpha color component stencil test - a pixel mask test May update stencil depth test - fragment occlusion test May update the depth buffer The Stencil Test is the much more complex than depth test Tied to the depth test, which follows it

Cueva de las Manos, Santa Cruz, Argentina: ~7300 BC
Stencil Test Cueva de las Manos, Santa Cruz, Argentina: ~7300 BC

An Interactive Introduction to OpenGL Programming
Stencil Test Control drawing based on values in the stencil buffer Fragments that fail the stencil test are not drawn Example: create a mask and only draw objects not in mask area Region can be more complex than for the Scissors test Unlike other buffers, we do not draw into the stencil buffer. We set its values with the stencil functions. However, the rendering can alter the values in the stencil buffer depending on whether a fragment passes or fails the stencil test.

Applications of Stencil Test
An Interactive Introduction to OpenGL Programming Applications of Stencil Test Next week we will talk about making shadows, and we will revisit the stencil buffer

An Interactive Introduction to OpenGL Programming
Stencil Test I understand the stencil test for brief periods around the presentation of this class Everything I know about it I learned from Mark Kilgard's "Improving Shadows and Reflections via the Stencil Buffer" We will start with a simple example

Redbook stenciltest.c Unlike other buffers, we do not draw into the stencil buffer. We set its values with the stencil functions. However, rendering can alter the values in the stencil buffer depending on whether a fragment passes or fails the stencil test. Implementations are required to support at least one bit of stencil. Today, most support 8 bits or more. This example uses 1 bit and distinguishes four areas: background, red, green, and blue

Initialize glClearColor(0.0, 0.0, 0.0, 0.0); glClearStencil(0); glStencilMask(1); // Now we can only write LSBit glEnable(GL_STENCIL_TEST); // But not Depth Test glClear(GL_COLOR_BUFFER_BIT|GL_STENCIL_BUFFER_BIT); // ? Didn’t we do this with glClearStencil(0)?

Initialize glClearColor(0.0, 0.0, 0.0, 0.0); glClearStencil(0); glStencilMask(1); // Now we can only write LSBit glEnable(GL_STENCIL_TEST); // But not Depth Test glClear(GL_COLOR_BUFFER_BIT|GL_STENCIL_BUFFER_BIT); // ? Didn’t we do this with glClearStencil(0)? The glClear() function sets the bitplane area of the window to values previously set by glClearStencil() ... glClearStencil() defines what 'clear' means.

Draw static void Draw(void) { glClear(GL_COLOR_BUFFER_BIT|GL_STENCIL_BUFFER_BIT); glStencilFunc(GL_ALWAYS, 1, 1); glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); glColor3ub(200, 0, 0); glBegin(GL_POLYGON); glVertex3i(-4, -4, 0); glVertex3i( 4, -4, 0); glVertex3i( 0, 4, 0); glEnd();

First Layer glStencilFunc(GL_ALWAYS, 1, 1); glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); Values in the stencil buffer after drawing red triangle

glStencilFunc() glStencilFunc(GL_ALWAYS, 1, 1); glStencilFunc(GLenum func, GLint ref, GLuint mask); func - comparison function, The function can be GL_NEVER, GL_ALWAYS, ..GL_EQUAL,… ref - The reference value for the stencil test. The ref parameter is clamped to the range [0, 2^n-1], where n is the number of bitplanes in the stencil buffer. mask – A mask that is ANDed with both the reference value and the stored stencil value when the test is done. By default, func is GL_ALWAYS, ref is 0, mask is all 1s, and stenciling is disabled.

Translation glStencilFunc(GL_ALWAYS, 1, 1); glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); void glStencilFunc(GLenum func, GLint ref, GLuint mask); void glStencilOp( GLenum fail, GLenum zfail, GLenum zpass); fail: The action to take when the stencil test fails. GL_KEEP Keeps the current value. zfail: Stencil action when the stencil test passes, but the depth test fails. zpass: Stencil action when both the stencil test and the depth test pass, or when the stencil test passes and either there is no depth buffer or depth testing is not enabled. GL_REPLACE Set stencil buffer value to ref

First Layer glStencilFunc(GL_ALWAYS, 1, 1); glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); Values in the stencil buffer after drawing red triangle

Second Layer static void Draw(void) { ... glStencilFunc(GL_EQUAL, 1, 1); glStencilOp(GL_INCR, GL_KEEP, GL_DECR); glColor3ub(0, 200, 0); glBegin(GL_POLYGON); glVertex3i(3, 3, 0); glVertex3i(-3, 3, 0); glVertex3i(-3, -3, 0); glVertex3i(3, -3, 0); glEnd(); Note that depth test has not been enabled

Painting in Green What does the stencil buffer hold now?
glStencilFunc(GL_EQUAL, 1, 1); glStencilOp(GL_INCR, GL_KEEP, GL_DECR); There are two effects: Color (or not) and Set stencil buffer (or not) Note that depth test is not enabled What does the stencil buffer hold now? Let's review what these calls mean… 54

Translation This is a one bit program
glStencilFunc(GL_EQUAL, 1, 1); glStencilOp(GL_INCR, GL_KEEP, GL_DECR); void glStencilFunc(GLenum func, GLint ref, GLuint mask); void glStencilOp( GLenum fail, GLenum zfail, GLenum zpass); fail: The action to take when the stencil test fails. GL_INCR Increments the current stencil buffer value zfail: Stencil action when the stencil test passes, but the depth test fails. GL_KEEP Keeps the current value. zpass: Stencil action when both the stencil test and the depth test pass, or when the stencil test passes and either there is no depth buffer or depth testing is not enabled. GL_DECR Decrements the current stencil buffer value.

Painting in Green What does the stencil buffer hold now?
glStencilFunc(GL_EQUAL, 1, 1); glStencilOp(GL_INCR, GL_KEEP, GL_DECR); What does the stencil buffer hold now? 56

Painting in Green glStencilFunc(GL_EQUAL, 1, 1); glStencilOp(GL_INCR, GL_KEEP, GL_DECR); 57

Last Layer static void Draw(void) { ... glStencilFunc(GL_EQUAL, 1, 1); glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); glColor3ub(0, 0, 200); glBegin(GL_POLYGON); glVertex3i(3, 3, 0); glVertex3i(-3, 3, 0); glVertex3i(-3, -3, 0); glVertex3i(3, -3, 0); glEnd(); Let's review what these calls mean…

Translation glStencilFunc(GL_EQUAL, 1, 1); glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); void glStencilFunc(GLenum func, GLint ref, GLuint mask); void glStencilOp( GLenum fail, GLenum zfail, GLenum zpass); fail: The action to take when the stencil test fails. GL_KEEP Keeps the current value. zfail: Stencil action when the stencil test passes, but the depth test fails. zpass: Stencil action when both the stencil test and the depth test pass, or when the stencil test passes and either there is no depth buffer or depth testing is not enabled.

Painting in Blue What does the stencil buffer hold now?
glStencilFunc(GL_EQUAL, 1, 1); glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); What does the stencil buffer hold now? 60

Painting in Blue glStencilFunc(GL_EQUAL, 1, 1); glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); Just what it held before...

Stencil vs Depth Buffers
The stencil buffer is the same size as the frame buffer, but is not displayed. Like the depth buffer The stencil buffer holds unsigned numbers (Usually at least 8 bits) The depth buffer holds real numbers in the range 0.0 – 1.0 We use the stencil buffer to tag pixel positions. Based on the tags and a comparison function, we accept or reject a pixel, and update stencil value. The depth buffer compares new value to old, and replaces old when the new value is closer to viewer We can use the stencil buffer like an array of counters, and manipulate the values using different masks, or treat it as one number The depth buffer holds a single floating point number

Stencil Test Protocol Summary
glutInitDisplayMode (... | GLUT_STENCIL); glClearStencil(0); glEnable(GL_STENCIL_TEST); glClear(...|GL_STENCIL_BUFFER_BIT); glStencilFunc (GL_EQUAL, // Comparison 0x1, // Reference value 0x1); // Mask Mask is applied to stencil pixels and reference value before comparison glStencilOp (GL_KEEP, // stencil fail GL_INCR, // stencil pass, depth fail GL_DECR); // stencil pass, depth pass /* draw things */

glStencilFunc() glStencilFunc(GL_ALWAYS, 1, 1); glStencilFunc(GLenum func, GLint ref, GLuint mask); func - comparison function, The function can be GL_NEVER, GL_ALWAYS, ..GL_EQUAL,… ref - The reference value for the stencil test. The ref parameter is clamped to the range [0, 2^n-1], where n is the number of bitplanes in the stencil buffer. mask – A mask that is ANDed with both the reference value and the stored stencil value when the test is done. By default, func is GL_ALWAYS, ref is 0, mask is all 1s, and stenciling is disabled.

glStencilOp() glStencilOp(GL_INCR, GL_KEEP, GL_DECR);
void glStencilOp( GLenum fail, GLenum zfail, GLenum zpass); GL_KEEP Keeps the current value. GL_ZERO Sets the stencil buffer value to zero. GL_REPLACE Sets the stencil buffer value to ref, as specified by glStencilFunc. GL_INCR Increments the current stencil buffer value. Clamps to the maximum representable unsigned value. GL_DECR Decrements the current stencil buffer value. Clamps to zero. GL_INVERT Bitwise inverts the current stencil buffer value. fail: The action to take when the stencil test fails. zfail: Stencil action when the stencil test passes, but the depth test fails. Accepts the same symbolic constants as fail. zpass: Stencil action when both the stencil test and the depth test pass, or when the stencil test passes and either there is no depth buffer or depth testing is not enabled. Accepts the same symbolic constants as fail.

Drawing Concave Polygon
OpenGL does not support drawing concave polygons directly. Consider the following example: (thanks, Jeff dB)

Drawing Concave Polygon
Draw each triangle, with stencil operation set to invert bits Points covered an even number of times hold 0 Points coveran odd number of times hold 1 Color using stencil as guide Could also count and use least significant bit

SIGGRAPH 1997 decal.c Z-Fighting when drawing co-planar objects
Using Stencil Buffer There are 4 co-planar objects: the ground (red) the asphalt, the yellow stripes, and the plane's shadow

Decal void setupDecalState(int decalNum) { if (useStencil) {
glDisable(GL_DEPTH_TEST); glDepthMask(0); glEnable(GL_STENCIL_TEST); glStencilFunc(GL_GREATER, decalNum, 0xff); glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); }

Translation glStencilFunc(GL_GREATER, decalNum, 0xff); glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); void glStencilFunc(GLenum func, GLint ref, GLuint mask); void glStencilOp( GLenum fail, GLenum zfail, GLenum zpass); fail: The action to take when the stencil test fails. GL_KEEP Keeps the current value. zfail: Stencil action when the stencil test passes, but the depth test fails. zpass: Stencil action when both the stencil test and the depth test pass, or when the stencil test passes and either there is no depth buffer or depth testing is not enabled. GL_REPLACE Replace with decalNum

Draw drawAirplane(); … setupBasePolygonState(3); /* 2 decals */
drawGround(); /* decal # 1 = the runway asphalt */ setupDecalState(1); drawAsphalt(); /* decal # 2 = yellow paint on the runway */ setupDecalState(2); drawStripes(); /* decal # 3 = the plane's shadow */ setupDecalState(3); ...

Decal void setupBasePolygonState(int maxDecal) {
The following is only used for the Red Base void setupBasePolygonState(int maxDecal) { glEnable(GL_DEPTH_TEST); if (useStencil) { glEnable(GL_STENCIL_TEST); glStencilFunc(GL_ALWAYS, maxDecal + 1, 0xff); glStencilOp(GL_KEEP, GL_REPLACE, GL_ZERO); } 72

Translation glStencilFunc(GL_ALWAYS, maxDecal + 1, 0xff); glStencilOp(GL_KEEP, GL_REPLACE, GL_ZERO); void glStencilFunc(GLenum func, GLint ref, GLuint mask); void glStencilOp( GLenum fail, GLenum zfail, GLenum zpass); fail: The action to take when the stencil test fails. GL_KEEP Keep the current value. zfail: Stencil action when the stencil test passes, but the depth test fails. GL_REPLACE Replace with maxDecal + 1 zpass: Stencil action when both the stencil test and the depth test pass, or when the stencil test passes and either there is no depth buffer or depth testing is not enabled. GL_ZERO Set to zero

Draw Stencil state as colors
doneWithFrame: ... switch(dataChoice) { case COLOR: break; /* color already in back buffer */ case STENCIL: copyStencilToColor(GL_BACK); break; case DEPTH: copyDepthToColor(GL_BACK); } glutSwapBuffers();

Array of colors These colors are used to map stencil values to figure unsigned char colors[][3] = { {255, 0, 0}, /* red */ {255, 218, 0}, /* yellow */ {72, 255, 0}, /* yellowish green */ {0, 255, 145}, /* bluish cyan */ {0, 145, 255}, /* cyanish blue */ {72, 0, 255}, /* purplish blue */ {255, 0, 218}, /* reddish purple */ }; 75

SIGGRAPH 1997 dissolve.c 76

Create the Eraser mask // Initialize an array of bits void makeEraser(void) { ... for(y = 0; y < eraserHeight; y++) for(x = 0; x < eraserWidth; x++) { dx = x - eraserWidth / 2; dy = y - eraserHeight / 2; d = sqrt(dx * dx + dy * dy); if(pow(drand48(), .75) * eraserWidth / 2 > d) eraserpix[i + 0] = 255; eraserpix[i + 1] = 255; eraserpix[i + 2] = 255; eraserpix[i + 3] = 255; } i += 4; 77

Mouse /* Left button, first layer. Middle button, second layer */ void mouse(int button, int state, int x, int y) { if (state == GLUT_DOWN) { eraser = GL_TRUE; if (button == GLUT_LEFT_BUTTON) layer = 1; else /* GLUT_MIDDLE: GLUT_RIGHT is for menu */ layer = 0; } else { /* GLUT_UP */ eraser = GL_FALSE; } glutPostRedisplay(); I don't have a middle button: I removed the menu to restore this functionality to right button 78

DrawEraser void draweraser(void) { ... /* replace with this layer */ glStencilFunc(GL_ALWAYS, layer, 0); glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE); glDisable(GL_LIGHTING); glDisable(GL_DEPTH_TEST); glEnable(GL_ALPHA_TEST); glAlphaFunc(GL_NOTEQUAL, 0); glRasterPos2i(eraserpos[X], eraserpos[Y]); glBitmap(0, 0, 0.f, 0.f, -winWidth/8.f, -winHeight/12.f, 0); glDrawPixels(eraserWidth, eraserHeight, GL_RGBA, GL_UNSIGNED_BYTE, eraserpix); glDisable(GL_ALPHA_TEST); } 79

Translation glStencilFunc(GL_ALWAYS, layer, 0); glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE); void glStencilFunc(GLenum func, GLint ref, GLuint mask); void glStencilOp( GLenum fail, GLenum zfail, GLenum zpass); fail: The action to take when the stencil test fails. GL_KEEP Keeps the current value. zfail: Stencil action when the stencil test passes, but the depth test fails. GL_REPLACE Replace with layer zpass: Stencil action when both the stencil test and the depth test pass, or when the stencil test passes and either there is no depth buffer or depth testing is not enabled.

Draw Routine void redraw(void) { ... glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); glStencilFunc(GL_EQUAL, 2, (unsigned)~0); drawlayer2(); // Checkerboard glStencilFunc(GL_EQUAL, 1, (unsigned)~0); drawlayer1(); // Sphere and cone glStencilFunc(GL_EQUAL, 0, (unsigned)~0); drawlayer0(); // Rotating cone if(eraser) draweraser(); ... } 81

Recap: glStencilFunc glStencilFunc(GL_ALWAYS, 2, 1); glStencilFunc(GLenum func, GLint ref, GLuint mask); func - comparison function, ref The reference value for the stencil test. The ref parameter is clamped to the range [0,2n 1], where n is the number of bitplanes in the stencil buffer. mask mask that is ANDed with both the reference value and the stored stencil value when the test is done. The function can be GL_NEVER, GL_ALWAYS, ..GL_EQUAL,… By default, func is GL_ALWAYS, ref is 0, mask is all 1s, and stenciling is disabled.

glStencilOp glStencilOp(GL_INCR, GL_KEEP, GL_DECR);
void glStencilOp( GLenum fail, GLenum zfail, GLenum zpass); fail: The action to take when the stencil test fails. GL_KEEP Keeps the current value. GL_ZERO Sets the stencil buffer value to zero. GL_REPLACE Sets the stencil buffer value to ref, as specified by glStencilFunc. GL_INCR Increments the current stencil buffer value. Clamps to the maximum representable unsigned value. GL_DECR Decrements the current stencil buffer value. Clamps to zero. GL_INVERT Bitwise inverts the current stencil buffer value. zfail: Stencil action when the stencil test passes, but the depth test fails. Accepts the same symbolic constants as fail. zpass: Stencil action when both the stencil test and the depth test pass, or when the stencil test passes and either there is no depth buffer or depth testing is not enabled. Accepts the same symbolic constants as fail.

Layer 2 void drawlayer2(void) // Use a texture { ... glDisable(GL_LIGHTING); glDisable(GL_DEPTH_TEST); glEnable(GL_TEXTURE_2D); glBegin(GL_QUADS); glTexCoord2i(0, 0); glVertex2i(0, 0); glTexCoord2i(1, 0); glVertex2i(winWidth, 0); glTexCoord2i(1, 1); glVertex2i(winWidth, winHeight); glTexCoord2i(0, 1); glVertex2i(0, winHeight); glEnd(); glDisable(GL_TEXTURE_2D); } 84

Layer 0 Layer 1 is similar, but more complex…
void drawlayer0(void) // Rotating cone { static GLfloat lightpos[] = {50.f, 50.f, 0.f, 1.f}; glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(-50.f, 50.f, -50.f, 50.f, 0.f, 100.f); glMatrixMode(GL_MODELVIEW); glTranslatef(0.f, 0.f, -50.f); glRotatef(angle, 0.f, 1.f, 0.f); glRotatef(90.f, 0.f, 0.f, 1.f); glTranslatef(0.f, -25.f, 0.f); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glLightfv(GL_LIGHT0, GL_POSITION, lightpos); glCullFace(GL_BACK); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glCallList(CONE); } 85

Halo When your action figure acquires super powers, draw her with a halo However, don't want to obscure her face with yellow schmutz So set reference stencil value and draw her normally Then expand her, and draw the enlarged area that does not touch the old values with the halo color, blending with background color 86

Reflections To draw reflections… We can use Ray Tracing We have faked reflections with environmental mapping We will"reflect" the model through a plane and draw it again Stencil buffer helps to keep it clean 87

Reflection Implementation
Keep two copies 88

Reflection Implementation
Two common problems: 89

Reflection Implementation
Two common problems: Extending past edge of mirror Appearing behind the mirror 90

multimirror Why stop at 2 copies?
SIGGRAPH 1997 Advanced OpenGL tutorial Can add or remove rooms: > < Can change viewpoint: h

92 Before and After Kilgard walks through an implementation of a mirror without the stencil buffer He points out two problems, Then walks through it again, using the stencil buffer

93 Prepare view 1. Load the modelview matrix with the view transform. For example, given the eye location, the center of viewing, and up direction vector, the view transform can be set as follows: glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(eye[0], eye[1], eye[2], center[0], center[1], center[2], up[0], up[1], up[2]); 2. Push the modelview matrix: glPushMatrix();

94 Reflection Transform 3. Multiply the current modelview matrix by a reflection matrix that reflects the scene through the plane of the mirror. Consider the special case where the mirror happens to lie in the z=0 plane. This special case reflection is a simple scaling matrix that negates the Z coordinate: glScalef(1.0, 1.0, -1.0); For an arbitrary plane, a 4 by 4 matrix can be constructed to reflect through the plane 4. If using back face culling, cull front faces instead. The reflective transformation flips the front/back orientation of transformed polygons. glCullFace(GL_FRONT);

95 Reflected Scene 5. Draw the scene, but be careful to render only objects on the reflective side of the mirror plane. Otherwise, objects behind the mirror that should properly be obscured by the mirror will be rendered as if they are actually in front of the mirror plane. 6. Resume normal back face culling and undo the reflection matrix. glCullFace(GL_BACK); glPopMatrix();

Mirror Surface glEnable(GL_BLEND);
96 Mirror Surface 7. Optionally, to give the appearance of a semi-reflective surface such as polished marble, or simply a dull or dirty mirror, a textured planar surface coincident with the mirror plane can be blended on top of the reflection rendered in Step 5. For example: glEnable(GL_BLEND); glBlendFunc(GL_ONE, GL_ONE); // additive blending renderMarbleTexturedMirrorSurfacePolygons(); glDisable(GL_BLEND); Even if mirror surface is not blended with a semi-reflective surface, it is important to render the mirror surface polygons into the depth buffer to ensure that when the scene is rendered again in the next step that objects properly are occluded by the mirrors. glColorMask(0,0,0,0); // disable color buffer updates renderMirrorSurfacePolygons(); // update depth buffer with the mirror surface glColorMask(1,1,1,1); // re-enable color buffer updates

Unreflected Scene 8. Finally, now render the unreflected scene.
97 Unreflected Scene 8. Finally, now render the unreflected scene. This type of rendering algorithm is often referred to as multi-pass because the sequence of operations involves rendering the scene or portions of the scene multiple times. Notice that steps 5 and 8 each render the scene but with different model view transformations. Assumes that you cannot look behind the plane of the mirror. Reflective stairs…

Unreflected Scene 8. Finally, now render the unreflected scene.
98 Unreflected Scene 8. Finally, now render the unreflected scene. This type of rendering algorithm is often referred to as multi-pass because the sequence of operations involves rendering the scene or portions of the scene multiple times. Notice that steps 5 and 8 each render the scene but with different model view transformations. Assumes that you cannot look behind the plane of the mirror. Reflective stairs…

Mirrors with Stencil Buffer
When clearing the frame buffer at start, also clear the stencil buffer to zero. For each mirror in the scene, the application must maintain a data structure that contains the non-overlapping and co-planar polygons that compose the mirror surface. For a standard rectangular mirror, the coordinates of four vertices are sufficient. Clear the color, depth, and stencil buffers. Then render the scene, excluding the mirror surfaces, with depth buffering but without stencil testing. glClearStencil(0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFER_BIT); glEnable(GL_DEPTH_BUFFER_BIT); glDisable(GL_STENCIL_TEST); drawScene(); // draw everything except mirrors

Set the bits in Stencil Buffer
100 Set the bits in Stencil Buffer Then for each mirror, perform the following sequence: 1. Set up stenciling to write the value 1 into the stencil buffer when the depth test passes. Also disable writes to the color buffer. Then draw the mirror’s polygons. glEnable(GL_STENCIL_TEST); glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); glStencilFunc(GL_ALWAYS, 1, ~0); glColorMask(0,0,0,0); renderMirrorSurfacePolygons(thisMirror); This step “tags” all the mirror’s visible pixels with a stencil value 1. Depth testing prevents occluded mirror pixels from being tagged.

Mirrors with Stencil Buffer
101 Mirrors with Stencil Buffer 2. With the color buffer writes still disabled, set the depth range to write the farthest value possible for all updated pixels and set the depth test to always pass. Also, set the stencil test to only update pixels tagged with the stencil value 1. Then draw the mirror’s polygons. glDepthRange(1,1); // always glDepthFunc(GL_ALWAYS); // write the farthest depth value glStencilFunc(GL_EQUAL, 1, ~0); // match mirror’s visible pixels glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // do not change stencil values renderMirrorSurfacePolygons(thisMirror); This step resets the depth buffer to its cleared maximum value for all the mirror’s visible pixels.

Prepare the mirror space
102 Prepare the mirror space 3. Restore the depth test, color mask, and depth range to their standard settings: glDepthFunc(GL_LESS); glColorMask(1,1,1,1); glDepthRange(0,1);

103 Draw the Reflection We are now ready to render the reflection itself. The pixels belonging to the reflection with the stencil value 1, and these same pixels also have their depth values cleared to the farthest depth value. The stencil test remains enabled and only updating pixels with the stencil value 1, i.e. those pixels on the visible mirror. And the less than depth test will ensure subsequent rendering to the mirror pixels will determine visible surfaces appropriately. 4. Rendering the reflection requires reflecting the scene through the mirror plane, but we must also be careful to only render objects on the reflective side of the mirror. Therefore, we establish a user-defined clip plane to render only objects on the reflective side of the mirror plane. The reflection itself is accomplished by concatenating the appropriate reflection matrix to the modelview matrix so that everything is reflected through the mirror plane. Because the reflection flips the sense of back and front facing polygons, the cull face state is reversed. Then the scene is rendered.

Draw the Reflection 104 GLfloat matrix[4][4]; GLdouble clipPlane[4];
glPushMatrix(); // returns world-space plane equation for mirror plane to use as clip plane computeMirrorClipPlane(thisMirror, &clipPlane[0]); // set clip plane equation glClipPlane(GL_CLIP_PLANE0, &clipPlane); // returns mirrorMatrix (see Appendix A) for given mirror computeReflectionMatrixForMirror(thisMirror, &matrix[0][0]); // concatenate reflection transform into modelview matrix glMultMatrixf(&matrix[0][0]); glCullFace(GL_FRONT); drawScene(); // draw everything except mirrors drawOtherMirrorsAsGraySurfaces(thisMirror); // draw other mirrors as // neutral “gray” surfaces glCullFace(GL_BACK); glDisable(GL_CLIP_PLANE0); glPopMatrix();

105 Draw the Reflection Now the mirror’s reflection is rendered correctly. Other mirrors visible in the reflection of this mirror are rendered gray so at least they properly occlude objects behind them even if they do not reflect objects correctly. Immediately following, we sketch a recursive algorithm that handles reflections of reflections. 5. Finally, we reset to zero the stencil value of all the mirror’s pixels so the pixels are not confused with another mirror’s pixels while rendering the reflections of subsequent mirrors. Also update the depth buffer to reflect the mirror’s proper depth so this mirror may properly occlude any subsequent mirrors. Do not update the color buffer during this step. glColorMask(0,0,0,0); glStencilOp(GL_KEEP, GL_KEEP, GL_ZERO); glDepthFunc(GL_ALWAYS); renderMirrorSurfacePolygons(thisMirror); glDepthFunc(GL_LESS); glColorMask(1,1,1,1);

multimirror Room is 2x2x2 cube centered at origin typedef struct {
GLfloat verts[4][3]; GLfloat scale[3]; /* Scale */ GLfloat trans[3]; /* Translation */ } Mirror; Mirror mirrors[] = { /* mirror on the left wall */ {{{-1., -.75, -.75}, {-1., .75, -.75}, {-1., .75, .75}, {-1, -.75, .75}}, {-1, 1, 1}, /* x = -x */ {2, 0, 0}}, /* x = x + 2 */ int nMirrors = 2; Room is 2x2x2 cube centered at origin

draw_mirror void draw_mirror(Mirror *m) { glBegin(GL_QUADS);
glVertex3fv(m->verts[0]); glVertex3fv(m->verts[1]); glVertex3fv(m->verts[2]); glVertex3fv(m->verts[3]); glEnd(); } does not draw what you see in mirror

reflect /* It would be easier to use push and pop to
* save and restore the matrices, but stack * is very shallow */ GLenum reflect_through_mirror(Mirror *m, GLenum cullFace) { GLenum newCullFace = ((cullFace == GL_FRONT) ? GL_BACK : GL_FRONT); glMatrixMode(GL_PROJECTION); glScalef(m->scale[0], m->scale[1], m->scale[2]); glTranslatef(m->trans[0], m->trans[1], m->trans[2]); glMatrixMode(GL_MODELVIEW); /* must flip the cull face */ glCullFace(newCullFace); return newCullFace; }

undo_reflect void undo_reflect_through_mirror(Mirror *m, GLenum cullFace) { glMatrixMode(GL_PROJECTION); glTranslatef(-m->trans[0], -m->trans[1], -m->trans[2]); glScalef(1./m->scale[0], 1./m->scale[1], 1./m->scale[2]); glMatrixMode(GL_MODELVIEW); glCullFace(cullFace); }

drawroom void draw_room(void) {
/* material for the walls, floor, ceiling */ static GLfloat wall_mat[] = {1.f, 1.f, 1.f, 1.f}; glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, wall_mat); glBegin(GL_QUADS); /* floor */ glNormal3f(0, 1, 0); glVertex3f(-1, -1, 1); glVertex3f(1, -1, 1); glVertex3f(1, -1, -1); glVertex3f(-1, -1, -1);

make viewpoint void make_viewpoint(void) { if (headsUp) { ... } else {
glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(60, 1, .01, 4 + 2*(draw_passes / nMirrors)); /* far */ gluLookAt(-2, 0, .75, 0, 0, 0, 0, 1, 0); }

draw callback void draw(void) { GLenum err; GLfloat secs = get_secs();
glDisable(GL_STENCIL_TEST); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); if (!headsUp) glEnable(GL_STENCIL_TEST); draw_scene(secs, draw_passes, GL_BACK, 0, (unsigned)-1); ... }

draw_scene void draw_scene(GLdouble secs, int passes, GLenum cullFace, GLuint stencilVal, GLuint mirror) { GLenum newCullFace; int passesPerMirror, passesPerMirrorRem; unsigned int curMirror, drawMirrors; int i; /* one pass to draw the real scene */ passes--; /* only draw in my designated locations */ glStencilFunc(GL_EQUAL, stencilVal, 0xffffffff); /* draw things which may obscure the mirrors first */ draw_sphere(secs); draw_cone();

draw_scene /* now draw the appropriate number of mirror reflections. for * best results, we perform a depth-first traversal by allocating * a number of passes for each of the mirrors. */ if (mirror != 0xffffffff) { passesPerMirror = passes / (nMirrors - 1); passesPerMirrorRem = passes % (nMirrors - 1); if (passes > nMirrors - 1) drawMirrors = nMirrors - 1; else drawMirrors = passes; } else { /* mirror == -1 means that this is the initial scene */ passesPerMirror = passes / nMirrors; passesPerMirrorRem = passes % nMirrors; if (passes > nMirrors) drawMirrors = nMirrors; else drawMirrors = passes; }

draw_scene for (i = 0; drawMirrors > 0; i++) {
curMirror = i % nMirrors; if (curMirror == mirror) continue; drawMirrors--; /* draw mirror into stencil buffer but not color or depth buffers */ glColorMask(0, 0, 0, 0); glDepthMask(0); glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); draw_mirror(&mirrors[curMirror]); glColorMask(1, 1, 1, 1); glDepthMask(1); glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);

draw_scene for (i = 0; drawMirrors > 0; i++) {
curMirror = i % nMirrors; if (curMirror == mirror) continue; drawMirrors--; /* draw mirror into stencil buffer ... */ ... /* draw reflected scene */ newCullFace = reflect_through_mirror(&mirrors[curMirror], cullFace); if (passesPerMirrorRem) { draw_scene(secs, passesPerMirror + 1, newCullFace, stencilVal + 1, curMirror); passesPerMirrorRem--; } else { draw_scene(secs, passesPerMirror, newCullFace, stencilVal + 1, curMirror); } undo_reflect_through_mirror(&mirrors[curMirror], cullFace);

Summary We can use buffers to do some powerful image processing One more way to harness the power in your graphics card

Resources Excellent paper "Improving Shadows and Reflections via the Stencil Buffer" by Mark Kilgard. The alpha blending and stencil test examples are from the Red Book halomagic - The rest are from 1996 and 1997 SIGCSE Advanced OpenGL talks Several of them require multiple source files and data files motionblur.c, convolve.c, field.c – depth of field 1997 – decal.c, dissolve.c

Resources Writing the Life game with Stencil Buffer Drawing Concave Polygons with Stencil Buffer

Video Lady from Shanghai Mirror sequence Manhattan Murder Mystery Mirror sequence Cheat Sheet

List of Sample Programs
alpha alphablend motionblur convolve field stencil decal dissolve halogmagic reflectdino multimirror