In the last section, we saw that our computation of the cosine of the angle of incidence has certain requirements. Namely, that the two vectors involved, the surface normal and the light direction, are of unit length. The light direction can be assumed to be of unit length, since it is passed directly as a uniform.

The surface normal can also be assumed to be of unit length.
*Initially.* However, the normal undergoes a transformation by an
arbitrary matrix; there is no guarantee that this transformation will not apply scaling
or other transformations to the vector that will result in a non-unit vector.

Of course, it is easy enough to correct this. The GLSL function
`normalize`

will return a vector that is of unit length without
changing the direction of the input vector.

And while mathematically this would function, geometrically, it would be nonsense. For example, consider a 2D circle. We can apply a non-uniform scale (different scales in different axes) to the positions on this circle that will transform it into an ellipse:

This is all well and good, but consider the normals in this transformation:

The ellipse in the middle has the normals that you would expect if you transformed the
normals from the circle by the same matrix the circle was transformed by. They may be
unit length, but they no longer reflect the *shape* of the ellipse.
The ellipse on the right has normals that reflect the actual shape.

It turns out that, what you really want to do is transform the normals with the same
rotations as the positions, but invert the scales. That is, a scale of 0.5 along the X
axis will shrink positions in that axis by half. For the surface normals, you want to
*double* the X value of the normals, then normalize the
result.

This is easy if you have a simple matrix. But more complicated matrices, composed from multiple successive rotations, scales, and other operations, are not so easy to compute.

Instead, what we must do is compute something called the *inverse
transpose* of the matrix in question. This means we first compute the
inverse matrix, then compute the *transpose* of that matrix. The
transpose of a matrix is simply the same matrix flipped along the diagonal. The columns
of the original matrix are the rows of its transpose. That is:

So how does this inverse transpose help us?

Remember: what we want is to invert the scales of our matrix without affecting the rotational characteristics of our matrix. Given a 3x3 matrix M that is composed of only rotation and scale transformations, we can re-express this matrix as follows:

That is, the matrix can be expressed as doing a rotation into a space, followed by a
single scale transformation, followed by another rotation. We can do this
*regardless* of how many scale and rotation matrices were used to
build M. That is, M could be the result of twenty rotation and scale matrices, but all
of those can be extracted into two rotations with a scale inbetween.^{[4]}

Recall that what we want to do is invert the scales in our transformation. Where we scale by 0.4 in the original, we want to scale by 2.5 in the inverse. The inverse matrix of a pure scale matrix is a matrix with each of the scaling components inverted. Therefore, we can express the matrix that we actually want as this:

An interesting fact about pure-rotation matrices: the inverse of any rotation matrix
*is* its transpose. Also, taking the inverse of a matrix twice
results in the original matrix. Therefore, you can express any pure-rotation matrix as
the inverse transpose of itself, without affecting the matrix. Since the inverse is its
transpose, and doing a transpose twice on a matrix does not change its value, the
inverse-transpose of a rotation matrix is a no-op.

Also, since the values in pure-scale matrices are along the diagonal, a transpose operation on scale matrices does nothing. With these two facts in hand, we can re-express the matrix we want to compute as:

Using matrix algebra, we can factor the transposes out, but doing so requires reversing the order of the matrix multiplication:

Similar, we can factor out the inverse operations, but this requires reversing the order again:

Thus, the inverse-transpose solves our problem. And both GLM and GLSL have nice
functions that can do these operations for us. Though really, if you can avoid doing an
inverse-transpose in GLSL, you are *strongly* advised to do so; this
is not a trivial computation.

We do this in the Scale and Lighting tutorial. It controls mostly the same as the previous tutorial, with a few exceptions. Pressing the space bar will toggle between a regular cylinder and a scaled one. The “T” key will toggle between properly using the inverse-transpose (the default) and not using the inverse transpose. The rendering code for the cylinder is as follows:

**Example 9.5. Lighting with Proper Normal Transform**

glutil::PushStack push(modelMatrix); modelMatrix.ApplyMatrix(g_objtPole.CalcMatrix()); if(g_bScaleCyl) { modelMatrix.Scale(1.0f, 1.0f, 0.2f); } glUseProgram(g_VertexDiffuseColor.theProgram); glUniformMatrix4fv(g_VertexDiffuseColor.modelToCameraMatrixUnif, 1, GL_FALSE, glm::value_ptr(modelMatrix.Top())); glm::mat3 normMatrix(modelMatrix.Top()); if(g_bDoInvTranspose) { normMatrix = glm::transpose(glm::inverse(normMatrix)); } glUniformMatrix3fv(g_VertexDiffuseColor.normalModelToCameraMatrixUnif, 1, GL_FALSE, glm::value_ptr(normMatrix)); glUniform4f(g_VertexDiffuseColor.lightIntensityUnif, 1.0f, 1.0f, 1.0f, 1.0f); g_pCylinderMesh->Render("lit-color"); glUseProgram(0);

It's pretty self-explanatory.

One more thing to note before we move on. Doing the inverse-transpose is only really
necessary if you are using a *non-uniform* scale. In practice, it's
actually somewhat rare to use this kind of scale factor. We do it in these tutorials, so
that it is easier to build models from simple geometric components. But when you have an
actual modeller creating objects for a specific purpose, non-uniform scales generally
are not used. At least, not in the output mesh. It's better to just get the modeller to
adjust the model as needed in their modelling application.

Uniform scales are more commonly used. So you still need to normalize the normal after transforming it with the model-to-camera matrix, even if you are not using the inverse-transpose.

^{[4] }We will skip over deriving how exactly this is true. If you are interested,
search for “Singular Value Decomposition”. But be warned: it is
math-heavy.