PLEASE NOTE: This post contains incorrect information. Read the entire thread before continuing. ( Authors errata )
Why is my fixed function OpenGL lighting wrong? It’s suprising how often this question comes up on these message boards. When hundreds of students ask the same question again and again, the problem may no longer be with the students, but with the text books and the tutorials. Hopefully this post will explain to the students what’s happening behind the scenes, and how they can fix it. Let’s banish this question from the message boards once and for all.
POSITIONAL LIGHT
According to the OpenGL RedBook, the “location is transformed by the modelview matrix and stored in eye coordinates”. What it doesn’t say is after every transform (eg. translation, rotation), the light position is updated again, which is not what people expect. In complex scenes, people expect the light position to be set once in world coordinates (eg. a street light), but are then suprised when they draw another object (eg. a car), the lighting is incorrect. This is because OpenGL will reposition the light by the car modelview matrix (and this repositioning happens behind your back).
C = camera transform
S = object spatial
MV = CS (modelview matrix)
EL = CL (eye coordinates of light, what we want)
ELgl = MV * L (what OpenGL fixed function does)
Lf = MV(-1) * C * L (this is how we counteract the transform)
Verification: ELgl = MVLf = MVMV(-1)CL = C*L
In code, you’d do something like this EVERY TIME after modifying the modelview matrix:
Matrix4 cam = camera modelview matrix
Matrix4 mv = current modelview matrix
Vector3 light_world = light world position
Vector4 light_fixed = mv.GetInverse() * cam * light_world;
glLightfv(light_index, GL_POSITION, light_fixed);
So, if you have a list of scenes to render, you’d do something like this:
for (i=0; i < number_scenes; i++)
{
scene[i].UpdateSpatial();
for (int j = 0; j < number_lights; j++)
lights[j].Update();
scene[i].Render();
}
DIRECTIONAL_LIGHTS
According to the OpenGL RedBook, the “direction is transformed by the modelview matrix”. This only happens once, when setting the light direction, so you only have to do this once AFTER setting the camera matrix. Eg.
Matrix4 cam = camera transform
Vector4 dir = light direction, make sure 4th component is zero
LoadMatrix(cam);
glLightfv(light_index, GL_POSITION, dir);
for (i=0; i < number_scenes; i++)
{
scene[i].UpdateSpatial();
scene[i].Render();
}
Be aware that the direction vector points TOWARDS the light.
SPOT LIGHTS
The spotlight position adheres to the same rules as Positional lights. You will have to update the spot light position AFTER every change to the current modelview matrix.
According to the RedBook, “the spotlight’s direction is transformed by the modelview matrix just as though it were a normal vector, and the result is stored in eye coordinates”. What it doesn’t mention (but the BlueBook does), is that it only uses the upper leftmost 3x3 portion of the modelview matrix, since we’re only concerned with rotations at this point. Just like with the lights position, we need to reverse the transform which will be performed by OpenGL.
C = camera transform
MV3 = 3x3 upper leftmost of modelview matrix
D = spotlight direction (world)
ED = CD (direction in eye coordinates, what we want)
EDgl = MV3D (what OpenGL fixed function does)
Df = MV3(-1) * C * D (this is how we counteract the transform)
Verification: EDgl = MV3Df = MV3MV3(-1)CD = C*D
In code:
Matrix4 cam = camera transform matrix
Matrix3 mv = upper left 3x3 of modelview matrix (caution - modelview is 4x4)
Vector3 dir_world = desired spotlight direction
Vector3 dir_fixed = mv.GetInverse() * cam * dir_world;
dir_fixed.Normalise();
glLightfv(light_index, GL_SPOT_DIRECTION, dir_fixed);
Be aware that the spotlight direction is FROM the light source.
Just like with Positional lights, you will have to update the light direction after every modelview matrix modification.
for (i=0; i < number_scenes; i++)
{
scene[i].UpdateSpatial();
for (int j = 0; j < number_lights; j++)
lights[j].Update(); // update position and spotlight direction
scene[i].Render();
}
Summary
The above FAQ should help you resolve your light position / direction issues, and your scenes should finally be lit correctly. Also worth mentioning is that with programmable shaders, you generally don’t have to do this, since you control when the light positions and direction are converted to eye coordinates.