11
\$\begingroup\$

I'm hoping someone can help me understand the GLViewport and what happens when we resize it

This will illustrate my confusion....

enter image description here

So, here I have a quad stuck in the middle of the screen. If I have my GLViewport match the device's width and height, I get what is on the first (left hand) picture. Exactly what I would expect.

  • Device resolution, 2560 x 1600
  • Viewport resolution 2560 x 1600
  • Quad size 200 x 200 (Note, the image above is not to scale!!! :-))
  • Quad shape, appears as square

Now, for the 2nd (right hand) picture...

  • Device resolution, 2560 x 1600
  • Viewport resolution 2560 x 1200 (and centered vertically)
  • Quad size (200, 200)
  • Quad shape, appears as rectangle

My question is, why is the quad displaying as a rectangle now and not a square? I've confirmed by logging that my quad is 200 x 200 pixes - surely the size of the physical pixels stays the same? They can't change. So what is going on here?

I thought (clearly incorrectly) that when I scaled the viewport, it literraly just chopped off pixels.

Would appreciate if someone could explain how this works.

Edit

Currently, I'm setting my viewport like this:

width = (int) Math.min(deviceWidth, deviceHeight * 1.702127659574468);    
height = (int) Math.min(deviceHeight, deviceWidth / 1.702127659574468);

ratio = width / height;
GLES20.glViewport(offsetX, offsetY, width, height);

Matrix.orthoM(mProjMatrix, 0, -ratio, ratio, -1, 1, 3, 7);

offsetX and offsetY are just so that when there are letterboxes, the viewport is centered.

\$\endgroup\$
6
  • \$\begingroup\$ Did you change your projection matrix as well as the viewport when changing from 1600 to 1200 vertical pixels? (you need to) \$\endgroup\$
    – bcrist
    Jun 20, 2014 at 2:12
  • \$\begingroup\$ Hi @bcrist, please see my edit to show how I am setting my viewport. Everything I draw that has an equal number of pixels (say 50 x 50, or 100 x 100) is drawing 'stretched' - Any ideas?! \$\endgroup\$ Jun 20, 2014 at 20:05
  • \$\begingroup\$ BTW, if you use glScissor instead of glViewport, it will simply crop pixels off without changing where anything is rendered. glViewport behaves more like resizing the original image, rather than cropping it; that's why it squishes your box. \$\endgroup\$ Jun 20, 2014 at 20:09
  • \$\begingroup\$ Thanks @NathanReed, I'm still really confused. What I have is this: A viewport that takes the whole screen up. A scissor box that is scaled to keep the ratio of the original device (so I can display the game in the scissor box and draw stuff outside of this for filler), everything is appearing stretched (vertially), so longer than it is wide. What am I doing wrong? \$\endgroup\$ Jun 20, 2014 at 20:25
  • \$\begingroup\$ Hmm. The scissor rect shouldn't be changing anything about the aspect ratio or scaling of the image. If it looks correct without the scissor, then just enabling the scissor - keeping the same viewport, projection matrix, etc. - should simply crop the image. \$\endgroup\$ Jun 21, 2014 at 4:05

3 Answers 3

13
\$\begingroup\$

To understand what's going on, you have to understand the rendering pipeline:

Your geometry (the quad) is initially defined in world space, think of this as some global coordinate system. Inside of the vertex shader those are transformed to normalised device coordinates (NDC), a virtual coordinate system defined so, that everything from -1 to 1 will get drawn to the screen. Note, that NDC is ranging from -1 to 1 in X and Y, and it's totally independent of the devices aspect ratio and resolution. This transformation from world space to NDC is done by the model, view and projection matrix (in a simple form, just one matrix for everything or the geometry was even defined in NDC to begin with!).

The rasterisation unit needs to know where and how large it should raster and this is what you define with the glViewport call: where to start are the first two parameters, the size are the second two. The hardware will then convert from NDC to pixel coordinates by a simple scale and shift - and this is what you see in your example: a scale in the Y axis.

So to make sure your quad gets rendered in the correct aspect ratio, you also need to adjust the projection matrix to include the expected aspect ratio of the glViewport call.

