I’m confused about normal vectors. The OpenGL SuperBible claims that normal vectors are scaled by glScale transformations. So does a glScale of the MODEL_MATRIX scale the normal vectors? How about scaling the PROJECTION_MATRIX - the normals are effected?
I don’t quite see the underlying justification for this, so are there other operations that [adversely] affect normal vector?
Normals are transformed by the inverse transpose of the current modelview matrix. So anything that affects the modelview matrix (i.e. all the matrix commands) will likewise affect your normals.
If you glEnable(GL_NORMALIZE), normals will be normalized after transformation.
If you glEnable(GL_RESCALE_NORMAL), normals will again be normalized after transformation, but only if there’s a uniform scale on the modelview (i.e. an equal scale for all 3 axes). This is intended as a possible optimization over the full normalize above, but in practice you may not see much of a difference.
The details can be found in the specification:
P.S. The justification in transforming normals by the modelview is simple: you want your model’s normals to move with your model (if you want proper lighting, that is)
Thanks for pointing me to the spec. However, I’m looking at the “Normal Transformation” section, and have little confidence that I can digest the implications of the matrix math, so, as a clarification…
Manipulations of the modelview matrix will affect normal vectors, but changes to the projection matrix will not affect them.
Is that correct?
As to the justification…
I can understand translations and rotations being applied to the normals (as you said, to track with the model). But I fail to see why scaling my model (uniformly in 3D) should invalidate my normals – they should continue to point in the same direction and remain unit length. Speaking critically, GL_RESCALE_NORMAL feels like a patch to an underlying problem: the assumption that a scale operation should affect normal vectors. It seems to me that by default a scaling operation should not apply to normals [assuming the uniform 3D case], unless a state like GL_SCALE_NORMALS is set [declaring the non-uniform case].
Having said that, I’m more interested in how to get it to work, than how I think it should work. So I’m quite willing to defer to an expert, accept that it works as you say, and thank you for your advise.
This neophyte thanks you for your comments. They were very helpful!
Is that correct?
Yes, I couldn’t have said it better myself.
But I fail to see why scaling my model (uniformly in 3D) should invalidate my normals
It’s because M^(-T) != M, in the general case. Since normals are transformed by the inverse transpose, any changes to M can mean changes to M^(-T), even a uniform scaling.
Without getting too mired in the math, look at some uniform scale on the identity matrix, s I. The inverse of this matrix is 1/s I; the transpose of this inverse is 1/s I. So you’ll still need a re-normalization or a re-scale on a normal multiplied by this matrix.
Rescale is just an optimization, in the finest spirit of optimizations
Thanks for enlightening me. I truly appreciate it.
simplex: you can’t move a normal. it is vector (just direction). so when you move it, it is still pointing to the same direction.
fred9812: if you uniformly scale a vector, then its lenght is different. and if you non-uniformly scale a vector, then its direction can change.
you can’t move a normal.
Sure you can “Move” is not really a mathematical term. Here I simply meant to convey a change of posture, rather than position, in the same sense that moving your arm generally implies a rotation, rather than a translation. In any case, “move” is not to be confused with “translation”. Only the changes in the model that would impart a corresponding change in its normals are relevant here.
if you uniformly scale a vector, then its lenght is different.
Obviously, but Fred wasn’t inquiring about simply scaling a vector, but rather the effects of scale and GL_NORMALIZE/GL_RESCALE_NORMAL, as they relate to the OpenGL normal matrix and (unit) vectors.
With GL_RESCALE_NORMAL, it is assumed that the incoming vertex normals are already normalized, if that wasn’t clear. It’s only after the normal matrix transformation that the vertex normal is rescaled, thereby recovering its original (unit) length.
By the way, there’s a great little derivation of the normal matrix in the appendix of the online OpenGL Programming Guide:
The OpenGL transformation matrices can be found there, too.
But I fail to see why scaling my model (uniformly in 3D) should invalidate my normals – they should continue to point in the same direction and remain unit length.
Well it is simple. Normals are just vectors, and they get scaled the same as other vectors, and won’t be unit length after a rescale: you get white wash lighting on the model. Why it is that so ? Because in the past, keeping normals as unit length was costly, and done only when user requested it, that is the meaning of glEnable(GL_NORMALIZE).
It was much faster to use glEnable(GL_RESCALE_NORMAL) when you could accept the optimization.
Nowadays, it is merely a way to puzzle beginners I think …
PS: and the real-use cases where you want to non-uniformly scale a model are not very frequent.
Normals are just vectors, and they get scaled the same as other vectors, and won’t be unit length after a rescale: you get white wash lighting on the model.
They will be unit length if they were unit vectors to begin with; and that being the case, you will not get white wash lighting on the model. This is the point of the rescale! Many applications supply normals that are already normalized, so that with uniform scaling, rescale will work quite nicely.
I’ve never done a performance comparison between rescale and normalize. The results might prove interesting. Then again, it could very well be the the case that the results will not prove interesting.