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!
Hey Swiftless, great job with the tutorial. I have one question though about this. (You did not mention it in the tutorial). How can I make it so that my 3D drawings in perspective mode show up underneath the 2D drawings in Orthogonal. Right know I have both 2D and 3D drawings, and when I change my view so that the 2D objects overlap with the 3D objects. The 2D objects hide underneath. I’m looking for the effect in games where I can store stats and other information on the screen at all times. Thanks in advance. -Nick.
Just kidding. I was drawing my 2D stuff before the 3D. I changed it so I draw the 2D after and now it works fine 😛
precompiled executables would be a massive benefit, as well as the source code in a project.
I keep finding myself having to create projects, sort out the source code, and compile. This takes time, especially on the longer tutorials.
Other than that, nice work!
Dale
Hey Dale,
I don’t know if you have noticed, but the new OpenGL 4.0 tutorials I provide include Visual C++ Projects, and once you have created a template project, most of my code should be copy and paste into one file.
However precompiled executables are generally not recommended to be placed online. A lot of people don’t trust them for starters, so I have no intention of placing these online at any stage.
Cheers,
Swiftless
Hey Swiftless, thanks for the reply.
All im saying is that some people (like myself) would prefer to have access to an executable to see the effects of the tutorial without having to compile it all first.
I dont want to get into an argument, but if people dont want to trust them, then surely that is their choice?
if your worried about security, zip it up and create a hask key. put the hask at the top of the tutorial. This way people can find out if it has been modified at all.
The main reason i suggested this in the first place is because sometime copying and pasting your tutorials, the code lines get messed up, and i have to sit there for a few minutes fixing it so the code will compile. i can imagine this would be worse on small netbooks though!
Hey man, its only a suggestion, you dont have to read a comment from me ever again, but you want to know how to make your site better, and that is my 2 pence worth.
Dale
Hey Dale,
I didn’t mean to come off rude, I apologize if I did.
For the very reason of showing off the effects of the tutorial, I moved to creating Youtube videos with the latest OpenGL 4.0 tutorials and am hoping to get time to do the same for these as well when I rewrite them.
I find it takes more effort to supply an executable in that manner than it does to film it for 10secs, add a title and upload it.
In the current rewrites of the tutorials, I have also switched to a better method of displaying code, which has a copy and paste friendly button 🙂
Feel free to leave any feedback, I read everything that people post on the site and have tried to cater for peoples requests thus far.
Cheers,
Swiftless
Hey Swiftless,
You didnt come across as rude, infact, i wondered if i was! but likewise, it wasnt intentional if i did.
Your solutions definately sound more user friendly than what is currently available.
keep up with the good articles, and if you ever need and help with web development give me a shout, i probably owe it to you for supplying these tutorials 😀
Dale
nice tutorials, but please add some images (applies for every tutorial on this site). a picture says more then a hundert words…
Hey a,
Thats a great idea, and with the new tutorials I am even trying to add YouTube videos to support them.
Cheers,
Swiftless
Great stuff! Just what I needed, much appreciated!