OpenGL stretched shapes - aspect ratio

apoiat picture apoiat · Jan 30, 2012 · Viewed 14.3k times · Source

I got my openGL view at a size of (lets say..) 800(width)x600(height). Then i got a 2D object of coords:

0.0 , 0.0
0.1 , 0.0
0.1 , 0.1
0.0 , 0.1

this, as you understand is supposed to be a square (based on analogy). But on my openGL view prints this stretched. Now, i understand why this is happening. Its basically because im working on the default matrix of -1,1. And since my resolution of 800x600 doesnt have an aspect ratio of 1, my shape is getting stretched.

Now im more interested on how to fix this. Ive read about functions like glFrustum, glOrtho, glViewPort which are happening on the projection matrix and can address such issues. Thing is, im not sure on using them. What i basically want is keep the above coords when making a square and actually apear as a square on my View.

Whats the correct way of addressing this issue?

Answer

datenwolf picture datenwolf · Jan 30, 2012
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
float aspect = (float)width / (float)height;
glOrtho(-aspect, aspect, -1, 1, -1, 1);

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

Update: Explanation what happens

OpenGL is a state machine and in the case of OpenGL-2.1 and below maintains a set of transformation matrices. A vertex ↑v first gets multiplied with the modelview matrix to yield a eye coordinate vertex ↑v', which is used for lighting calculations. Then ↑v' is multiplied by the projection matrix to reach the so called clip space ↑v'', where (the name suggests it) the clipping operations are performed (or at least their outcome is defined). From clip space the so called Normalized Device Coordinates (NDC) are reached by calculating ↑v#{x,y,z,w} = ↑v{x,y,z,w}/↑v_w

↑v# is defined to be in the range [-1 … 1] which is mapped to fill the selected viewport rect. The viewport does not influence the transformation matrices! It's only the transformation from NDC to window coordinates it defines.

In the code above I set the modelview matrix to identity, i.e. the vertices go into projection as they are. The projection itself is a ortho projection, that maps the x value range [-aspect … aspect] → [-1 … 1] and y value range [-1 … 1] → [-1 … 1] (i.e. no change for y). So vertices get transformed by the window aspect to fit into the NDC to viewport value range.

And why did I tell you that OpenGL is a state machine? Because it means that you can switch projections and transformations anytime. So if you need to render a minimap, don't struggle with getting it placed in the 3D scene. Just switch the viewport and projection for it. In general you should set all rendering state (that means all the matrices) in the drawing function. Window event handlers (except the redraw handler) should perform no OpenGL operations at all.