0
votes

I am following the tutorials at LearnOpenGL.com and I am confused about the order of Matrices.

The Transformations chapter tells:

Matrix multiplication is not commutative, which means their order is important. When multiplying matrices the right-most matrix is first multiplied with the vector so you should read the multiplications from right to left. It is advised to first do scaling operations, then rotations and lastly translations when combining matrices otherwise they might (negatively) affect each other. For example, if you would first do a translation and then scale, the translation vector would also scale!

So If I am not wrong, the order is Translate * Rotate * Scale * vector_to_transform.

But immediately in the next Chapter, when calculating the LookAt matrix, the multiplication order is flipped. Here is the code snippet from the website:

// Custom implementation of the LookAt function
glm::mat4 calculate_lookAt_matrix(glm::vec3 position, glm::vec3 target, glm::vec3 worldUp)
{
    // 1. Position = known
    // 2. Calculate cameraDirection
    glm::vec3 zaxis = glm::normalize(position - target);
    // 3. Get positive right axis vector
    glm::vec3 xaxis = glm::normalize(glm::cross(glm::normalize(worldUp), zaxis));
    // 4. Calculate camera up vector
    glm::vec3 yaxis = glm::cross(zaxis, xaxis);

    // Create translation and rotation matrix
    // In glm we access elements as mat[col][row] due to column-major layout
    glm::mat4 translation = glm::mat4(1.0f); // Identity matrix by default
    translation[3][0] = -position.x; // Third column, first row
    translation[3][1] = -position.y;
    translation[3][2] = -position.z;
    glm::mat4 rotation = glm::mat4(1.0f);
    rotation[0][0] = xaxis.x; // First column, first row
    rotation[1][0] = xaxis.y;
    rotation[2][0] = xaxis.z;
    rotation[0][1] = yaxis.x; // First column, second row
    rotation[1][1] = yaxis.y;
    rotation[2][1] = yaxis.z;
    rotation[0][2] = zaxis.x; // First column, third row
    rotation[1][2] = zaxis.y;
    rotation[2][2] = zaxis.z; 

    // Return lookAt matrix as combination of translation and rotation matrix
    return rotation * translation; // Remember to read from right to left (first translation then rotation)
}

At the end of the code snippet, the matrix is calculated as rotation * translation, even though the matrix is going to be multiplied as,

gl_position = projection * lookAt * model * vec4(vertexPosition, 1.0);

as Column-major matrices must be pre-multiplied to the vector.

Please help me understand this.

1
There is no correct order - you always apply matrices in the order that is most suited for your problem (see e.g. one of my previous answers). The LookAt matrix is a bit special in that it is somewhat inverse. That can be a reason why the code uses rotation * translation. But it always depends on how you derive the formula. You can also use translation * rotation if you calculate the translation a bit differently. - Nico Schertler
@NicoSchertler So it's just the View Matrix that's a special case? And by your answer, the final result should be the same. But I get a different result when I flip the order. - Vignesh Gunasekaran
You need a different translation matrix if you want to flip the order. - Nico Schertler
Okay! I think I got the gist. I missed an important detail: the View matrix should not scale stuff. So it's just a Matrix to rotate and translate the model matrix. To do so, the matrix should be combined in a particular way. As you mentioned, the view matrix is kind of a special case. See the Combining Matrices section of Transformations chapter. - Vignesh Gunasekaran

1 Answers

0
votes

Okay! After reading through the entire chapter again, I missed a crucial detail. The View matrix usually does not scale the objects. It's just a matrix to rotate and translate the model matrix in such a way to simulate an eye into the world. This chapter of LearnOpenGL.com has a block explaining about combining matrices and it shows how to combine a translate and rotate matric and it's how the lookAt function is implemented.