OpenGL gotcha: remember light 0

In my experimenting with some simple scene geometry and lighting today, I stumbled into the same pitfall I’ve stumbled into several times before (you’d think I would have learned by now!). While moving around the scene, I found that the lighting on surfaces appeared to change depending on my camera angle — if I looked at a polygon square-on, it seemed much brighter than if I was looking slightly away from it.

There are two common reasons for this problem…

The rookie mistake is to specify your lights before you’ve specified your camera angle. When you specify a light position in OpenGL, it’s modified by the model-view matrix in the same way as geometry. As such, the order for operations should typically be:

  • Reset your matrix (using glLoadIdentity())
  • Specify your camera position/angle (e.g. using gluLookAt())
  • Set your light positions and other parameters (using glLightf*())
  • Render your geometry

In this case, that wasn’t the mistake that I made. Instead, I ran into the easy trap befalling people using light 0 (GL_LIGHT0) as an ambient-only light source. Here’s the code I was using just before rendering my geometry:

glEnable(GL_LIGHT0);
glLight4f(GL_LIGHT0, GL_AMBIENT, 0.2f, 0.2f, 0.2f, 1.0f);

The problem is that light 0 is unique in having a default diffuse setting of fully bright white (1,1,1,1), whereas all other lights have a default diffuse setting of totally dark (0,0,0,1). All lights also have a default position of 0,0,1,0, which would effectively place it as a directional light, facing forward, from just behind the camera. Given that the default setting was obviously in the system before my camera was specified, it had the effect of moving around with my camera, and pointing the same direction.

(The “w” parameter in the light position, which is the 4th component, determines whether the light is directional or whether it’s a point source — values 0 and 1 respectively.)

The Solution

The most obvious and effective solution is quite simple:

glLight4f(GL_LIGHT0, GL_DIFFUSE, 0.0f, 0.0f, 0.0f, 1.0f);

Just add that line after the light is enabled (or perhaps even do it once in your initialisation function if you’d prefer). That prevents light 0 from having any diffuse influence as a point/directional source. If you are specifying specular attributes in your polygon materials, then you’ll need to do the same for light 0’s specular colour, since its default for that is 1,1,1,1 as well.

Implementation Note

On looking back over this post, I realise I forgot to mention something. The OpenGL function “glLight4f” doesn’t actually exist. I had defined that as a wrapper function in my code so I didn’t have to keep constructing and passing arrays to “glLightfv”. It’s worth doing, as it saves lots of hassle sometimes! 🙂

Leave a Reply

Your email address will not be published. Required fields are marked *