Basic OpenGL Lighting.

by Steve Baker

Introduction.

Many people starting out with OpenGL are confused by the way that OpenGL's built-in lighting works - and consequently how colour functions. I hope to be able to clear up some of the confusion. What is needed to explain this clearly is a flow chart:

Lighting ENABLED or DISABLED?

The first - and most basic - decision is whether to enable lighting or not.

   glEnable ( GL_LIGHTING ) ;

...or...

   glDisable ( GL_LIGHTING ) ;

If it's disabled then all polygons, lines and points will be coloured according to the setting of the various forms of the glColor command. Those colours will be carried forward without any change other than is imparted by texture or fog if those are also enabled. Hence:

   glColor3f ( 1.0f, 0.0f, 0.0f ) ;

...gets you a pure red triangle no matter how it is positioned relative to the light source(s).

With GL_LIGHTING enabled, we need to specify more about the surface than just it's colour - we also need to know how shiney it is, whether it glows in the dark and whether it scatters light uniformly or in a more directional manner.

The idea is that OpenGL switches over to using the current settings of the current 'material' instead of the simplistic idea of a polygon 'colour' that is sufficient when lighting is disabled. We shall soon see that this is an over-simplistic explanation - but keep it firmly in mind.

glMaterial and glLight

The OpenGL light model presumes that the light that reaches your eye from the polygon surface arrives by four different mechanisms: So, there are three light colours for each light - Ambient, Diffuse and Specular (set with glLight) and four for each surface (set with glMaterial). All OpenGL implementations support at least eight light sources - and the glMaterial can be changed at will for each polygon (although there are typically large time penalties for doing that - so we'd like to minimise the number of changes).

The final polygon colour is the sum of all four light components, each of which is formed by multiplying the glMaterial colour by the glLight colour (modified by the directionality in the case of Diffuse and Specular). Since there is no Emission colour for the glLight, that is added to the final colour without modification.

A good set of settings for a light source would be to set the Diffuse and Specular components to the colour of the light source, and the Ambient to the same colour - but at MUCH reduced intensity, 10% to 40% seems reasonable in most cases.

For the glMaterial, it's usual to set the Ambient and Diffuse colours to the natural colour of the object and to put the Specular colour to white. The emission colour is generally black for objects that do not shine by their own light.

Before you can use an OpenGL light source, it must be positioned using the glLight command and enabled using glEnable(GL_LIGHTn) where 'n' is 0 through 7. There are additional commands to make light sources directional (like a spotlight or a flashlight) and to have it attenuate as a function of range from the light source.

glColorMaterial

This is without doubt the most confusing thing about OpenGL lighting - and the biggest cause of problems for beginners.

The problem with using glMaterial to change polygon colours is three-fold:

For these reasons, OpenGL has a feature that allows you do drive the glMaterial colours using the more flexible glColor command (which is not otherwise useful when lighting is enabled).

To drive (say) the Emission component of the glMaterial using glColor, you must say:


   glColorMaterial ( GL_FRONT_AND_BACK, GL_EMISSION ) ;
   glEnable ( GL_COLOR_MATERIAL ) ;

From this point performing a glColor command (or setting the glColor via a vertex array or something) has the exact same effect as calling:

   glMaterial ( GL_FRONT_AND_BACK, GL_EMISSION, ...colours... ) ;

One especially useful option is:

   glColorMaterial ( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE ) ;

This causes glColor commands to change both Ambient and Diffuse colours at the same time. That's a very common thing to want to do for real-world lighting models.

Light Sources.

OpenGL's lights are turned on and off with glEnable(GL_LIGHT_n) and glDisable(GL_LIGHT_n) where 'n' is a number in the range zero to the maximum number of lights that this implementation supports (typically eight). The glLight call allows you to specify the colour (ambient, diffuse and specular), position, direction, beam width and attenuation rate for each light. By default, it is assumed that both the light and the viewer are effectively infinitely far from the object being lit. You can change that with the glLightModel call - but doing so is likely to slow down your program - so don't do it unless you have to. glLightModel also allows you to set a global ambient lighting level that's independent of the other OpenGL light sources.

There is also an option to light the front and back faces of your polygons differently. That is also likely to slow your program down - so don't do it.

glNormal

When lighting is enabled, OpenGL suddenly needs to know the orientation of the surface at each polygon vertex. You need to call glNormal for each vertex to define that - OpenGL does not provide a useful default.

Good Settings.

With this huge range of options, it can be hard to pick sensible default values for these things.

My advice for a starting point is to:

Using Alpha with lighting enabled.

One confusing thing is that each of the colour components (Ambient,Diffuse, Specular and Emission) has an associated 'alpha' component for setting transparency. It is important to know that only the DIFFUSE colour's alpha value actually determines the transparency of the polygon. If you have taken my advice and used glColorMaterial to cause glColor to drive the ambient and diffuse components then this seems perfectly natural...but people sometimes want to use the glColor to drive one of the other material components and are then very confused about their inability to set the transparency using glColor. If that happens to you - remember to use glMaterial to set the diffuse colour - and hence the alpha for the polygon overall.

Summary

I started out by saying that enabling OpenGL lighting disables the glColor command - but in practice, since we generally want to set the glMaterial specular colour to white and the emission colour to black for almost all of our polygons, it's really convenient to set the glColorMaterial option to GL_AMBIENT_AND_DIFFUSE and simply use glColor to set the 'real' colour of the object just as we did when OpenGL lighting was disabled.