Lesson 18: Normalizing Normals

Earlier, I hinted that calling glEnable(GL_NORMALIZE) slows things down. Indeed it does, as it requires OpenGL to rescale all of the normal vectors to have a magnitude of 1. OpenGL needs the normals to have a magnitude of 1 to get lighting right. We'll see how we can eliminate this call.

As usual, we'll be optimizing our spinning cube program. In fact, all of the normals in the spinning cube program already have a magnitude of 1. So if we just comment out the call to glEnable(GL_NORMALIZE), the program will work the same as before. The lighting looks just fine.

A problem arises if we have any calls to glScalef. In the spinning cube program, we'll change the cube to be 2 units from the camera, rather than 20 units, and add a call to glScalef(0.1f, 0.1f, 0.1f). If you do this, the lighting looks messed up, as shown below:

Incorrect lighting

Why does this happen? Well, when you give OpenGL a normal vector, it doesn't directly use that vector. If, for example, there were any rotations, OpenGL has to rotate the vector by the same amount. So, normal vectors are affected by transformations. However, we can't apply the same transformations to normal vectors that we do to points. For example, it wouldn't make sense to translate a normal vector. What OpenGL actually does is it transforms normals using the "inverse transpose" transformation.

Without worrying about what the inverse transpose transformation is exactly, just know that whenever we scale by a certain amount, OpenGL will consequently scale our normals by the reciprocal of that amount. So, in this case, OpenGL scales our normals to have a magnitude of 10. We need to fix that.

One way to fix that, the way used in the source code for this lesson, is to scale our normals by a factor 0.1. If we change the normals in the program to have a magnitude of 0.1, OpenGL will automatically scale them to have a magnitude of 1, even without a call to glEnable(GL_NORMALIZE). So, if we run the program, the lighting is fixed.

A second way to fix the scaling issue is to just not call glScalef, but rather to manually scale the vertices. In this case, we can just leave the normals the way they originally were.

If we ever scale by a different amount in the three directions, then this second technique still works. However, the first technique really won't work. It's not obvious how to alter the normal vectors to fix the scaling problem.

So that's how you can eliminate the call to glEnable(GL_NORMALIZE) to speed up an OpenGL program.

Next is "Lesson 19: Mipmapping".