0
\$\begingroup\$

I am working on rendering various 2D shapes in OpenGL. After initial experimentation, I came to the conclusion that the rendered image is distorted due to the window having a non-square aspect ratio:

Distorted render

See how the hexagon looks squashed and the circle looks like an oval due to aspect ratio distortion

The code that produced this issue is given here - I linked it as it is quite long and complex and separated over multiple files.

I have also come to the conclusion that there are 3 methods of rectifying this issue:

  • Adjusting glViewport() such that the viewport is square (but this would lead to large gaps on each side of the square viewport)
  • Using a projection matrix to achieve the same effect as the prior method (but again, this would lead to large gaps)
  • Changing the vertices of the polygons themselves with an offset so they are drawn with the correct aspect ratio and do not get stretched

Which of these methods is the suggested method for dealing with viewport distortion?

\$\endgroup\$
5
  • \$\begingroup\$ Why would using a projection matrix cause gaps? Basically every 3D game does it this way with no gaps: the entire screen/window gets rendered to, no matter how wide/skinny, with no bars outside the renderable area, though your shapes may be grouped in the middle instead of stretched to fill. \$\endgroup\$
    – DMGregory
    Jun 15, 2023 at 13:21
  • \$\begingroup\$ @DMGregory sorry, I wasn't aware of this, I thought a projection matrix would have the same effect as setting glViewport() \$\endgroup\$
    – JS4137
    Jun 15, 2023 at 13:25
  • \$\begingroup\$ Do you have a minimal, complete and verifiable example? Based on your description, this doesn't sound like standard behavior or match my experience w/ OpenGL (that is to say, by default, square things should render as square, etc unless something else has be set to make them 'stretch'). \$\endgroup\$
    – Pikalek
    Jun 15, 2023 at 13:25
  • \$\begingroup\$ @Pikalek added as per your comment \$\endgroup\$
    – JS4137
    Jun 15, 2023 at 14:05
  • 1
    \$\begingroup\$ Code samples should be embedded in the question itself, not linked off-site. \$\endgroup\$
    – DMGregory
    Jun 15, 2023 at 14:29

2 Answers 2

2
\$\begingroup\$

Your vertex shader code takes the vertex position and dumps it straight into gl_Position:

#version 330 core
in vec2 position;
in vec3 color;
out vec3 Color;
void main() {
    Color = color;
    gl_Position = vec4(position, 0.0, 1.0);
}

The trouble with this is that gl_Position expects a position (that, after the perspective divide by w, will be) in a coordinate system called Normalized Device Coordinates (NDC). In this coordinate system, x=-1 is the left edge of the viewport, x=+1 is the right edge, y=-1 is the bottom, and y=+1 is the top, regardless of the size or aspect ratio of that viewport.

That means a horizontal edge of length 1 in NDC will be small on a narrow viewport and longer on a wide viewport.

By passing your positions through unchanged this way, you're saying "I already handled converting to NDC CPU-side, so the GPU doesn't have to worry about transforming these coordinates to match the window/screen". But that's not true here.

The simplest fix would be to add a uniform aspectRatio, which you populate just before drawing to be the ratio of the current height to its width. Then you can adjust your vertex shader to say...

gl_Position = vec4(position.x * aspectRatio, position.y, 0.0, 1.0);

On tall skinny windows, aspectRatio will be greater than one, stretching the x axis to compensate for the shrinking of the NDC coordinate space's screen coverage in that direction. On squat wide windows, aspectRatio will be less than one, shrinking the x axis to compensate for the stretching of the NDC coordinate space. Either way, squares stay square.

This particular approach keeps the y axis unchanged, so you'll still fit the same amount of content between the bottom and top edge of the window no matter how large the window is - the content will just shrink or grow uniformly as the window is scaled vertically. Changing the window's width will act like cropping - keeping the displayed content the same size, but shrinking or growing the aperture through which we're viewing it.

A projection matrix can do the same job, just with more generality to encompass other kinds of transforms a 3D app might want to do too. But that does come with (minor) extra costs - 16 floats instead of one, 4 dot products instead of one scalar multiply. Nothing that would break the bank - modern games use matrix multiplies in shaders all the time - just a bit overkill if you only need aspect ratio adjustment in one axis.

\$\endgroup\$
-1
\$\begingroup\$

the suggested method for dealing with viewport distortion in this case is to adjust the vertices of the polygons themselves with an offset so they are drawn with the correct aspect ratio and do not get stretched. This approach allows you to maintain control over the shape of the objects being rendered and ensures that they appear undistorted regardless of the window's aspect ratio.

By modifying the vertices with an offset, you can compensate for the stretching or shrinking of the NDC coordinate space's screen coverage in a specific direction. This method keeps the y axis unchanged, so the content will fit between the bottom and top edges of the window while the x axis is adjusted to maintain the correct aspect ratio. This ensures that squares stay square, and the overall appearance of the rendered shapes remains consistent.

and so while using a projection matrix can achieve similar results, it introduces additional complexity and overhead, as it encompasses other kinds of transformations that may not be necessary in this scenario. If you only need aspect ratio adjustment in one axis, modifying the vertices directly provides a more straightforward and efficient solution.

i mean adjusting the vertices of the polygons themselves with an offset is the suggested method for preventing viewport distortion in this context. It allows you to maintain control over the shape of the rendered objects and ensures that they appear undistorted regardless of the window's aspect ratio.

\$\endgroup\$
1
  • 3
    \$\begingroup\$ Sections of this answer appear to be sentence-for-sentence paraphrases of mine. I'd recommend focusing on your own original contributions that add something new to the thread which has not already been stated. \$\endgroup\$
    – DMGregory
    Jun 16, 2023 at 17:27

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .