1
votes

I'm using OpenGL ES 2.0 to render only 2D shapes, since it's OpenGL ES 2.0 I have to create my own transformaton matrices.

Being only 2D shapes I'd like to keep all my matrices as 3x3 and my vectors with only 2 coordinates.

I also need a projection matrix as I would like to have my coordinates mapped to the screen, I don't want to specify positions from [-1.0; 1.0].

From what I've seen people still use 4x4 matrices for 2D and vectors with z set to 1, and an orthographic projection matrix.

My question is: How can I do all my transformations and the projection with only 3x3 matrices? I have all but the projection matrix set up, but the translation doesn't work.

So far I've done my translation matrix like this:

m[0][0] = 1;  m[0][1] = 0;  m[0][2] = tx;
m[1][0] = 0;  m[1][2] = 1;  m[1][2] = ty;
m[2][0] = 0;  m[2][3] = 0;  m[2][2] = 1;

And from what I can tell this should work, even with NDC coordinates, when multiplied by a vector it should result in

x+tx y+ty 1.0

But when I try to translate this triangle that goes from -1 to 1 with tx = 2f it gets squashed and doesn't move.

Before translation: enter image description here

After translation: enter image description here

for reference here is how I draw the triangle:

    GLES20.glUniformMatrix3fv(shader.GetUniform("u_transform_mat"), 1, false, Transform.GetTransformationMatrix().ToFloatBuffer()); 

    GLES20.glEnableVertexAttribArray(shader.GetAttribute("a_vert_position"));
    GLES20.glEnableVertexAttribArray(shader.GetAttribute("a_vert_color"));

    GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, VBO[0]);

    GLES20.glVertexAttribPointer(shader.GetAttribute("a_vert_position"), 2, GLES20.GL_FLOAT, false, Vertex.SIZE, 0);
    GLES20.glVertexAttribPointer(shader.GetAttribute("a_vert_color"), 4, GLES20.GL_FLOAT, false, Vertex.SIZE, Vertex.POS_SIZE);

    GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, IBO[0]);    

    GLES20.glDrawElements(primitive, size, GLES20.GL_UNSIGNED_INT, 0);      

    GLES20.glDisableVertexAttribArray(shader.GetAttribute("a_vert_color"));
    GLES20.glDisableVertexAttribArray(shader.GetAttribute("a_vert_position"));

and my vertex shader

uniform mat3 u_transform_mat;   

attribute vec3 a_vert_position;  
attribute vec4 a_vert_color;     

varying vec4 v_vert_color;       

void main()                 
{                           
   v_vert_color = a_vert_color;       

   gl_Position = vec4(u_transform_mat * a_vert_position, 1.0);
}                                 
1

1 Answers

2
votes

You can certainly use 3x3 matrices to apply affine transformations in 2D. You just have to be careful that you're consistent about it.

From a look at the code fragments you posted, there are two things that look problematic:

Matrices need to be column major

OpenGL expects matrices to be stored in column major order. From the way you build your matrix:

m[0][0] = 1;  m[0][1] = 0;  m[0][2] = tx;
m[1][0] = 0;  m[1][2] = 1;  m[1][2] = ty;
m[2][0] = 0;  m[2][3] = 0;  m[2][2] = 1;

this places the translation components where they would have to be for a row major matrix. It also assigns m[1][2] twice, and assigns m[2][3], which is out of range for a 3x3 matrix. In turn, it never assigns m[1][1] and m[2][1].

If you want to keep writing your matrices this way (which I agree is more readable), you can swap around the indices:

m[0][0] = 1;  m[1][0] = 0;  m[2][0] = tx;
m[0][1] = 0;  m[1][1] = 1;  m[2][1] = ty;
m[0][2] = 0;  m[1][2] = 0;  m[2][2] = 1;

3rd vector component needs to be 1.0

You're specifying two components for your vertex position attribute, which makes sense:

GLES20.glVertexAttribPointer(shader.GetAttribute("a_vert_position"), 2,
                             GLES20.GL_FLOAT, false, Vertex.SIZE, 0);

But then in the shader, you have this:

attribute vec3 a_vert_position;

Declaring the attribute variable larger than the number of components passed into the shader is perfectly legal. Unspecified components are filled with 0.0 for components y and z, and 1.0 for w. Therefore, in your shader code, a_vert_position.z will be filled with 0.0. If you then multiply it with the 3x3 transformation matrix:

u_transform_mat * a_vert_position

This means that the translation part will not be applied. Just like the 4th component needs to 1.0 when applying 4x4 matrices to 4 component vectors for an affine transformation in 3D space, the 3rd component needs to be 1.0 when applying 3x3 matrices to 3 component vectors for an affine transformation in 2D space.

The easiest way to fix this is to declare the attribute as vec2, and add 1.0 as the 3rd component when applying the transformation matrix:

attribute vec2 a_vert_position;
...
   gl_Position = vec4(u_transform_mat * vec3(a_vert_position, 1.0), 1.0);