Help stamp out GL_PROJECTION abuse.
By Steve Baker Home page of this article can be found here.
One of the most common OpenGL programming errors is misuse of the GL_PROJECTION matrix stack. This matrix stack is often perceived as being the correct place to put the position and orientation of the 'camera' - but this is not in fact the case.
Unfortunately, when someone does go and put their camera transform into the GL_PROJECTION matrix (instead of into GL_MODELVIEW where it belongs) - the consequences are rather subtle:
- Lighting: OpenGL has to transform vertex normals into world coordinate space - that is to say WITHOUT the effects of perspective - but WITH the effects of the camera position. Hence, only the GL_MODELVIEW matrix is applied to the normals for lighting. If you put the camera transform into the GL_PROJECTION matrix then your lighting will be wrong. However, some people do their own lighting - and in any case, it can be a subtle error that you might not have noticed previously.
- Fog: OpenGL has to figure out how far each vertex is from the camera. Once again, perspective effects are not relevent to this calculation - so the GL_PROJECTION matrix is not used. If you put the camera transform into the GL_PROJECTION matrix then your fogging will be wrong. Since few people use fog, many people have the error and don't know it.
- TexGen: Since OpenGL uses the eyepoint to figure out some of the TexGen'ed texture coordinates, if the camera position is all mixed up with the projection information then you'll end up with some pretty strange texture coordinates. (Thanks to Brian Sharp at 3Dfx for pointing this one out)
- Z-buffer: I believe (but have no proof) that a screwed up GL_PROJECTION matrix could cause your Z values to be computed strangely. This would likely only manifest itself as a lack of Z precision under some circumstances - so you might not notice - and it might not matter.
So, it's easy to see how someone could have a program that pretty much works correctly - that would appear to fail when the programmer first tries to play with Fog - or first notices a peculiar lighting anomaly. Perhaps one might not notice until switching to a new OpenGL implementation causes the behaviour to break.
Hence, most people are very disbelieving when their problems are diagnosed as projection abuse.
The Golden Rule.
The rule is:
The only functions that should be called when the glMatrixMode is set to GL_PROJECTION are:
- glLoadIdentity - to initialise the stack.
- gluPerspective/glFrustum/glOrtho/gluOrtho2 - to set the appropriate projection onto the stack.
- You *could* use glLoadMatrix to set up your own projection matrix (if you understand the restrictions and consequences) - but I'm told that this can cause problems for some OpenGL implementations which rely on data passed to glFrustum, etc to determine the near and far clip planes.
It is actually possible to store the camera position in the GL_PROJECTION matrix under certain VERY specific circumstances - and if you are VERY familiar with OpenGL and attempting to use it only for rasterization. However, it is pretty much always the case that if you are asking the question then you don't understand enough to deal with the answer. If you don't already know what I mean then don't do it.
OK, So how DO I set up the Camera?
Well, you need to push the inverse of the camera position/orientation transform onto the GL_MODELVIEW stack before you start drawing polygons. If you use gluLookAt to do this, it's fairly painless - but using glRotate/glTranslate is likely to cause problems for all but the simplest camera motion since you want the INVERSE of the camera location.
What I do is to compose the camera matrix by hand, and then invert it before doing a glLoadMatrix. However, that does require a certain confidence with matrix operations that many people seem to lack.