Reputation: 151
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:
After translation:
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);
}
Upvotes: 1
Views: 825
Reputation: 54592
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:
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;
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);
Upvotes: 2