Incorrect Normal Matrix

| | August 4, 2015

In my game engine I’m passing a normal matrix to my shaders like so:

Matrix3f normalMatrix = modelMatrix.invert().transpose();

However this is causing lighting to rotate with my object, producing a weird effect.

As I’m not performing any non-uniform scaling, I tried substituting normalMatrix for mat3(modelMatrix), and that worked fine.

Also doing the transpose inverse on the GPU using transpose(inverse(modelMatrix)) worked fine.

This has lead me to believe there is a bug in my invert() and transpose() functions.

According to some math websites, when the modelMatrix lacks any non-uniform scaling, calling inverse().transpose() on it will produce the exact same matrix.

However in my code the output is far from the same:

modelMatrix


0.25    0      0      2353.3474
0       0.25   0      0
0       0      0.25   51.86973
0       0      0      1


normalMatrix = new Matrix3f(modelMatrix).invert().transpose()

4.0 0.0 0.0 
0.0 4.0 0.0 
0.0 0.0 4.0 

Would anyone be able to shed some light on this issue…
(I don’t want to just stick with modelMatrix because I do want to support non-uniform scaling in the future)

Here are some screenshots describing the issue:

These are two frames of my planet as it rotates. The light source appears to be rotating with it (it shouldn’t be). Using the modelMatrix instead of normalMatrix solves the issue.
These are two frames of my planet as it rotates
The light source appears to be rotating with it (it shouldn't be). Using the <code>modelMatrix</code> instead of <code>normalMatrix</code> solves the issue

One Response to “Incorrect Normal Matrix”

  1. According to some math websites, when the modelMatrix lacks any
    non-uniform scaling, calling inverse().transpose() on it will produce
    the exact same matrix.

    This is slightly incorrect. Let’s define N as M.inverse().transpose(). Then N = M is true only if M is a rotation matrix. If M has uniform scaling, the resulting matrix N will be different. However when transforming vertices with matrix N, the resulting vectors will have the same direction as with M but different magnitude. If you normalize the vectors after the transformation, the results will indeed be the same. With non-uniform scaling this is no longer enough. As a summary:

    • If all you have is rotation, you don’t need a separate normal matrix.
    • If you have rotation and uniform scaling, you don’t need the normal matrix either, but you need to normalize the transformed normals.
    • If you have non-uniform scaling / shearing, you need to calculate normal matrix with the transpose of the inverse.

    The output from your normal matrix calculation looks correct.

    Also doing the transpose inverse on the GPU using
    transpose(inverse(normalMatrix)) worked fine.

    Is this a typo? You are using the normal matrix calculation for the normalMatrix, which results in the original matrix.

    Based on the given information it’s hard to guess what is wrong. Note that normal matrix is 3×3 whereas your model matrix is 4×4. Try to reduce the model matrix to 3×3 as the first thing by discarding the w components. It’s also possible that you are passing the normal matrix incorrectly to the GPU. You might need to add more information / screenshots to the question.

Leave a Reply