34. Orthogonal Projections (Version 2.0)

Introduction

Orthogonal projections in OpenGL allow you to incorporate 2D elements into a 3D program. These projections are essential for creating overlays like health bars, ammo counters, and other HUD (Heads-Up Display) elements that should remain fixed on the screen, independent of the 3D camera’s rotation or movement.

In this tutorial, we’ll explore how to implement orthogonal projections in OpenGL. This includes setting up the projection, drawing 2D elements, and seamlessly switching back to perspective projection for the 3D scene.

Setting Up Variables

We’ll define two global variables to store the window’s width and height, which will be updated in the reshape function:

int w1; // Window width
int h1; // Window height

These variables are used to configure the orthogonal projection space and ensure it matches the window dimensions.

Starting Orthogonal Projections

The `orthogonalStart` function sets up the orthogonal projection. Here’s the breakdown:

void orthogonalStart(void) {
    // Switch to projection matrix mode
    glMatrixMode(GL_PROJECTION);

    // Save the current projection matrix
    glPushMatrix();

    // Reset the projection matrix
    glLoadIdentity();

    // Set up the orthogonal projection
    gluOrtho2D(0, w1, 0, h1);

    // Flip the Y-axis and translate to correct the origin
    glScalef(1, -1, 1);
    glTranslatef(0, -h1, 0);

    // Switch back to model-view matrix mode
    glMatrixMode(GL_MODELVIEW);
}

Explanation:
– **`glPushMatrix` and `glPopMatrix`:** These functions save and restore the current projection matrix, ensuring the orthogonal projection doesn’t interfere with the perspective projection.
– **`gluOrtho2D`:** Sets up the orthogonal projection. The parameters define the viewable area in screen coordinates, where (0,0) is the bottom-left corner.
– **`glScalef` and `glTranslatef`:** Correct the Y-axis orientation, as OpenGL’s default coordinate system has the origin at the bottom-left, but most screen systems place the origin at the top-left.

Ending Orthogonal Projections

Once the 2D elements are drawn, we call `orthogonalEnd` to restore the previous projection matrix and return to the perspective projection:

void orthogonalEnd(void) {
    // Switch to projection matrix mode
    glMatrixMode(GL_PROJECTION);

    // Restore the previous projection matrix
    glPopMatrix();

    // Switch back to model-view matrix mode
    glMatrixMode(GL_MODELVIEW);
}

This ensures the 3D scene continues unaffected after rendering the 2D elements.

Drawing 2D Elements

With orthogonal projections active, we can draw 2D shapes using pixel coordinates. For example:

void display(void) {
    // Clear the screen and depth buffer
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    // Start orthogonal projection
    orthogonalStart();

    // Draw a 2D quad
    glBegin(GL_QUADS);
    glVertex2f(125, 125); // Bottom-left corner
    glVertex2f(125, 375); // Top-left corner
    glVertex2f(375, 375); // Top-right corner
    glVertex2f(375, 125); // Bottom-right corner
    glEnd();

    // End orthogonal projection
    orthogonalEnd();

    // Swap buffers for double buffering
    glutSwapBuffers();
}

Key Points:
– 2D elements are drawn with `glVertex2f`, which specifies vertices in 2D space.
– Coordinates in orthogonal mode correspond to pixels, making placement intuitive for screen-based UI elements.

Handling Reshape Events

In the reshape function, we update the `w1` and `h1` variables with the window’s current width and height:

void reshape(int w, int h) {
    // Set the viewport to the new window dimensions
    glViewport(0, 0, (GLsizei)w, (GLsizei)h);

    // Update the perspective projection
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(60, (GLfloat)w / (GLfloat)h, 0.1, 1000.0);

    // Update global window size variables
    w1 = w;
    h1 = h;

    // Switch back to model-view mode
    glMatrixMode(GL_MODELVIEW);
}

This ensures the orthogonal projection adjusts dynamically when the window is resized.

Tutorial Code

Here’s the complete program for implementing orthogonal projections in OpenGL:

#include 
#include 

// Global variables for window dimensions
int w1;
int h1;

// Start orthogonal projection
void orthogonalStart(void) {
    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    glLoadIdentity();
    gluOrtho2D(0, w1, 0, h1);
    glScalef(1, -1, 1);
    glTranslatef(0, -h1, 0);
    glMatrixMode(GL_MODELVIEW);
}

// End orthogonal projection
void orthogonalEnd(void) {
    glMatrixMode(GL_PROJECTION);
    glPopMatrix();
    glMatrixMode(GL_MODELVIEW);
}

// Render the scene
void display(void) {
    glClearColor(1.0, 0.0, 0.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();

    // Draw in orthogonal mode
    orthogonalStart();
    glBegin(GL_QUADS);
    glVertex2f(125, 125);
    glVertex2f(125, 375);
    glVertex2f(375, 375);
    glVertex2f(375, 125);
    glEnd();
    orthogonalEnd();

    glutSwapBuffers();
}

// Handle window resizing
void reshape(int w, int h) {
    glViewport(0, 0, (GLsizei)w, (GLsizei)h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(60, (GLfloat)w / (GLfloat)h, 0.1, 1000.0);
    w1 = w;
    h1 = h;
    glMatrixMode(GL_MODELVIEW);
}

// Main function
int main(int argc, char **argv) {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_DEPTH | GLUT_RGBA);
    glutInitWindowSize(500, 500);
    glutCreateWindow("Orthogonal Projection Example");

    glutDisplayFunc(display);
    glutIdleFunc(display);
    glutReshapeFunc(reshape);

    glutMainLoop();
    return 0;
}

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

  • March 25, 2010
  • 10