16
\$\begingroup\$

I have researched this question from several sources and have yet to find a firm answer saying that "yes that is correct thinking" or "no, here is how it's done."

I am trying to ensure resolution independence with OpenGL rendering. The way I think I should go about it, is to create a projection using glOrtho with whatever I want the world coordinates to be. For example, glOrtho(-50.0, 50.0, -50.0, 50.0, 1.0, -1.0). Next, set the viewport to the screen resolution, i.e. - glViewport(0, 0, 800, 600). Finally, whenever the window is re-sized, make a call to glViewport with the updated screen resolution. This will scale your figures.

Is this the correct way to ensure that models take the same proportion of screen-space on different resolutions? Should I be using a projection equal to the resolution as well? I found some answers saying glOrtho should use the resolution of your window while others said it should/can be different.

Thoughts?

\$\endgroup\$

1 Answer 1

20
\$\begingroup\$

What you have described is entirely adequate and appropriate to provide resolution independence. Anything you draw will indeed always take up the same proportion of your window.

However, if you do nothing more than this, you will have aspect ratio problems. For example, given the numbers you wrote, if you draw a circle, it will be squashed — wider than it is tall, because your horizontal scale is 800/(50+50) = 8 and your vertical scale is 600/(50+50) = 6.

There cannot be any automatic solution to this problem; you will have to choose what graphical result you want, and consider its effect on the gameplay. Some common examples:

  • In a perspective-projection 3D game with no HUD, the usual simple solution (available in OpenGL as part of gluPerspective) is to fix the projection's field of view relative to the vertical dimension. This means that in such a game, if you resize the window horizontally, you will see more or less of the scene and the middle part of the image will not change at all.

    The same idea can be applied to a 2D/2.5D view as well.

    (Note that this allows the player to enlarge their field of view horizontally by making a wide but not tall window. This could be unwanted in a competitive multiplayer game.)

  • If you have viewport-filling graphics with a fixed aspect ratio (for example, a non-scrolling 2D map, or a decorative border), or if you otherwise cannot change the layout, then you have to do something like having black bars on two sides to fill the unused space.

    Or you can make graphics which have thematically consistent filler material around the edges that they can be cropped to fit the display without harming gameplay.

  • If you have a HUD on top of other graphics, you can decide that each HUD element is fixed to a part of the window (top left, bottom center, etc.) and compute its coordinates; the 'stretching' of varying the aspect ratios gets absorbed by the 'white space' between HUD elements.

As to the specific mathematics of the matter, given that you are doing resolution independence, all you need to start out with is the aspect ratio, a single number: width / height. For example, if the window is square it will be 1; if it is 800 by 600 it will be 800/600 = 4/3 = 1.333̅.

For example, suppose you want the orthographic projection you have written to gain additional space on the left and right sides if the window is widened. You can do that like this:

float aspect = width / height;
glViewport(0, 0, width, height);
glOrtho(-50.0 * aspect, 50.0 * aspect, -50.0, 50.0, 1.0, -1.0);

This guarantees that, for windows at least as wide as they are tall, anything you draw with x and y coordinates within -50 to 50 will be visible.

On the other hand, if the window is narrower than it is tall, the -50-to-50 range will be cut off. Let's say you wanted to make sure that it is always visible (so that your content is at its maximum size if the window is square, but smaller otherwise); in that case, you simply do the same thing to the height instead of the width.

float aspect = width / height;
glViewport(0, 0, width, height);
if (aspect >= 1.0)
  glOrtho(-50.0 * aspect, 50.0 * aspect, -50.0, 50.0, 1.0, -1.0);
else
  glOrtho(-50.0, 50.0, -50.0 / aspect, 50.0 / aspect, 1.0, -1.0);

Note that in the second case we divide rather than multiply. This is simply because we computed the aspect ratio as width / height rather than height / width; take the reciprocal if you find it easier to understand that way.

Again, this is not the only way to manage aspect ratio; this is just a very simple one for making sure that your content is neither squished nor cut off. Figure out what you want to happen when your window is wide, tall, or whatever; then work out the mathematics of it.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ Thank you for the VERY insightful response. Answered my question beyond sufficiently. \$\endgroup\$
    – user26519
    Feb 25, 2013 at 16:07

You must log in to answer this question.