1
votes

I am learning OpenGL ES 2.0, without ever having learned OpenGL or OpenGL ES 1.x.

I'm applying non-uniform scaling to my modelViewMatrix, so the tutorials tell me that I need to take special steps to compute a normalMatrix. In my application the modelViewMatrix has dimension 4x4.

  1. Some tutorials say that for the normalMatrix I need to simply calculate transpose(inverse(modelViewMatrix)).

  2. Other instructions say that I need to first take the upper left 3x3 sub-matrix of my modelViewMatrix and then compute transpose(inverse(submatrix)).

Is there any difference? Do they lead to the same result?

Right now I'm using method 1, and then in the vertex shader I extract a vec3 after applying the transformation:

vec3 vNormalEyespace = vec3(normalMatrix * vec4(vertexNormal, 1.0));

I am doubting this because I see strange effects in my diffuse lighting. I'm thinking about trying method 2, but the android.opengl.Matrix class does not offer methods for inverting or transposing 3x3 matrices...

My actual code in renderFrame() is as follows:

  final float[] normalMatrix=new float[16];
  final float[] unscaledModelViewMatrix=modelViewMatrix_Vuforia.getData();
  Matrix.invertM(normalMatrix, 0, unscaledModelViewMatrix, 0);
  Matrix.transposeM(normalMatrix, 0, normalMatrix, 0);
  // pass the normalMatrix to the shader
  GLES20.glUniformMatrix4fv(normalMatrixHandleBottle, 1, false, normalMatrix, 0);
1

1 Answers

2
votes

A 3x3 matrix is enough to transform the normals.

The primary purpose of using 4x4 matrices that operate on homogenous coordinates for positions is that they can express translations. A 3x3 matrix applied to a 3-member vector can not express translations. You can easily confirm that because it will always map the origin back to the origin.

Since normals are vectors, and not positions, we specifically do not want to apply the translation part of the modelview matrix to them. They describe directions, and directions do not change when a translation is applied.

So the cleanest approach is to use a 3x3 matrix for normals, and set it to the inverse-transpose of the top-left 3x3 elements of the modelview matrix.

In the case where you only have rotations and uniform scaling in the modelview matrix (which does not apply to your specific situation), people sometimes use the same modelview matrix that they also use for the positions. Which is correct, as long as the translation part is not applied. This can be done by setting the w component of the vector to zero for the multiplication:

vec3 transformedNormal = (modelViewMatrix * vec4(originalNormal, 0.0)).xyz

With the w component being zero, the matrix elements that encode the translation have no effect on the result, and this corresponds to using only the top-left 3x3 part of the matrix. As long as this is the same as the inverse-transpose of the matrix, which is the case for rotations and uniform scaling, this is a valid shortcut.