1 buffers © jeff parker, 2011. 2 objectives introduce additional opengl buffers buffers in general...
TRANSCRIPT
1
Buffers
© Jeff Parker, 2011
2
Objectives
Introduce additional OpenGL buffers
Buffers in General
Alpha Blending
Accumulation Buffers
Stencil Buffers
Please fill out the on-line evaluations
3 Angel: Interactive Computer Graphics 4E © Addison-Wesley 2004
BufferDefine a buffer by
Spatial resolution (n x m) and its
Depth (or precision) k, the number of bits/pixel
Purpose
pixel
4 Angel: Interactive Computer Graphics 4E © Addison-Wesley 2004
OpenGL Frame Buffer
We have seen the first three buffers
We will focus on two more tonight
Accumulation Buffer
Accumulate – add up
Stencil Buffer
Single out - mark
5 Angel: Interactive Computer Graphics 4E © Addison-Wesley 2004
Writing in BuffersConceptually, 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
frame buffer(destination)
writing into frame buffer
sourcememory
6 Angel: Interactive Computer Graphics 4E © Addison-Wesley 2004
Writing Model
Read destination pixel before writing source
7 Angel: Interactive Computer Graphics 4E © Addison-Wesley 2004
Bit Writing ModesSource and destination bits are combined bitwise
16 possible functions (one per column in table)
replaceOR
XOR
8 Angel: Interactive Computer Graphics 4E © Addison-Wesley 2004
XOR modeRecall 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
S S M
swaps the S and M
9 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
10 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
11
ExamplesWe have seen two 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();
}
12 Angel: Interactive Computer Graphics 4E © Addison-Wesley 2004
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
13
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)
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();
}
Which first?
void display(void)
{
glClear(GL_COLOR_BUFFER_BIT);
if (leftFirst) {
drawLeftTriangle();
drawRightTriangle();
}
else {
drawRightTriangle();
drawLeftTriangle();
}
glFlush();
}
16
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 (1 + 1 + 97)/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
17
Applications
We can combine images
Or perform image processing on image
We can use this for
Motion blur
Antialiasing
Depth of Field
Soft Shadows
Next week
18
Motion Blur
SIGGRAPH 1996
motionblur.c
Render the ball multiple times, moving the ball between passes
http://www.sgi.com/products/software/opengl/examples/glut/advanced/
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);
break;
Clear bufferAdd into accumulation bufferCopy 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();
glPushMatrix();
glTranslatef(-20.f, -80.f, -600.f);
glCallList(CONE);
glPopMatrix();
Note that we waste time reimaging everything, when only the sphere moves
ConvolutionsIt is common in image processing to use convolutions
A common use is to find edges, as with the Sobel filter below
22
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};
23
Note that we waste time reimaging everything, when only the sphere moves
http://www.sgi.com/products/software/opengl/examples/glut/advanced/
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]);
}
}
}25
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 pairsThis 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.
29
Depth of FieldSIGGRAPH 1996
field.c
How can we get the effect of the depth of field?
30
http://www.sgi.com/products/software/opengl/examples/glut/advanced/
Imagefor(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);
glLoadIdentity();
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
32
Fragment TestingNot every fragment becomes a pixel.
Some make it to frame buffer, but are overwritten
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.
33 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
34
Bit Tests
35
Stencil Buffer
37
Stencil Buffer
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.
Redbook stenciltest.cUnlike 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. Most support 8 today.
This example 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);
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);
glClear(GL_COLOR_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
// ? Didn’t we do this
The glClear() function sets the bitplane area of the window to values previously set by glClearStencil() ...
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
42
00000100000
00001110000
00011111000
00111111100
01111111110
Values in the stencil buffer
TranslationglStencilFunc(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.
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_REPLACE Set stencil buffer value to ref
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
45
What does the stencil hold?
glStencilFunc(GL_EQUAL, 1, 1);glStencilOp(GL_INCR, GL_KEEP, GL_DECR);
There are two effects: Color (or not) andSet stencil buffer (or not)
Note that depth test is not enabled
TranslationglStencilFunc(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
47
00000100000
00110001100
00100000100
00000000000
01111111110
glStencilFunc(GL_EQUAL, 1, 1);glStencilOp(GL_INCR, GL_KEEP, GL_DECR);
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();
Painting in Blue
49
00000100000
00110001100
00100000100
00000000000
01111111110
glStencilFunc(GL_EQUAL, 1, 1);glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
TranslationglStencilFunc(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.
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_KEEP Keeps the current value.
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
51
Stencil Test Protocol Summary
52
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.
maskA - 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.
53
glStencilOpglStencilOp(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.
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
56
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);
}}
TranslationglStencilFunc(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.
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_REPLACE Replace with decalNum
Draw
58
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
59
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);
}
}
TranslationglStencilFunc(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
60
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);
break;
}
glutSwapBuffers();
}61
Array of colorsThese 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 */
};
62
SIGGRAPH 1997 dissolve.c
63
Eraser
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;
}
}
64
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();
}
65
I don't have a middle button: I removed the menu to restore this functionality to right button
DrawEraser
void draweraser(void)
{
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0, winWidth, 0, winHeight);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
/* 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);
}
66
Larger on next page…
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);
}
67
TranslationglStencilFunc(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.
GL_REPLACE Replace with layer
Draw Routine
void redraw(void)
{
if (glutLayerGet(GLUT_NORMAL_DAMAGED) || clearstencil == GL_TRUE) {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
clearstencil = GL_FALSE;
} else
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
glStencilFunc(GL_EQUAL, 2, (unsigned)~0);
drawlayer2();
glStencilFunc(GL_EQUAL, 1, (unsigned)~0);
drawlayer1();
glStencilFunc(GL_EQUAL, 0, (unsigned)~0);
drawlayer0();
if (eraser)
draweraser(); ...
}
69
Larger on later page…
glStencilFunc
glStencilFunc(GL_ALWAYS, 1, 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.
70
glStencilOpglStencilOp(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.
71
Draw Routinevoid redraw(void)
{
...
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
glStencilFunc(GL_EQUAL, 2, (unsigned)~0);
drawlayer2();
glStencilFunc(GL_EQUAL, 1, (unsigned)~0);
drawlayer1();
glStencilFunc(GL_EQUAL, 0, (unsigned)~0);
drawlayer0();
if(eraser)
draweraser(); ...
}
72
Layer 2void drawlayer2(void)
{
...
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);
}
73
Layer 0
void drawlayer0(void)
{
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);
glLoadIdentity();
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);
}
74
Layer 1 is similar, but more complex…
Init
glClearStencil(2);
glEnable(GL_STENCIL_TEST); /* used all the time */
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
75
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
76
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
77
78
multimirror
SIGGRAPH 1997 Advanced OpenGL tutorial
Can add or remove rooms: > <
Can change viewpoint: h
79
multimirror
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
80
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
81
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;
}
82
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);
}
83
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);
84
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);
}
85
draw callbackvoid 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);
glDisable(GL_STENCIL_TEST);
...
}
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();
87
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);
90
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();
91
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);
92
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();
93
Mirror Surface
7. Optionally, to give the appearance of a semi-reflective surface such as polished marble, orsimply a dull or dirty mirror, a textured planar surface coincident with the mirror plane canbe blended on top of the reflection rendered in Step 5. For example:
glEnable(GL_BLEND);glBlendFunc(GL_ONE, GL_ONE); // additive blendingrenderMarbleTexturedMirrorSurfacePolygons();glDisable(GL_BLEND);
Even if mirror surface is not blended with a semi-reflective surface, it is important torender the mirror surface polygons into the depth buffer to ensure that when the scene isrendered again in the next step that objects properly are occluded by the mirrors.
glColorMask(0,0,0,0); // disable color buffer updatesrenderMirrorSurfacePolygons(); // update depth buffer with the mirror surface
glColorMask(1,1,1,1); // re-enable color buffer updates
94
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…
95
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 thatcontains 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 mirrorsurfaces, 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
96
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.
97
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); // alwaysglDepthFunc(GL_ALWAYS);
// write the farthest depth valueglStencilFunc(GL_EQUAL, 1, ~0);
// match mirror’s visible pixelsglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
// do not change stencil valuesrenderMirrorSurfacePolygons(thisMirror);
This step resets the depth buffer to its cleared maximum value for all the mirror’s visible pixels.
98
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);
99
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.
100
Draw the Reflection
GLfloat matrix[4][4];GLdouble clipPlane[4];glPushMatrix();// returns world-space plane equation for mirror plane to use as clip
planecomputeMirrorClipPlane(thisMirror, &clipPlane[0]);// set clip plane equationglClipPlane(GL_CLIP_PLANE0, &clipPlane);// returns mirrorMatrix (see Appendix A) for given mirrorcomputeReflectionMatrixForMirror(thisMirror, &matrix[0][0]);// concatenate reflection transform into modelview matrixglMultMatrixf(&matrix[0][0]);glCullFace(GL_FRONT);drawScene(); // draw everything except mirrorsdrawOtherMirrorsAsGraySurfaces(thisMirror); // draw other mirrors as// neutral “gray” surfacesglCullFace(GL_BACK);glDisable(GL_CLIP_PLANE0);glPopMatrix();
101
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);
Summary
We can use buffers to do some powerful image processing
One more way to harness the power in your graphics card
Don't forget to fill out the evaluations.
102
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
http://www.opengl.org/resources/code/samples/redbook/
halomagic - http://www.sgi.com/products/software/opengl/examples/glut/examples/
The rest are from 1996 and 1997 SIGCSE Advanced OpenGL talks
http://www.sgi.com/products/software/opengl/examples/glut/advanced/
http://www.opengl.org/resources/code/samples/advanced/advanced97/programs/programs.html
Several of them require multiple source files and data files.
1996 - motionblur.c, convolve.c, field.c – depth of field
1997 – decal.c, dissolve.c
Video
Lady from Shanghai Mirror sequence
http://www.youtube.com/watch?v=G05H0QacqQM
Manhattan Murder Mystery Mirror sequence
http://www.youtube.com/watch?v=yUmd9w84Q2Q
Sample Programs
alphablend
motionblur
convolve
field
stencil
decal.c
dissolve.c
multimirror.c
halogmagic
http://www.opengl.org/resources/code/samples/redbook/
http://www.sgi.com/products/software/opengl/examples/glut/advanced/