NeHe OpenGL Lesson26 – Clipping & Reflections Using The Stencil Buffer
This sample shows us how to display a reflection effect with stencil buffer and plane clipping in OpenGL. The stencil buffer usually goes with the depth buffer in the display card, and also called mask buffer. The stencil buffer filled along with the normal render process. That means when you want to render something to the frame buffer, you could mask some area in the stencil buffer with some stencil operation settings. Later, you could do some further process (usually render something) aim to those masked area with correct stencil testing functions.
Render Processing
For this sample, let go into it’s main draw scene function, and check how the scene will be rendered. In summary, the main scene rendering process could be divided into four part as following:
1) draw the floor only into the stencil buffer, color buffer writable disabled. And you need to set the correct depth testing here, stencil testing goes with depth testing. The code as following:
// step (1) // Disable the color buffer writable glColorMask(0,0,0,0); // set up how the stencil buffer will be filled glEnable(GL_STENCIL_TEST); glStencilFunc(GL_ALWAYS, 1, 1); glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); // z-test always sccuss glDisable(GL_DEPTH_TEST); // draw floor into the stencil buffer. // The area that floor will render to in the stencil buffer will be 1. DrawFloor(); // restore the render state settings glEnable(GL_DEPTH_TEST);
2) Enable the color buffer writable, set up the stencil buffer & plane clipping limitation, draw the reflected sphere object. Mirror the object against XZ plane using glScale(1,-1,1) is a very good idea. The code as following:
// step (2) // Enable the color buffer writable glColorMask(1,1,1,1); // Only the area that stencil buffer filled with 1 could be render into. glStencilFunc(GL_EQUAL, 1, 1); // Do not need to write stencil, just keep everything there glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // With clip plane to limit the reflect object display do not beyond the plane glEnable(GL_CLIP_PLANE0); glClipPlane(GL_CLIP_PLANE0, eqr); glPushMatrix(); // mirror the object against the XZ plane glScalef(1.0f, -1.0f, 1.0f); glLightfv(GL_LIGHT0, GL_POSITION, LightPos); glTranslatef(0.0f, height, 0.0f); glRotatef(xrot, 1.0f, 0.0f, 0.0f); glRotatef(yrot, 0.0f, 1.0f, 0.0f); // draw the reflected object DrawObject(); glPopMatrix(); glDisable(GL_CLIP_PLANE0); // stencil only used for limit the reflected object display on the floor. // We do not need it now, so shut it down. glDisable(GL_STENCIL_TEST);
glClipPlane used to define the clip plane, and this plane will be defined in the world space. Here are some details about this function, “glClipPlane specifies a half-space using a four-component plane equation. When glClipPlane is called, equation is transformed by the inverse of the modelview matrix and stored in the resulting eye coordinates. Subsequent changes to the modelview matrix have no effect on the stored plane-equation components.” This will make our clipping plane set up job more easy, what we need to do is find the correct plane in the local space of the clipped object. Of course, you need to make both of them in the same space when rendering. It is a bit similar to that if you want to see though a window, you just need to find the frame of the window.
3) Disable the stencil test, draw the floor with alpah blend operation. The code as following:
// step (3) // draw the floor with alpha blend glLightfv(GL_LIGHT0, GL_POSITION, LightPos); glEnable(GL_BLEND); glDisable(GL_LIGHTING); glColor4f(1.0f, 1.0f, 1.0f, 0.8f); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); DrawFloor();
4) Draw the sphere with lighting. The code as following:
// step (4) // draw the sphere with lighting glEnable(GL_LIGHTING); glDisable(GL_BLEND); glTranslatef(0.0f, height, 0.0f); glRotatef(xrot, 1.0f, 0.0f, 0.0f); glRotatef(yrot, 0.0f, 1.0f, 0.0f); DrawObject();
Something More
In addition to the reflection effect, here something more that you could explore.
1) The second texture used for the sphere specular map, with glBlendFunc(GL_SRC_ALPHA, GL_ONE) and another blend process, one diffuse texture blend with one specular texture:
2) What will happen, if you switch on the auto-texture coordinates generate feature and also provide them with glTexCoord2f? The answer is that auto-generates one will override other ones, that means the auto-generate one will always be used. The specular map for the sphere will use the auto-generate one, but one already there.
The full source code for windows version could be downloaded from here.