# Normal Matrix Optimization

When it is time to render a mesh, we normally render that mesh multiplying all the vertices by a `Model-View-Projection` matrix. This matrix will move, rotate, and scale all the vertices; however, we don't want to do this for the normals associated with those vertices.

To correctly rotate normals we will need another matrix, normally called `Normal Matrix`. This matrix is a 3x3 matrix with a rotation encoded in it, and the short answer on how to get this matrix is to compute the transpose of the inverse of the Model matrix.

``````normal_mat = transpose(inverse(model_mat));
``````

If you want to know more behind the math involved in this computation, please take a look at this fantastic post.

The only problem here is that `inverse(...)` operation is expensive :).

While trying to optimize our normal matrix computation, these are some characteristics that we were required to maintain (and others to take advantage of).

• Our matrices are always orthogonal.
• Our matrices can contain non-uniform scale.

At the same time, our goal is to compute a matrix that will rotate the normal associated with a vertex in the same way the model matrix does, but removing any non-uniform scale and any translation at the same time.

Lets put an example:

Model matrix

`````` X    Y    Z
1.0  0.0  0.0  0.0
0.0  2.0  0.0  0.0
0.0  0.0  1.5  0.0
0.0  0.0  0.0  1.0
``````

Normal matrix (expected)

`````` X    Y    Z
1.0  0.0  0.0  0.0
0.0  0.5  0.0  0.0
0.0  0.0  0.66 0.0
0.0  0.0  0.0  1.0
``````

In the example above we can see how the `Y` and `Z` axis have a non-uniform scale, and if we want to remove that scale, the axis itself will need to multiply by the inverse of the scale.

So, we can create a `Normal Matrix` with this idea in mind:

We extract the axis of the matrix to operate with them.

``````Matrix44 m = model_mat;

vec3 x = vec3(m, m, m);
vec3 y = vec3(m, m, m);
vec3 z = vec3(m, m, m);
``````

We compute the length of those axis.

``````float lx = length(x);
float ly = length(y);
float lz = length(z);
``````

After we have the length, we will normalize them.

``````normalize(x);
normalize(y);
normalize(z);
``````

We multiply them by the inverse of the length(scale) to remove any non-uniform scale on that axis.

``````x = x * (1.0 / lx);
y = y * (1.0 / ly);
z = z * (1.0 / lz);
``````

We create a new Normal Matrix with the axis we have just computed.

``````Matrix33 normal_mat = identity_matrix;

normal_mat = x;
normal_mat = x;
normal_mat = x;

normal_mat = y;
normal_mat = y;
normal_mat = y;

normal_mat = z;
normal_mat = z;
normal_mat = z;
``````

At this point, `normal_mat` will contain a Normal Matrix that will be able to rotate normals and remove the non-uniform scale of them. This will be very important to allow a correct computation of the light.

This way of computing the Normal Matrix was ~55% faster than computing it by `inverse(modela_mat)`.

Posted on
Written by Llorenç Marti
Tagged under c++, 3d engine