1
votes

I'm developing an Android app with OpenGL ES 2.0. It consists of a 2D HUD overlaid on a 3D scene. Everything is fine with the 3D part, but I cannot figure out how to position the origin at the top-left corner of the screen (with the y-axis pointing down) when drawing in 2D. Right now I have the origin at the center of the screen and I am just subtracting half the width and height from the coordinates as I draw. (I know this is a hacky solution and I want to fix this!)

Here is the relevant code:

GLES20.glViewport(0, 0, surfaceWidth, surfaceHeight);
Matrix.orthoM(projectionMatrix, 0, -width / 2, width / 2, height / 2, -height / 2, -1, 1);
Matrix.setLookAtM(viewMatrix, 0, 0, 0, 2f, 0, 0, 0, 0, -1, 0);
Matrix.multiplyMM(mvpMatrix, 0, projectionMatrix, 0, viewMatrix, 0);

Here are the relevant shaders:

private final static String vertexShaderCode =
                "uniform mat4 uMVPMatrix;" +
                "attribute vec2 aTexCoordinate;" +
                "varying vec2 vTexCoordinate;" +
                "attribute vec4 aPosition;" +
                "attribute vec4 aColor;" +
                "varying vec4 vColor;" +
                "void main() {" +
                "  vColor = aColor;" +
                "  vTexCoordinate = aTexCoordinate;" +
                "  gl_Position = aPosition * uMVPMatrix;" +
                "}";

private final static String fragmentShaderCode =
                "precision mediump float;" +
                "uniform sampler2D uTexture;" +
                "varying vec2 vTexCoordinate;" +
                "varying vec4 vColor;" +
                "void main() {" +
                "  gl_FragColor = vColor * texture2D(uTexture, vTexCoordinate);" +
                "}";

What I have tried:

  • I have tried using this line instead:

    Matrix.orthoM(projectionMatrix, 0, 0, width, height, 0, -1, 1);
    
  • I have tried positioning the origin at the bottom-left, not the top-left (I shouldn't be doing this but I tried anyway).

  • I have tried switching the order of aPosition and uMVPMatrix in the second-to-last line of the vertex shader.

No combination of these three changes solved the problem for me (the 2D objects do not render).

The other thing I tried was setting the origin to be one pixel off center, then two, then three, etc. The images kept getting skewed the more I moved off center, eventually becoming too thin to be visible -- but that still doesn't tell me how to fix this.

So again, the question is: how do I position the origin at the top-left in OpenGL ES 2.0?

P.S. My contract (first job!) ends on July 29 so I need an answer pretty soon; I don't want to leave the next people with a hacky fix like this.

1

1 Answers

1
votes

Is not a rule have only one mvpMatrix in OpenGL. It is good because you can customize a lot of views.

If all of your application is based in mvpMatrix that you showed here, I recommend you create another mvpMatrix for the proposal that you are asking: Make the center of the view in top-left corner.

So, the answer is simple, lets recreate every matrix. At first, viewMatrix:

Matrix.setLookAtM(viewMatrix, 0, 
     0, 0,  6,   //eye
     0, 0,  0,   //center
     0, 1,  0);  //UP *remove your -1

The magic is below. You have to invert bottom and top:

float screenRatio = (float) width / height;
Matrix.orthoM(projectionMatrix, 0, 
        0, screenRatio, // left, right
        1, 0,           // bottom, top        <----Solution is invert bottom with top
        -1, 10);        // near, far

And finally:

    Matrix.multiplyMM(
            viewProjectionMatrix, 0, 
            projectionMatrix, 0, 
            viewMatrix, 0);

P.S: don't need to change GLES20.glViewport(0, 0, surfaceWidth, surfaceHeight);

OpenGL have a lot of solution for this problem, because you can handle all matrix as you can.

Another solution is to customize your own matrix, without using Matrix.orthoM(..) or Matrix.frustum(..):

public static final float[] topLeftMatrix(int width, int height){
    float matrix[] = new float[16];

    // This is inverse of viewPort matrix
    matrix[0] = 2.0f/width;
    matrix[1] = 0;
    matrix[2] = 0;
    matrix[3] = 0;

    matrix[4] = 0;
    matrix[5] = -2.0f/height;
    matrix[6] = 0;
    matrix[7] = 0;

    matrix[8] = 0;
    matrix[9] = 0;
    matrix[10] = 1;
    matrix[11] = 0;

    matrix[12] = -1;
    matrix[13] = 1;
    matrix[14] = 0;
    matrix[15] = 1;

    return matrix;
}

This matrix above is good for handling objects over pixels (like user-interface).

For full understanding, I recommend you read this article: 3D Graphics with OpenGL. It helped me a lot to understand the opengl magic.

P.S: there is an error in your shader. The correct way to multiply matrix4x4 by a vector 4x1 is:

gl_Position = uMVPMatrix * aPosition;