Nov 4, 2011

Maintaining Aspect Ratio in OpenGL with glFrustum While Resizing Windows

Maintaining aspect ratio in OpenGL can be tricky with glFrustum calls. I searched the web for some good code to do this, but I found nothing really satisfying.  Let me set up the scenario. Let's say you have a texture that you want to display (an image). Let's also say it has some aspect ratio.  Whenever you resize you window you want to set the viewport to be the same size as the window, but still maintain aspect ratio and not have the texture spill out of window bounds.  The correct way to do this is with some glFrustum calls to set up your projection matrix correctly.  First let's draw the texture centered at (0,0).  Here is the code for that:

Assuming you have already created a texture, bound it and enabled GL_TEXTURE...
glTexCoord2f(0.0f, 0.0f);
glVertex2f(-aspectRatio * 0.5f, 0.5f);
glTexCoord2f(0.0f, 1.0f);
glVertex2f(-aspectRatio * 0.5f, -0.5f);
glTexCoord2f(1.0f, 1.0f);
glVertex2f(aspectRatio * 0.5f, -0.5f);
glTexCoord2f(1.0f, 0.0f);
glVertex2f(aspectRatio * 0.5f, 0.5f);

Notice that our x-coordinates are all half of the aspect ratio and the y-coordinates are half of one. Every time you resize your window you need to then snap your texture to the top and bottom with some space on the sides or to the left and right with some space on the sides. This is assuming that your window aspect ratio is not the same as your texture aspect ratio. If they are the same, then all is well and there will be no space on either side. So, what magic will get you the right aspect ratio on every resize without the texture spilling out of bounds? Here it is:
// get the window aspect ratio
float windowAspectRatio = this->width() / (float) this->height();
float halfWindowAspectRatio = windowAspectRatio * 0.5f;
// aspectRatio is the aspect ratio of your frame or texture
float halfAspectRatio = aspectRatio * 0.5f;
float left, right, top, bottom;
if (aspectRatio < windowAspectRatio) {
    // top and bottom should be flush with window
    top = 0.5f; // remember this is the top of the texture
    left = -halfWindowAspectRatio;
} else {
    // left and right should be flush with window
    left = -halfAspectRatio;
    // we are keeping top relative to the windowAspectRatio (normalizing here)
    top = halfAspectRatio / windowAspectRatio;
right = -left;
bottom = -top;
There it is. Notice that the two cases rely on the fact that the aspect ratio of the texture is either less than or greater than the aspect ratio of the window. There it is though, in all its glory.  Once you have that you just need to set those values into your glFrustum call and you're done.

No comments:

Post a Comment