\$\endgroup\$
3
  • \$\begingroup\$ Hi @Robert, many thanks for this, I'm having some difficulty trying to get this implemented, could you please give an example of setting up the projection matrix in you answer? Thanks! \$\endgroup\$ Jun 20, 2014 at 14:59
  • 4
    \$\begingroup\$ Hello user22241, there are no OpenGL commands to do this for you, you need to define the matrix on your own (a 4 by 4 array of floats) and provide them to your vertex shader as a uniform. What projection you need (perspective, orthogonal) depends on what you want to do. If the mathematical concepts of 3D projections are new to you, you might want to look up OpenGL tutorials teaching the core profile which works the same as OpenGL ES in this regard. \$\endgroup\$
    – Robert
    Jun 21, 2014 at 11:41
  • \$\begingroup\$ @BungleBonce while there're no OpenGL commands for this in core profile, you can always look at the documentation of obsolete commands like glFrustum, which does list the exact matrix which is constructed by these functions. Other common projection matrix you may want was created by glOrtho. While all these commands are obsolete, docs on them are an excellent source of some pre-derived maths. \$\endgroup\$
    – Ruslan
    Sep 4, 2016 at 7:52
0
\$\begingroup\$

In your call to glViewport you are specifying less pixels in width and height. Your projection matrix is defined in terms of pixels. That's why you will have to calculate a new projection matrix after a glViewport call.

\$\endgroup\$
2
  • \$\begingroup\$ Hi @bogglez, thanks for your answer. I've edited my question to show (at the end) what code I'm using for my viewport. All I'm using is glViewPort - is this incorrect way to do this? Please could you show an example of what you mean within your answer? Thanks. \$\endgroup\$ Jun 20, 2014 at 11:05
  • \$\begingroup\$ Read this: songho.ca/opengl/gl_projectionmatrix.html For 2D you want an orthogonal projection matrix, for 3D you probably want a perspective matrix. If you use modern OpenGL, you will supply a matrix to a shader. If you use deprecated OpenGL with the fixed graphics pipeline, you will use functions such as glLoadMatrix, glFrustum, etc. to calculate the projection matrix. The utility library GLU has a function gluPerspective (opengl.org/sdk/docs/man2/xhtml/gluPerspective.xml) and gluOrtho2D (opengl.org/sdk/docs/man2/xhtml/gluOrtho2D.xml) to help you with that. \$\endgroup\$
    – bogglez
    Jun 21, 2014 at 10:49
0
\$\begingroup\$

For first picture:

  1. We are creating projection matrix. For example, the parameters used are: Left1, Right1, Bottom1, Top1, Near1, Far1. Here, Aspect_Ratio_1A = (Right1 - Left1) / (Top1 - Bottom1);

  2. Then we are doing a view port transformation. For example, the parameters used are: Width1, Height1. (I am not mentioning shift parameters for simplicity). Here, Aspect_Ratio_1B = Width1 / Height1;

For second picture:

  1. We are creating same projection matrix with same parameters as before. So: Aspect_Ratio_2A = Aspect_Ratio_1A;

  2. This time the view port transformation is having different parameters: Width2 and Height2. Here, Aspect_Ratio_2B = Width2 / Height2.

We note that Aspect_Ratio_1B and Aspect_Ratio_2B are different. Specifically they are:

Aspect_Ratio_1B = 2560 / 1600 = 1.6 Aspect_Ratio_2B = 2560 / 1200 = 2.13

So, if we summarize what we are doing for the second case is:

We are projecting the image onto a plane with Aspect_Ratio_2A and before showing it to user, we are scaling it to Aspect_Ratio_2B. Other users are asking us to correct our projection matrix in such a way that the Aspect_Ratio_2A = Aspect_Ratio_2B.

We can also read Robert's comments a couple of times to have a better understanding.

Also, I cannot agree that the red quad in the second picture has a 200 by 200 pixel size when drawn on the screen. Since we know, a pixel is a square, if the quad has one of the sides longer than the other, the longer side takes more pixels to be drawn for sure. I, personally, do not know what logging is, but may be it doesn't return screen pixel size.

\$\endgroup\$

You must log in to answer this question.

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