28. OpenGL Basic Reflection (Version 2.0)
Introduction
Reflections are a visually striking effect that can greatly enhance the realism of your OpenGL scenes. In this tutorial, we’ll explore how to create basic reflections using the stencil buffer. This approach allows us to restrict reflections to a specific plane, such as a water surface or a polished floor.
The stencil buffer ensures that reflections are rendered only on the defined plane, while the rest of the scene remains unaffected. This method is particularly effective for static reflections, and it builds upon concepts introduced in the shadows tutorial.
What is the Stencil Buffer?
The stencil buffer acts as a mask, defining which parts of the scene should be affected by certain rendering operations. For reflections, the stencil buffer:
- Defines the plane where reflections are drawn.
- Clips any part of the reflection outside the specified plane.
- Ensures reflections do not overwrite other parts of the scene.
Setting Up the Stencil Buffer
To use the stencil buffer, we modify the display mode when initializing GLUT:
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_STENCIL); // Add stencil buffer
We also clear the stencil buffer alongside the color and depth buffers:
glClearStencil(0); // Reset stencil buffer glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
Defining the Reflection Plane
The reflection plane is defined using the stencil buffer. First, disable color and depth masking:
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); // Disable color updates glDepthMask(GL_FALSE); // Disable depth updates glEnable(GL_STENCIL_TEST); // Enable stencil testing
Next, set the stencil buffer to replace data on the plane:
glStencilFunc(GL_ALWAYS, 1, 0xFFFFFFFF); // Stencil test always passes glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE); // Replace stencil data
We then draw the plane using the `bench()` function:
bench(); // Define the reflection plane
Finally, re-enable color and depth masking:
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // Enable color updates glDepthMask(GL_TRUE); // Enable depth updates glStencilFunc(GL_EQUAL, 1, 0xFFFFFFFF); // Pass only where stencil equals 1 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // Keep stencil buffer data
Rendering the Reflection
The reflection is rendered by flipping the object vertically, translating it to the reflection plane, and applying the necessary transformations:
glDisable(GL_DEPTH_TEST); // Disable depth testing for reflection glPushMatrix(); glScalef(1.0f, -1.0f, 1.0f); // Flip reflection vertically glTranslatef(0, 2, 0); // Translate to the reflection plane glRotatef(angle, 0, 1, 0); // Apply rotation square(); // Draw the reflection glPopMatrix(); glEnable(GL_DEPTH_TEST); // Re-enable depth testing glDisable(GL_STENCIL_TEST); // Disable stencil testing
Rendering the Scene
To blend the reflection with the surface, we use alpha blending for transparency:
glEnable(GL_BLEND); // Enable blending glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Set blending function bench(); // Render the reflective surface glDisable(GL_BLEND); // Disable blending
Finally, render the rest of the scene, including the original object:
glRotatef(angle, 0, 1, 0); // Apply rotation square(); // Draw the original object
Tutorial Code
Here’s the complete implementation of the basic reflections tutorial:
#include <GL/gl.h> #include <GL/glut.h> float angle = 0; // Rotation angle GLuint texture[2]; // Texture IDs // Function to load textures GLuint loadtextures(const char *filename, float width, float height) { GLuint texture; unsigned char *data; FILE *file = fopen(filename, "rb"); if (file == NULL) return 0; data = (unsigned char *)malloc(width * height * 3); fread(data, width * height * 3, 1, file); fclose(file); glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_2D, texture); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); gluBuild2DMipmaps(GL_TEXTURE_2D, 3, width, height, GL_RGB, GL_UNSIGNED_BYTE, data); free(data); return texture; } // Function to draw a square void square(void) { glPushMatrix(); glBindTexture(GL_TEXTURE_2D, texture[0]); glTranslatef(0, 2.5, 0); glScalef(2, 2, 2); glBegin(GL_QUADS); glTexCoord2f(1, 0); glVertex3f(-1, -1, 0); glTexCoord2f(1, 1); glVertex3f(-1, 1, 0); glTexCoord2f(0, 1); glVertex3f(1, 1, 0); glTexCoord2f(0, 0); glVertex3f(1, -1, 0); glEnd(); glPopMatrix(); } // Function to draw the bench void bench(void) { glPushMatrix(); glColor4f(1, 1, 1, 0.7); // Semi-transparent surface glBindTexture(GL_TEXTURE_2D, texture[1]); glTranslatef(0, -2.5, 0); glScalef(4, 2, 4); glBegin(GL_QUADS); glTexCoord2f(1, 0); glVertex3f(-1, -1, 1); glTexCoord2f(1, 1); glVertex3f(-1, 1, -0.5); glTexCoord2f(0, 1); glVertex3f(1, 1, -0.5); glTexCoord2f(0, 0); glVertex3f(1, -1, 1); glEnd(); glPopMatrix(); } // Display callback void display(void) { glClearStencil(0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); glLoadIdentity(); glTranslatef(0, 0, -10); // Stencil buffer setup glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); glDepthMask(GL_FALSE); glEnable(GL_STENCIL_TEST); glStencilFunc(GL_ALWAYS, 1, 0xFFFFFFFF); glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE); bench(); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glDepthMask(GL_TRUE); glStencilFunc(GL_EQUAL, 1, 0xFFFFFFFF); glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // Render reflection glDisable(GL_DEPTH_TEST); glPushMatrix(); glScalef(1.0f, -1.0f, 1.0f); glTranslatef(0, 2, 0); glRotatef(angle, 0, 1, 0); square(); glPopMatrix(); glEnable(GL_DEPTH_TEST); glDisable(GL_STENCIL_TEST); // Render scene glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); bench(); glDisable(GL_BLEND); glRotatef(angle, 0, 1, 0); square(); glutSwapBuffers(); angle++; } // Initialization function void init(void) { glEnable(GL_DEPTH_TEST); glEnable(GL_TEXTURE_2D); text
Great job!
For those who can’t succeed run the code, here some tips:
1. Make sure all the ” (double quotes) are actually ” (double quotes). When you copy and paste the code, the quotes aren’t the right way the are supposed to be;
2. Make sure all the – (minus signals) are actually – (minus signals). Same energy as before.
3. Make sure all the comment lines are actually comment out.