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:

  1. int w1; // Window width  
  2. 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:

  1. void orthogonalStart(void) {  
  2.     // Switch to projection matrix mode  
  3.     glMatrixMode(GL_PROJECTION);  
  4.   
  5.     // Save the current projection matrix  
  6.     glPushMatrix();  
  7.   
  8.     // Reset the projection matrix  
  9.     glLoadIdentity();  
  10.   
  11.     // Set up the orthogonal projection  
  12.     gluOrtho2D(0, w1, 0, h1);  
  13.   
  14.     // Flip the Y-axis and translate to correct the origin  
  15.     glScalef(1, -1, 1);  
  16.     glTranslatef(0, -h1, 0);  
  17.   
  18.     // Switch back to model-view matrix mode  
  19.     glMatrixMode(GL_MODELVIEW);  
  20. }  

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:

  1. void orthogonalEnd(void) {  
  2.     // Switch to projection matrix mode  
  3.     glMatrixMode(GL_PROJECTION);  
  4.   
  5.     // Restore the previous projection matrix  
  6.     glPopMatrix();  
  7.   
  8.     // Switch back to model-view matrix mode  
  9.     glMatrixMode(GL_MODELVIEW);  
  10. }  

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:

  1. void display(void) {  
  2.     // Clear the screen and depth buffer  
  3.     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);  
  4.   
  5.     // Start orthogonal projection  
  6.     orthogonalStart();  
  7.   
  8.     // Draw a 2D quad  
  9.     glBegin(GL_QUADS);  
  10.     glVertex2f(125, 125); // Bottom-left corner  
  11.     glVertex2f(125, 375); // Top-left corner  
  12.     glVertex2f(375, 375); // Top-right corner  
  13.     glVertex2f(375, 125); // Bottom-right corner  
  14.     glEnd();  
  15.   
  16.     // End orthogonal projection  
  17.     orthogonalEnd();  
  18.   
  19.     // Swap buffers for double buffering  
  20.     glutSwapBuffers();  
  21. }  

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:

  1. void reshape(int w, int h) {  
  2.     // Set the viewport to the new window dimensions  
  3.     glViewport(0, 0, (GLsizei)w, (GLsizei)h);  
  4.   
  5.     // Update the perspective projection  
  6.     glMatrixMode(GL_PROJECTION);  
  7.     glLoadIdentity();  
  8.     gluPerspective(60, (GLfloat)w / (GLfloat)h, 0.1, 1000.0);  
  9.   
  10.     // Update global window size variables  
  11.     w1 = w;  
  12.     h1 = h;  
  13.   
  14.     // Switch back to model-view mode  
  15.     glMatrixMode(GL_MODELVIEW);  
  16. }  

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:

  1. #include <gl gl.h="">  
  2. #include <gl glut.h="">  
  3.   
  4. // Global variables for window dimensions  
  5. int w1;  
  6. int h1;  
  7.   
  8. // Start orthogonal projection  
  9. void orthogonalStart(void) {  
  10.     glMatrixMode(GL_PROJECTION);  
  11.     glPushMatrix();  
  12.     glLoadIdentity();  
  13.     gluOrtho2D(0, w1, 0, h1);  
  14.     glScalef(1, -1, 1);  
  15.     glTranslatef(0, -h1, 0);  
  16.     glMatrixMode(GL_MODELVIEW);  
  17. }  
  18.   
  19. // End orthogonal projection  
  20. void orthogonalEnd(void) {  
  21.     glMatrixMode(GL_PROJECTION);  
  22.     glPopMatrix();  
  23.     glMatrixMode(GL_MODELVIEW);  
  24. }  
  25.   
  26. // Render the scene  
  27. void display(void) {  
  28.     glClearColor(1.0, 0.0, 0.0, 1.0);  
  29.     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);  
  30.     glLoadIdentity();  
  31.   
  32.     // Draw in orthogonal mode  
  33.     orthogonalStart();  
  34.     glBegin(GL_QUADS);  
  35.     glVertex2f(125, 125);  
  36.     glVertex2f(125, 375);  
  37.     glVertex2f(375, 375);  
  38.     glVertex2f(375, 125);  
  39.     glEnd();  
  40.     orthogonalEnd();  
  41.   
  42.     glutSwapBuffers();  
  43. }  
  44.   
  45. // Handle window resizing  
  46. void reshape(int w, int h) {  
  47.     glViewport(0, 0, (GLsizei)w, (GLsizei)h);  
  48.     glMatrixMode(GL_PROJECTION);  
  49.     glLoadIdentity();  
  50.     gluPerspective(60, (GLfloat)w / (GLfloat)h, 0.1, 1000.0);  
  51.     w1 = w;  
  52.     h1 = h;  
  53.     glMatrixMode(GL_MODELVIEW);  
  54. }  
  55.   
  56. // Main function  
  57. int main(int argc, char **argv) {  
  58.     glutInit(&argc, argv);  
  59.     glutInitDisplayMode(GLUT_DOUBLE | GLUT_DEPTH | GLUT_RGBA);  
  60.     glutInitWindowSize(500, 500);  
  61.     glutCreateWindow("Orthogonal Projection Example");  
  62.   
  63.     glutDisplayFunc(display);  
  64.     glutIdleFunc(display);  
  65.     glutReshapeFunc(reshape);  
  66.   
  67.     glutMainLoop();  
  68.     return 0;  
  69. }  
  70. </gl></gl>  

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

  • March 25, 2010
  • 10