18. OpenGL Popping and Pushing Matrices (Version 2.0)

Introduction

In OpenGL, transformations such as rotations, translations, and color changes can accumulate, affecting all subsequent objects. This behavior occurs because OpenGL operates as a state machine, where each transformation modifies the current state and remains active until explicitly changed.

This tutorial explores how to control transformations using matrix stacks, specifically with glPushMatrix() and glPopMatrix(). These commands allow you to isolate transformations for individual objects, making it possible to manipulate them independently within the same scene.

Understanding Matrix Stacks

OpenGL uses matrix stacks to manage transformations. Each stack operates like a stack of plates:
glPushMatrix(): Saves the current transformation state onto the stack.
glPopMatrix(): Restores the last saved transformation state, discarding any changes made since the corresponding glPushMatrix().

Why Use Matrix Stacks?

Matrix stacks are essential for scenarios where you want to:
– Apply transformations to one object without affecting others.
– Temporarily modify the transformation state for a specific task, then revert to the previous state.

Here’s an example:

glPushMatrix();              // Save the current state
glTranslatef(1.0, 0.0, 0.0); // Apply translation
glRotatef(45.0, 0.0, 1.0, 0.0); // Apply rotation
glutWireCube(1.0);           // Draw a cube
glPopMatrix();               // Revert to the previous state

In this example, the translation and rotation only affect the cube drawn between the glPushMatrix() and glPopMatrix() calls.

Attributes and Matrix Stacks

Beyond transformations, OpenGL provides glPushAttrib() and glPopAttrib() to save and restore specific attributes, such as lighting and colors. These commands allow finer control over the rendering state:

glPushAttrib(GL_LIGHTING_BIT);  // Save the lighting attributes
glEnable(GL_LIGHTING);          // Enable lighting
glPopAttrib();                  // Restore the previous lighting state

Tutorial Code

Below is a complete example demonstrating how to use glPushMatrix() and glPopMatrix() to control transformations for two independently rotating cubes:

// Include necessary libraries
#include <GL/gl.h>
#include <GL/glut.h>

GLfloat angle = 0.0;  // Rotation angle for the first cube
GLfloat tangle = 0.0; // Rotation angle for the second cube

// Draw the first cube
void cube(void) {
    glPushMatrix();               // Save the current state
    glTranslatef(1.0, 0.0, 0.0);  // Move the cube to the right
    glRotatef(angle, 1.0, 0.0, 0.0); // Rotate around the X-axis
    glRotatef(angle, 0.0, 1.0, 0.0); // Rotate around the Y-axis
    glRotatef(angle, 0.0, 0.0, 1.0); // Rotate around the Z-axis
    glColor3f(1.0, 0.0, 0.0);    // Set the cube's color to red
    glutWireCube(2.0);            // Draw the cube
    glPopMatrix();                // Restore the previous state
}

// Draw the second cube
void cube2(void) {
    glPushMatrix();                // Save the current state
    glTranslatef(-1.0, 0.0, 0.0);  // Move the cube to the left
    glRotatef(tangle, 1.0, 0.0, 0.0); // Rotate around the X-axis
    glRotatef(tangle, 0.0, 1.0, 0.0); // Rotate around the Y-axis
    glRotatef(tangle, 0.0, 0.0, 1.0); // Rotate around the Z-axis
    glColor3f(0.0, 1.0, 0.0);     // Set the cube's color to green
    glutWireCube(2.0);             // Draw the cube
    glPopMatrix();                 // Restore the previous state
}

// Display callback
void display(void) {
    glClearColor(0.0, 0.0, 0.0, 1.0); // Set the background color to black
    glClear(GL_COLOR_BUFFER_BIT);     // Clear the color buffer
    glLoadIdentity();
    gluLookAt(0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); // Position the camera

    cube();   // Draw the first cube
    cube2();  // Draw the second cube

    glutSwapBuffers();
    angle += 1.0;   // Increment the first cube's angle
    tangle += 2.0;  // Increment the second cube's angle
}

// Reshape callback
void reshape(int w, int h) {
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(60, (GLfloat)w / (GLfloat)h, 1.0, 100.0);
    glMatrixMode(GL_MODELVIEW);
}

// Main function
int main(int argc, char** argv) {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE);
    glutInitWindowSize(500, 500);
    glutCreateWindow("Push and Pop Matrix Example");
    glutDisplayFunc(display);
    glutIdleFunc(display);
    glutReshapeFunc(reshape);
    glutMainLoop();
    return 0;
}

If you have any questions or run into issues, feel free to email me at swiftless@gmail.com. Happy coding!

  • March 25, 2010
  • 9