2. OpenGL Window Reshaping (Version 2.0)

Introduction

In a poorly written OpenGL application, you may notice that when you resize your window, your geometry will not transform to match the size and aspect ratio of your new window size. Your geometry may stay the same size even though the window is smaller or larger and what we want is for our geometry to scale with our window. This is because when GLUT sets up our window, it will initially set some default values for us, but GLUT doesn’t keep track of this information as the window changes, and for good reasons, every application has different needs, and there are no default magic values for everyone.

To fix this, we need to tell GLUT how we want our scene to be rendered, and to do so, we need to understand a little more about OpenGL matrices, and matrices in 3D applications in general. Now there are three main matrices in OpenGL, those being the Projection Matrix, Model View Matrix and the Texture Matrix.

Projection Matrix

The Projection Matrix, just like most matrices in 3D is a 4×4 matrix. This matrix provides the information needed to map a 3D scene to a 2D plane. In the case of OpenGL, this 2D plane is known as our Colour Buffer and this stores the image that we see rendered to our GLUT window, although rendering of the Colour Buffer to a window is not required for off-screen rending. In order to set up our Projection Matrix, we need to tell OpenGL the angle in degrees of our field of view, the aspect ratio for our window which will be our 2D rendering plane, and the near and far planes that bound our render.

Model View Matrix

The next main matrix is the Model View Matrix, which is also known as your Transformation Matrix. This matrix tells us the location and rotation of our geometry placement at any point in time. If you want to move an object, you will be modifying your Model View Matrix, without even knowing it.

Texture Matrix

The Texture Matrix is something you usually don’t want to touch. This is similar to the Model View Matrix as it is also a Transformation Matrix, but it is used for manipulation the position and rotation of textures in texture space. Personally I have only ever used this as a spare matrix when passing information through to GLSL shaders, but in fixed function rendering (standard OpenGL), you won’t want to touch this unless you have a specific reason.

Coding

So let’s now take a look at setting up how to reshape our window.

If you are following through my tutorials, each one will build upon another, and this one builds on top of the first tutorial on creating a window with GLUT. So for this tutorial you will want to open up the code from the previous tutorial.

Now go ahead, and create a new method called “reshape” which takes in two integer values and returns a void. The two incoming parameters will be sent via GLUT, and will contain the width and the height of the window at the point in time that it is called.

It should look something like this:

void reshape (int width, int height) {
}

Then down inside the main method, we need to let GLUT know which method to use for our reshaping, this is done almost identically to how we told GLUT to use our display() method.  After the line:

glutDisplayFunc(display); // Tell GLUT to use the method "display" for rendering

Add the line:

glutReshapeFunc(reshape); // Tell GLUT to use the method "reshape" for rendering

Now that GLUT knows which method to call, let’s fill it in so that it actually does something.

The first thing we are going to do is tell OpenGL the size of our viewport (the 2D plane) that we want to render to. We are going to tell OpenGL to start at (0, 0), which corresponds to the bottom left of our window, and then we will tell it to take the size (width, height), which will give us the top right of our window.

glViewport(0, 0, (GLsizei)width, (GLsizei)height); // Set our viewport to the size of our window

Once we have setup the size of our viewport, we need to switch to our Projection Matrix so that we can setup how our scene is rendered.

glMatrixMode(GL_PROJECTION); // Switch to the projection matrix so that we can manipulate how our scene is viewed

Now that we are in the correct mode, we are going to reset our Projection Matrix to the identity matrix so that we don’t cause any artefacts.

Forgetting to reset matrices is a big cause of unusual rendering as OpenGL adds on the previous matrix every time something is called. In the case of setting the Projection Matrix, it would be like trying to apply the projection on to the current projection.

glLoadIdentity(); // Reset the projection matrix to the identity matrix so that we don't get any artifacts (cleaning up)

Now we are ready for the core of this method. The following line will setup the parameters for our view, such as the field of view, aspect ratio and the near and far planes. The first variable that I am going to set is the field of view (FOV), which I am going to set to 60 degrees. This gives us a viewing angle of 30 degrees to the left, and 30 degrees to the right.

The next value after that is the aspect ratio of our window, which is calculated by dividing the width of our window by the height. The last two values are the near and far plane, and I am going to set them to 1.0 and 100.0 respectively. This means that anything with a position less than 1.0 unit (away from the camera) won’t be drawn and anything with a position greater than 100.0 units (away from the camera) won’t be drawn.

gluPerspective(60, (GLfloat)width / (GLfloat)height, 1.0, 100.0); // Set the Field of view angle (in degrees), the aspect ratio of our window, and the new and far planes

The very last thing we need to do in our method is switch back to the Model View Matrix. If we forget to do this, then we will be trying to position our geometry according to our Projection Matrix, and we do not want this.

glMatrixMode(GL_MODELVIEW); // Switch back to the model view matrix, so that we can start drawing shapes correctly

And that should do it, just make sure everything compiles, and when we add some geometry, you can see this method in action.

It doesn’t seem like much, but this is an extremely important method. You will want absolute control over your field of view, and near and far planes in most applications you write.

If you have any questions, please email me at swiftless@gmail.com

#include <GL/glew.h> // Include the GLEW header file
#include <GL/glut.h> // Include the GLUT header file

void display (void) {
glClearColor(1.0f, 0.0f, 0.0f, 1.0f); // Clear the background of our window to red
glClear(GL_COLOR_BUFFER_BIT); //Clear the colour buffer (more buffers later on)
glLoadIdentity(); // Load the Identity Matrix to reset our drawing locations
glFlush(); // Flush the OpenGL buffers to the window
}

void reshape (int width, int height) {
glViewport(0, 0, (GLsizei)width, (GLsizei)height); // Set our viewport to the size of our window
glMatrixMode(GL_PROJECTION); // Switch to the projection matrix so that we can manipulate how our scene is viewed
glLoadIdentity(); // Reset the projection matrix to the identity matrix so that we don't get any artifacts (cleaning up)
gluPerspective(60, (GLfloat)width / (GLfloat)height, 1.0, 100.0); // Set the Field of view angle (in degrees), the aspect ratio of our window, and the new and far planes
glMatrixMode(GL_MODELVIEW); // Switch back to the model view matrix, so that we can start drawing shapes correctly
}

int main (int argc, char **argv) {
glutInit(&argc, argv); // Initialize GLUT
glutInitDisplayMode (GLUT_SINGLE); // Set up a basic display buffer (only single buffered for now)
glutInitWindowSize (500, 500); // Set the width and height of the window
glutInitWindowPosition (100, 100); // Set the position of the window
glutCreateWindow ("Your first OpenGL Window"); // Set the title for the window
glutDisplayFunc(display); // Tell GLUT to use the method "display" for rendering
glutReshapeFunc(reshape); // Tell GLUT to use the method "reshape" for rendering
glutMainLoop(); // Enter GLUT's main loop
}
  • March 25, 2010
  • 52