Chapter 5
Lighting
In the real world, things are visible because of light. All light comes from various sources, 
usually identifiable, but even when the source is unknown, the light was actually emitted 
from somewhere. Not only does the visibility of everything (except those objects that emit 
their own light) depend on light emitted from somewhere and reflected to the viewer, but 
the detailed appearance of each object depends on properties of the material composing the 
illuminated surface. A realistic graphics system should incorporate these facts. This chapter 
explores lighting and material properties.
Building on the work of Chapter 4, save Obj.Dpr as ALight.Dpr in 
OpenGL\Chapter.5\Ambient. Save the main unit as ALight1.Pas.
Enable Lighting
One of the great features of OpenGL is the ability to produce realistic effects through 
simulation of lighting. In the code in the first four chapters, OpenGL provided the equivalent 
of lighting so that the objects were visible. Now is the time to provide lighting specifications 
directly. Here is a necessary OpenGL constant:

  GL_LIGHTING = $0B50;	
At the bottom of the FormCreate method, enable lighting:

glEnable(GL_LIGHTING);	
Save, compile, and run. Maneuver one of the cubes into the viewing volume (clipping 
region). Ugh! The red square is black! Rotate the cube so that two more faces show at least a 
little. They are black, too! It is difficult to distinguish one face from another because they 
have exactly the same color. Now that the program is taking responsibility for making the 
objects in the scene visible, OpenGL is taking no responsibility at all. Obviously the program 
needs to do more than just enable lighting.

PAGE 79
 Basically three kinds of lighting are available through OpenGL: ambient, diffuse, and specular. 
This chapter will examine each of them in turn.
Ambient Light
Definition
Ambient light has no apparent source, but seems to come from all directions. In the real 
world, all light has a source, but some light may be reflected and scattered from several 
surfaces before finally reaching the viewer. The source of such light may be difficult or 
impossible to trace. This kind of light is called ambient light. OpenGL simulates this effect by 
providing illumination equally from all directions. It can be associated with a particular light 
or it can have no specific source, just magically appearing (at the programmer’s command).
Commands and Constants
The command for defining a light is a variation of glLight. Use the “f” suffix to use GLfloat 
parameters. Many commands, including ones already discussed, have a vector version, 
indicated by a “v” suffix. The vector version substitutes a pointer to an array in place of 
several scalar parameters. The glLight command only takes three parameters in any version, 
so only the vector version meets the current need. The glLightModel command takes three 
parameters and also has a vector version. Then there is the glColorMaterial command, which 
introduces the idea of material properties. The relevant declarations follow. First a necessary 
type:

  PGLfloat =  ^GLFloat;	
Here are some useful constants:

  GL_LIGHT_MODEL_AMBIENT = $0B53;	
  GL_COLOR_MATERIAL      = $0B57;	
  GL_AMBIENT             = $1200;	
  GL_LIGHT0              = $4000;	
  GL_LIGHT1              = $4001;	
  GL_LIGHT2              = $4002;	
  GL_LIGHT3              = $4003;	
  GL_LIGHT4              = $4004;	
  GL_LIGHT5              = $4005;	
  GL_LIGHT6              = $4006;	
  GL_LIGHT7              = $4007;	
Finally, the commands:

PAGE 80
 
procedure glColorMaterial(face,mode:GLenum); stdcall;	
procedure glLightModelfv(pname:GLenum;params:PGLfloat); stdcall;	
procedure glLightfv(light,pname:GLenum;params:PGLfloat); stdcall;
procedure glMaterialf(face,pname:GLenum;param:GLfloat); stdcall;
procedure glMaterialfv(face,pname:GLenum;params:PGLfloat); stdcall;
procedure glMateriali(face,pname:GLenum;param:GLint); stdcall;	
Consider the parameters in the commands. The face parameter can be GL_ FRONT, 
GL_BACK, or GL_FRONT_AND_BACK. For now, use GL_FRONT. The mode parameter 
specifies the kind of lighting to which the material property relates. The example under 
development uses GL_AMBIENT. The params pointer could be pointing to a single glFloat, 
or the first element of an array of them. While this is very C-like, Delphi can handle it nicely. 
The pname parameter identifies the purpose for two of the commands. The command for the 
simplest method of implementing ambient light is glLightModel. Pass it 
GL_LIGHT_MODEL_AMBIENT for the first parameter.
The Easy Way
Lighting introduces a new level of complexity to the specification of an object’s appearance. 
A simple color specification does not tell the whole story about how a surface responds to 
light. Material properties allow a more complete way to describe responses to light. The 
present example avoids some of the complexity by having the material color track the 
current color by means of the glColorMaterial command. This is one of the options that 
requires enabling before use.
At the bottom of the FormCreate method, after the enabling of lighting, initialize the array 
and place some of the new commands with the appropriate arguments.

AmbientDef[0] := 1.0; {red}	
  AmbientDef[1] := 1.0; {green}	
  AmbientDef[2] := 1.0; {blue}	
  AmbientDef[3] := 1.0; {alpha}	
  glEnable(GL_COLOR_MATERIAL);	
glColorMaterial(GL_FRONT,GL_AMBIENT);	
glLightModelfv(GL_LIGHT_MODEL_AMBIENT,@AmbientDef);	
In this version of the glLightModel command, using GL_LIGHT_MODEL_ AMBIENT 
means the array contains the color specification of the ambient light. Here, giving maximum 
intensity (1.0) to the red, green, and blue components specifies a white ambient light. A later 
chapter covers alpha; just set it to 1.0 for now. Declare the array in the form’s private section.

    AmbientDef: array[0..3] of glFloat;	

PAGE 81
 Save, compile, and run. Now the cubes look like they did before. So what did the new 
commands accomplish? One benefit is control. Experiment with decreasing the red, green, 
and blue components of AmbientDef (positions 0, 1, and 2 in the array). Reducing the overall 
intensity of the ambient light or giving it a color bias produces effects that may be desirable 
in some applications.
The Hard Way
A more flexible approach uses the glLight command. Make a new directory named 
OpenGL\Chapter.5\Ambient0. In that directory save the program as Amb0.Dpr and the 
main unit as Ambient0.Pas. Change the bottom of the FormCreate method to this:

  glEnable(GL_LIGHTING);	
  AmbientDef[0] := 1.0; {red}	
  AmbientDef[1] := 1.0; {green}	
  AmbientDef[2] := 1.0; {blue}	
  AmbientDef[3] := 1.0; {alpha}	
	
  glLightfv(GL_LIGHT0,GL_AMBIENT,@AmbientDef);	
  glEnable(GL_LIGHT0);	
This approach defines a particular light (LIGHT0) instead of just setting up a light model. At 
least eight lights are available; some implementations of OpenGL may allow more. Notice 
that you must turn on any light you want to use. This example switches on LIGHT0 with 
glEnable(GL_LIGHT0). To use a light, the simple color tracking of glColorMaterial is not 
adequate. So instead of glColor commands inside the Cube method, use glMaterial 
commands. A vector version is most convenient, so define an array at the top of Cube. For 
this example there is no attempt to produce the same colors as before. The Cube method’s 
listing follows:

procedure TForm1.Cube;	
var	
  ambient: array[0..3] of glFloat;	
begin	
  fillchar(ambient,sizeOf(ambient),0);	
  ambient[3] := 1.0; {alpha}	
  glBegin(GL_POLYGON); {near face}	
    ambient[0] := 0.5; {red}	
    glMaterialfv(GL_FRONT,GL_AMBIENT,@ambient);	
    glVertex3f(-0.4,-0.4,+0.4);	
    glVertex3f(+0.4,-0.4,+0.4);	
    glVertex3f(+0.4,+0.4,+0.4);	
    glVertex3f(-0.4,+0.4,+0.4);	
  glEnd;	


PAGE 82
 	
  glBegin(GL_POLYGON); {right face}	
    ambient[1] := 0.5; {green} {total=yellow}	
    glMaterialfv(GL_FRONT,GL_AMBIENT,@ambient);	
    glVertex3f(+0.4,-0.4,+0.4);	
    glVertex3f(+0.4,-0.4,-0.4);	
    glVertex3f(+0.4,+0.4,-0.4);	
    glVertex3f(+0.4,+0.4,+0.4);	
  glEnd;	
	
  glBegin(GL_POLYGON); {left face}	
    ambient[0] := 0.0; {red} {total=green}	
    glMaterialfv(GL_FRONT,GL_AMBIENT,@ambient);	
    glVertex3f(-0.4,-0.4,-0.4);	
    glVertex3f(-0.4,-0.4,+0.4);	
    glVertex3f(-0.4,+0.4,+0.4);	
    glVertex3f(-0.4,+0.4,-0.4);	
  glEnd;	
	
  glBegin(GL_POLYGON); {back face}	
    ambient[2] := 0.5; {blue} {total=turquoise}	
    glMaterialfv(GL_FRONT,GL_AMBIENT,@ambient);	
    glVertex3f(+0.4,-0.4,-0.4);	
    glVertex3f(-0.4,-0.4,-0.4);	
    glVertex3f(-0.4,+0.4,-0.4);	
    glVertex3f(+0.4,+0.4,-0.4);	
  glEnd;	
	
  glBegin(GL_POLYGON); {bottom face}	
    ambient[1] := 0.0; {green} {total=blue}	
    glMaterialfv(GL_FRONT,GL_AMBIENT,@ambient);	
    glVertex3f(-0.4,-0.4,+0.4);	
    glVertex3f(-0.4,-0.4,-0.4);	
    glVertex3f(+0.4,-0.4,-0.4);	
    glVertex3f(+0.4,-0.4,+0.4);	
  glEnd;	
	
  glBegin(GL_POLYGON); {top face}	
    ambient[0] := 0.5; {red} {total=magenta/magenta}	
    glMaterialfv(GL_FRONT,GL_AMBIENT,@ambient);	
    glVertex3f(-0.4,+0.4,+0.4);	
    glVertex3f(+0.4,+0.4,+0.4);	
    glVertex3f(+0.4,+0.4,-0.4);	
    glVertex3f(-0.4,+0.4,-0.4);	
  glEnd;	
end;	
Listing 5-1. The Cube method.

PAGE 83
 Save, compile, and run. Bring the cubes into view and move them around. Notice something 
rather strange. A side of the cube facing directly toward the viewer is brighter than the same 
side with a significantly different angle to the viewer.
That is not the behavior of ambient light. Perhaps it has been set too bright. Ambient light is 
often a minor part of the total light in a scene. In the FormCreate method cut it down as 
follows:

  AmbientDef[0] := 0.3; {red}	
  AmbientDef[1] := 0.3; {green}	
  AmbientDef[2] := 0.3; {blue}	
  AmbientDef[3] := 1.0; {alpha}	
Save, compile, and run. Manipulate the scene as before. Alas, there is little noticeable 
difference between ambient light at 30% and ambient light at 100%. Something is very 
wrong.
Actually, LIGHT0 has a special characteristic different from LIGHT1, LIGHT2, or any of the 
others. LIGHT0 by default has a white light diffuse component and a white light specular 
component. The default ambient component is black, but the present code replaced the 
default value. To see the effect of ambient light alone requires replacing the diffuse and 
specular parts (with black). Make these changes to the FormCreate method:

  glEnable(GL_LIGHTING);	
  fillchar(AmbientDef,SizeOf(AmbientDef),0);	
  glLightfv(GL_LIGHT0,GL_DIFFUSE,@AmbientDef);	
  glLightfv(GL_LIGHT0,GL_SPECULAR,@AmbientDef);	
	
  AmbientDef[0] := 0.3; {red}	
  AmbientDef[1] := 0.3; {green}	
  AmbientDef[2] := 0.3; {blue}	
  AmbientDef[3] := 1.0; {alpha}	
	
  glLightfv(GL_LIGHT0,GL_AMBIENT,@AmbientDef);	
  glEnable(GL_LIGHT0);	
The new constants are:

  GL_DIFFUSE  = $1201;	
  GL_SPECULAR = $1202;	
Now save, compile, and run. Bring the cubes into view. What a difference! The cubes are 
rather dim, which is natural for only 30% ambient illumination, but most importantly they 
are uniformly dim. This is how ambient light should appear!

PAGE 84
 Can OpenGL handle bright white ambient light, or did the earlier supposition that the 
ambient light was set out of natural range have merit? Find out by replacing all the 0.3 
values in AmbientDef with 1.0. Save, compile, run, and move cubes around. Now uniform 
illumination abounds. Though it is quite bright, the ambient light appears evenly distributed 
regardless of angle, which is the right behavior for ambient light.
Diffuse Light
Definition
Imagine a lamp lighting a large room. Much of the light bounces off many surfaces before 
reaching a viewer. While the light actually comes from the lamp, it seems simply to be 
everywhere. This is the ambient portion of the light. Obviously, however, some of the light 
proceeds directly from the source to the surface of some object in the room and reflects 
directly to the viewer. However, unless the surface is a mirror, the light tends to scatter 
rather than bounce off at a predictable angle. In the real world, nothing is perfectly smooth, 
but some things have been polished enough that the surface irregularities are too small to 
matter. Such surfaces are “shiny.” Most surfaces have irregularities large enough to scatter 
most light as in Figure 5-1. These surfaces are “flat.” Unless the surface is white, it will 
absorb some colors from the light and reflect the rest, thus defining its color in terms of the 
portion of the light it reflects. This scattering effect determines the diffuse portion of the 
light. Not only does OpenGL allow the definition of at least eight light sources, but it also 
allows independent specification of the ambient portion and diffuse portion of each light.


PAGE 85
 Positional Lamps
Save the project as Difu.Dpr in a new directory called OpenGL\Chapter.5\ Dif1 and save 
the main unit as Diffus.Pas. For the lamp under development, most light shines directly on 
an object from the lamp (the diffuse portion), and a lesser contribution comes from multiple 
reflections (the ambient portion). Prepare to change the AmbientDef assignments in the 
FormCreate method as follows, but do not type it in just yet:

  glEnable(GL_LIGHTING);	
  AmbientDef[0] := 0.3; {red}	
  AmbientDef[1] := 0.3; {green}	
  AmbientDef[2] := 0.3; {blue}	
  AmbientDef[3] := 1.0; {alpha}	
While bland, homogenized arrays like this provide a simple, fairly language-independent 
way of passing data to the OpenGL DLLs, Delphi allows more meaningful structures that 
use memory exactly the same way. The following type declaration of a record structure is 
more intuitive and does not require the programmer to memorize the order of the colors:

  TLightDesc=record	
    red,	
    green,	
    blue,	
    alpha: glFloat;	
  end;	
Wait! Don’t type that in either! Arrays use contiguous memory, but record structures may 
have “holes” in the data due to memory alignment by the compiler. This particular record 
structure is safe (for the moment) because each of the elements is four bytes in size, but to 
guarantee that the record memory layout is exactly the same as the array memory layout, 
use the packed reserved word and place the declaration just above the form’s class 
declaration:

type	
  TLightDesc=packed record	
    red,	
    green,	
    blue,	
    alpha: glFloat;	
  end;	
TForm1 = class(TForm)	
Of course, the type for the AmbientDef field within the form declaration should become:

    AmbientDef: TLightDesc;	

PAGE 86
 Now the new assignments to AmbientDef in the FormCreate method can proceed:

  glEnable(GL_LIGHTING);	
  with AmbientDef do	
  begin	
    red := 0.3;	
    green := 0.3;	
    blue := 0.3;	
    alpha := 1.0;	
  end;	
  LightPlace.w := 1.0;	
The key distinction between diffuse light and ambient light is that the source of diffuse light 
has a position. Therefore, a demonstration of diffuse light requires the ability to set the 
position of the lamp. Accordingly, add these lines to the case statement in the FormKeyPress 
method:

    'l': xl := xl+sign;	
    'm': yl := yl+sign;	
    'n': zl := zl+sign;	
In the form’s class declaration, add these “L” variable declarations to describe the lamp 
position:

    xl,yl,zl,	
    x1,y1,z1,x2,y2,z2,xr1,yr1,zr1,	
    xr2,yr2,zr2,angle1,angle2,sign: glFloat;	
    AmbientDef: TLightDesc;	
    LightPlace: TLightPlace;	
LightPlace is also new and needs a type declaration:

type	
  TLightDesc=packed record	
    red,	
    green,	
    blue,	
    alpha: glFloat;	
  end;	
  TLightPlace=packed record	
    x,	
    y,	
    z,	
    w: glFloat;	
  end;	
TForm1 = class(TForm)	
In the FormPaint method, place some code to manipulate the light position:

PAGE 87
 
  glPushMatrix;	
  if before then	
     glRotatef(angle2,xr2,yr2,zr2);	
  glTranslatef(x2,y2,z2);   	
  if not before then	
     glRotatef(angle2,xr2,yr2,zr2);	
  Cube;	
  glPopMatrix;	
	
  glPushMatrix;	
  glTranslatef(xl,yl,zl);	
  glLightfv(GL_LIGHT0,GL_POSITION,@LightPlace);	
  glPopMatrix;	
Here is the declaration of the new constant:

GL_POSITION = $1203
Save, compile, run, and bring a cube barely into view, then go two keystrokes more down 
the negative z-axis. Move the cube near the upper left corner of the form. The cube appears 
very bright, but that will change as the lamp moves.
Press the minus key, then press the lowercase “n” repeatedly. After about five such presses, 
a slight difference is noticeable. As you continue to advance the lamp down the negative z-
axis, watch the shading develop on the cube!
Now it should be possible to have a uniformly colored cube that still shows its three-
dimensional nature by its shading in a diffuse light source. In the Cube method, change the 
type of the ambient local variable:

procedure TForm1.Cube;	
var	
  ambient: TLightDesc;	
begin	
Remove all assignments to ambient inside the Cube method and replace them with one set 
of assignments as follows:

procedure TForm1.Cube;	
var	
  ambient: TLightDesc;	
begin	
  ambient.red := 0.2;	
  ambient.green := 0.5;	
  ambient.blue := 0.8;	
  ambient.alpha := 1.0;	
  glMaterialfv(GL_FRONT,GL_AMBIENT,@ambient);	
  glBegin(GL_POLYGON); {near face}	

PAGE 88
 Save, compile, run, and perform movements as before. Around the 22nd “n” press the 
shading of the cube is more than sufficient to distinguish the faces. Enjoy! This is 
tremendous progress from the first step of making a colored background.
Different Material Colors
Various material properties can be set independently, including colors for ambient 
reflectivity, specular reflectivity, and diffuse reflectivity. Allowing the surface to reflect 
ambient light differently from diffuse light provides for some interesting effects. Adjust the 
top of the Cube method as follows:

procedure TForm1.Cube;	
var	
  diffuse,	
  ambient: TLightDesc;	
begin	
  ambient.red := 0.2;	
  ambient.green := 0.5;	
  ambient.blue := 0.8;	
  ambient.alpha := 1.0;	
  diffuse.red := 0.8;	
  diffuse.green := 0.5;	
  diffuse.blue := 0.2;	
  diffuse.alpha := 1.0;	
  glMaterialfv(GL_FRONT,GL_DIFFUSE,@diffuse);	
  glMaterialfv(GL_FRONT,GL_AMBIENT,@ambient);	
  glBegin(GL_POLYGON); {near face}	
Save, compile, run, and perform movements as before. This version more clearly 
demonstrates the ambient contribution and the diffuse contribution from the light source. At 
first, the diffuse light reflection color clearly dominates. As the light advances down the 
negative z-axis until it is almost past the near face of the cube, ambient reflection clearly 
shows one color, and diffuse reflection another color. When the light moves a little more, the 
ambient reflection color will completely dominate. Why does the diffuse contribution wane 
before the light is past the cube? Another behavior of real light reflecting off real surfaces is 
the fact that as the angle from the light source to the surface flattens, less of the diffuse light 
reflects to the viewer. Figure 5-2 illustrates light shining on a surface from an acute angle 
(left) and from a more obtuse angle (right). The right side of the illustration shows less of the 
scattered (diffuse) light reaching the viewer. OpenGL reproduces this behavior nicely.


PAGE 89
 So far the exercises have depended on the fact that LIGHT0 has a white diffuse component 
by default. The next exercise illustrates colored light on surfaces of different color. 
Accordingly, at the bottom of the FormCreate method, make the light pure red and explicitly 
define the diffuse component:

  glEnable(GL_LIGHTING);	
  with AmbientDef do	
    begin	
    red := 0.6;	
    green := 0.0;	
    blue := 0.0;	
    alpha := 1.0;	
  end;	
  LightPlace.w := 1.0;	
	
  glLightfv(GL_LIGHT0,GL_AMBIENT,@AmbientDef);	
  glLightfv(GL_LIGHT0,GL_DIFFUSE,@AmbientDef);	
  glEnable(GL_LIGHT0);	
end; {FormCreate}	
Save...well, you know the drill. Now, since the only light is red, that is the only light 
reflected, so the cube looks pure red. What will happen if the material property has no red 
component? Find out by removing the red at the top of the Cube method. At the same time, 
return the cube to having only one color response to the light:

procedure TForm1.Cube;	
var	
  ambient: TLightDesc;	
begin	
  ambient.red := 0.0;	
  ambient.green := 0.5;	


PAGE 90
   ambient.blue := 0.8;	
  ambient.alpha := 1.0;	
  glMaterialfv(GL_FRONT,GL_DIFFUSE,@ambient);	
  glMaterialfv(GL_FRONT,GL_AMBIENT,@ambient);	
  glBegin(GL_POLYGON); {near face}	
Repeat the steps and see the result. The cube is black. Only red light is available. The cube 
reflects no red light. Therefore, the cube reflects virtually no light.
Spotlights
The light source discussed so far can be a spotlight. First, this naked light needs some clothes. 
For this stage of the learning adventure, dress it in a...cube. Save the project as Spot.Dpr in 
OpenGL\Chapter.5\Spot, and save the main unit as Spot1.Pas in that directory. Give the 
Cube method the ability to set its material color from a passed parameter. Adjust its 
declaration inside the form class declaration and add a few fields:

    CubeColor,	
    SpotColor,	
    DiffDef,	
    AmbientDef: TLightDesc;	
    LightPlace: TLightPlace;	
    procedure ExceptionGL(Sender:TObject;E:Exception);	
    procedure Cube(matColor:TLightDesc);	
    procedure Axes;	
Now change its implementation code to handle the parameter:

procedure TForm1.Cube(matColor:TLightDesc);	
begin	
  glMaterialfv(GL_FRONT,GL_AMBIENT,@matColor);	
  glMaterialfv(GL_FRONT,GL_DIFFUSE,@matColor);	
  glBegin(GL_POLYGON); {near face}	
Set the color records in the FormCreate method:

  glEnable(GL_LIGHTING);	
  with AmbientDef do	
  begin	
    red := 0.3;	
    green := 0.3;	
    blue := 0.3;	
    alpha := 1.0;	
  end;	
  with DiffDef do	
  begin	
    red := 1.0;	
    green := 1.0;	


PAGE 91
     blue := 1.0;	
    alpha := 1.0;	
  end;	
  with CubeColor do	
    begin	
    red := 0.5;	
    green := 0.0;	
    blue := 0.8;	
    alpha := 1.0;	
  end;	
  with SpotColor do	
  begin	
    red := 0.0;	
    green := 0.5;	
    blue := 0.5;	
    alpha := 1.0;	
  end;	
	
  LightPlace.w := 1.0;	
	
  glLightfv(GL_LIGHT0,GL_AMBIENT,@AmbientDef);	
  glLightfv(GL_LIGHT0,GL_DIFFUSE,@DiffDef);	
Make the two calls to Cube in the FormPaint method use the parameter:

  Cube(CubeColor);	
Also adjust the code that positions the light. Be sure to make this cube smaller with glScalef:

  glPushMatrix;	
  glScalef(0.33,0.33,0.33);	
  glTranslatef(xl,yl,zl);	
  glLightfv(GL_LIGHT0,GL_POSITION,@LightPlace);	
  Cube(SpotColor);	
  glPopMatrix;	
Now, go back to the bottom of the FormCreate method and initialize some positions so you 
will not have to do so much work to position the objects. Note that the variable names 
contain the letter “l” not a number “1.”

  z2 := -2.7;	
  x2 := -0.7;	
  y2 := 0.7;	
  zl := -6.7;	
  xl := -1.8;	
  yl := -2.1;	
end; {FormCreate}	
Since the glScale command affects translation (and rotation), as well as the object size, adjust 
the case statement in the FormKeyPress method:

PAGE 92
 
    ‘l’: xl := xl+3.0*sign;	
    ‘m’: yl := yl+3.0*sign;	
    ‘n’: zl := zl+3.0*sign;	
Save, compile, and run. (You are remembering not to run from the IDE, aren’t you?) The 
magenta (or purple) cube and the cyan (blue-green, turquoise, whatever) “spotlight” are 
visible from the start. Use the “l,” “m,” and “n” keys to move the spotlight around. The 
center of the small cyan cube tracks the position of the light source so that the cube appears 
to be the light source. Notice the effect different light positions have on illumination of the 
magenta cube.
A really interesting effect is the illumination of the small cube from the inside, so that the 
sides of the cube appear to be translucent. This brings up an important fact: OpenGL knows 
nothing about shadows. When you “shine” an OpenGL light source on an OpenGL object, 
you see a very good representation of real illumination. However, if you interpose another 
seemingly solid OpenGL object between the light source and the first object, the more distant 
object receives illumination just as if there were nothing in the way. The consequence of this 
fact is that if you want shadows, you will have to program them yourself. Do not despair; it 
can be done. More about that comes later.
Notice that only half of the cyan cube lights up. By default, an OpenGL diffuse light source 
has a 180 degree spread as shown in Figure 5-3, which includes the cyan cube that 
represented the spotlight. A real spotlight is more focused, and OpenGL permits this effect 
with an appropriate constant in a familiar command:

  glLightf(GL_LIGHT0,GL_SPOT_CUTOFF,cutoff);	
The new constant has this value:

  GL_SPOT_CUTOFF = $1206;            
Express the value of the cutoff variable in degrees. Think of the light spreading from a 
spotlight as a cone of light. The angle from the center of the cone to the edge of the cone is 
the cutoff angle, as shown in Figure 5-4.
         Figure 5-4. The spotlight cutoff angle.

PAGE 93
 Add the cutoff field to the “ell” line in the form’s class declaration:

    xl,yl,zl,cutoff,	
    x1,y1,z1,x2,y2,z2,xr1,yr1,zr1,	
    xr2,yr2,zr2,angle1,angle2,sign: glFloat;	
Use the cutoff field in the FormCreate method:

  glLightfv(GL_LIGHT0,GL_AMBIENT,@AmbientDef);	
  glLightfv(GL_LIGHT0,GL_DIFFUSE,@DiffDef);	
  cutoff := 55.0;	
  glLightf(GL_LIGHT0,GL_SPOT_CUTOFF,cutoff);	
  glEnable(GL_LIGHT0);	
Save, compile, and run. Move the spotlight around with “l,” “m,” and “n.” Notice that you 
must move the spotlight closer to the magenta cube in the x and y directions or farther in the 
positive z direction in order to illuminate the magenta cube.
Directional Spotlights
A feature still missing from the spotlight illustrated thus far in the chapter is the ability to 
aim it. What kind of spotlight is it that you can’t...spot? One thing needed for that purpose 
is to add some more “ell” fields in the form class declaration:

    xl,yl,zl,xrl,yrl,zrl,anglel,cutoff,	
    x1,y1,z1,x2,y2,z2,xr1,yr1,zr1,	
    xr2,yr2,zr2,angle1,angle2,sign: glFloat;	
The xrl, yrl, and zrl fields set up the axis of rotation for the cyan cube and the aiming 
direction of the spotlight. Similarly the anglel field is the number of degrees of rotation about 
the axis for both the cube and the aiming of the spotlight. The default aim of a spotlight is a 
vector from the origin (0.0,0.0,0.0) to (0.0,0.0,–1.0) down the negative z-axis, which points 
toward the far face of the cube at its starting position. Therefore, aiming the far face of the 
cube effectively aims the spotlight.
To manipulate those fields, add to the case statement in the FormKeyPress method:

    'k': anglel := anglel+30.0*sign;	
    'o': xrl := xrl+3.0*sign;	
    'p': yrl := yrl+3.0*sign;	
    'q': zrl := zrl+3.0*sign;	
Add a new type:

  TLightDesc=packed record	
    red,	


PAGE 94
     green,	
    blue,	
    alpha: glFloat;	
  end;	
  TLightPlace=packed record	
    x,	
    y,	
    z,	
    w: glFloat;	
  end;	
  TPosition=packed record	
    x,	
    y,	
    z: glFloat;	
  end;	
Add a new field of that type in the form’s class declaration:

    AmbientDef: TLightDesc;	
    LightPlace: TLightPlace;	
    SpotDirect: TPosition;	
Initialialize SpotDirect in the FormCreate method to match the default value of a spotlight 
direction. Except for the z field, the zeros that come automatically from creating an instance 
of the form are sufficient:

  with SpotColor do	
  begin	
    red := 0.0;	
    green := 0.5;	
    blue := 0.5;	
    alpha := 1.0;	
  end;	
  SpotDirect.z := -1.0;	
  LightPlace.w := 1.0;	
Finally, add the use of SpotDirect and the rotational “ell” fields:

  glPushMatrix;	
  glScalef(0.33,0.33,0.33);	
  if before then	
    glRotatef(anglel,xrl,yrl,zrl);	
  glTranslatef(xl,yl,zl);	
  if not before then	
    glRotatef(anglel,xrl,yrl,zrl);	
  glLightfv(GL_LIGHT0,GL_POSITION,@LightPlace);	
  glLightfv(GL_LIGHT0,GL_SPOT_DIRECTION,@SpotDirect);	
  Cube(SourceColor);	
Here is the declaration of the new constant:

PAGE 95
 
GL_SPOT_DIRECTION = $1204;
Save, compile, run, and press the plus key. Press the lowercase “o” to set the axis of rotation 
for the spotlight in the positive x direction. Press and hold the lowercase “k” to produce 
rotation of the spotlight about the x-axis. Watch the change in lighting on the magenta cube 
as the spotlight direction changes. That is not a bad effect for just a little code. There is 
certainly a lot of power in simple OpenGL commands! Notice the brilliance of the face of the 
cyan cube from which the light seems to emanate.
Emission
Definition
Here is a profound revelation: Light sources emit light. They glow. A surface that glows 
looks different from a surface that merely reflects light. OpenGL provides a way to produce 
this effect. Notice that the subject is the appearance of surfaces. Therefore the appropriate 
command is glMaterialfv rather than glLightfv. The new constant is:

  GL_EMISSION = $1600;	
The purpose of the use of GL_EMISSION is to intensify the surface color so that it seems to 
glow. It does not add to the light shed on other surfaces.
Implementation
Save the spot project as Emission.Dpr in OpenGL\Chapter.5\Emit. Save the main unit as 
Emit1.Pas in the same directory. Since the use of GL_EMISSION applies to the material 
properties of a face in the spotlight cube, some special purpose code is necessary for this 
illustration. In the Cube method, surround the back face part with material commands. The 
if statement tests for a red component so the magenta cubes will not be affected, but the cyan 
cube will be affected. Remember this is just special code for a brief illustration.

  if matColor.red=0.0 then	
    glMaterialfv(GL_FRONT,GL_EMISSION,@matColor);	
  glBegin(GL_POLYGON); {back face}	
    glVertex3f(+0.4,-0.4,-0.4);	
    glVertex3f(-0.4,-0.4,-0.4);	
    glVertex3f(-0.4,+0.4,-0.4);	
    glVertex3f(+0.4,+0.4,-0.4);	
  glEnd;	
  glMaterialfv(GL_FRONT,GL_EMISSION,@black);	

PAGE 96
 The black emission statement confines the effect of the previous emission statement to the 
one face; otherwise everything would eventually end up with GL_EMISSION. Of course, the 
black field requires declaration within the form’s class declaration. Happily, it will 
automatically be initialized to all zeros.

    black: TLightDesc;	
    LightPlace: TLightPlace;	
    SpotDirect: TPosition;	
Frankly, since the back face of the cyan cube already almost seems to glow, using matColor 
with GL_EMISSION will have no noticeable effect. Replace it with the emit field:

if matColor.red=0.0 then	
    glMaterialfv(GL_FRONT,GL_EMISSION,@emit);	
Declare emit in the form’s class declaration:

    emit: TLightDesc;	
    black: TLightDesc;	
    LightPlace: TLightPlace;	
    SpotDirect: TPosition;	
Initialize emit in the FormCreate method:

  with emit do	
  begin	
    red := 0.6;	
    green := 1.0;	
    blue := 1.0;	
    alpha := 1.0;	
  end;	
end; {FormCreate}	
Save, compile, run, press the plus symbol, and press the lowercase “o.” Hold down 
lowercase “k” until the back side partially faces the viewer. Notice the intensity of the back 
face. This illustrates a feature that is seldom used, but you may find it comes in handy on 
special occasions.
To see why you needed glMaterialfv(GL_FRONT,GL_EMISSION,@black), comment it out, 
recompile, and rerun. GL_EMISSION affected everything. Ugh! The effect of a glMaterial 
command remains until explicitly changed.
Remember that emission is just a surface effect in OpenGL; it is not one of the three kinds of 
light that come from a light source. The next topic deals with such a type of light.

PAGE 97
 Specular Light
Definition
The third component of light from a light source is specular light. Ambient light has been 
scattered from many surfaces and seems to come from all directions. Diffuse light comes 
from a specific direction (the source, of course), but scatters from surfaces, giving a soft, 
“flat” appearance to surfaces. Ambient and diffuse components of a light source commonly 
share the same color. Specular light is also directional, but much “tighter” in reflection than 
diffuse light, often with a different color. Specular light more closely follows the classical 
rule of reflection, wherein the angle of reflection equals the angle of incidence, as in Figure 5-
5.
As you might expect, the result of reflection of specular light depends greatly on the material 
properties of the surface. You may have noticed that in the real world surfaces that are 
smooth and “shiny” often show a small bright spot in bright, direct light. The bright spot is 
known as the specular highlight. With OpenGL you can give a surface the appearance of being 
smooth and shiny by producing such a bright spot. While the color of the surface with 
respect to ambient and diffuse light may vary, the color of the specular highlight tends 
toward white.
Implementation
To demonstrate the specular component of a light source, load the Spot.Dpr project from 
OpenGL\Chapter.5\Spot. Create a new directory called OpenGL\Chapter.5\Spec and save 
the project as Specular.Dpr into the new directory. Save its main unit as Spec1.Pas in the 
same directory. At the bottom of the FormCreate method, add a new glLightfv command:

  glLightfv(GL_LIGHT0,GL_SPECULAR,@SpecDef);	
end; {FormCreate}	
Actually, LIGHT0 by default has a pure white specular component, but this illustration 
needs something a little less intense. Assign a medium gray to it:


PAGE 98
   with SpecDef do	
  begin	
    red := 0.5;	
    green := 0.5;	
    blue := 0.5;	
    alpha := 1.0;	
  end;	
  glLightfv(GL_LIGHT0,GL_SPECULAR,@SpecDef);	
end; {FormCreate}	
Oh yes! Be sure to declare SpecDef in the form’s class declaration:

    CubeColor,	
    SpotColor,	
    DiffDef,	
    SpecDef,	
    AmbientDef: TLightDesc;	
    LightPlace: TLightPlace;	
    SpotDirect: TPosition;	
For convenience, change the initial value of z2 from –2.7 to –3.7, moving the larger cube 
farther back, so that the spotlight cube will not bury itself in the larger cube in the coming 
illustration.

  z2 := -3.7;	
  x2 := -0.7;	
  y2 := 0.7;	
  zl := -6.7;	
  xl := -1.8;	
  yl := -2.1;	
To the specular light component, add the surface color response to that light:

  glLightfv(GL_LIGHT0,GL_SPECULAR,@SpecDef);	
  glMaterialfv(GL_FRONT,GL_SPECULAR,@SpecDef);	
end; {FormCreate}	
Notice that the new glMaterialfv command is in the FormCreate method. Remember that 
such a command remains in effect until another command changes it. By placing it here, 
with no contradictory command elsewhere, you give all surfaces the same response to 
specular light for all time. Well, it lasts for as long as the program runs, anyway.
The specular effect needs one more specification. Remember that specular highlights appear 
on shiny surfaces. Naturally OpenGL defines a constant for setting shininess in a command:

  GL_SHININESS = $1601;	

PAGE 99
 The shininess property is an integer that can range in value from 0 to 128. Therefore, the 
property needs an integer form of the glMaterial command. Give it maximum shininess for 
this illustration.

  glLightfv(GL_LIGHT0,GL_SPECULAR,@SpecDef);	
  glMaterialfv(GL_FRONT,GL_SPECULAR,@SpecDef);	
  glMateriali(GL_FRONT,GL_SHININESS,128);	
end; {FormCreate}	
Save, compile, run, and press plus. Press the lowercase “m” four times to raise the spotlight 
toward the magenta cube. Do you see the faint light streak running from the lower left 
corner to the upper right corner of the near face? It is not much, but that is as good as it gets 
with a face of a cube. To see a better specular highlight, you need a surface with some 
curvature. A later chapter introduces curved surfaces.
How Much Light?
So far you have seen three light source components: ambient, diffuse, and specular. Each of 
these has three color components: red, green, and blue. There is also an alpha component, 
which still must be put off until later. Never fear! You will see alpha when the time is right. 
You have also seen material properties that specify the response to light. Material properties 
also have ambient, diffuse, and specular parts. Each of these also has the three color 
components.
So what determines how much light of each color reaches the viewer? Would you believe a 
simple multiplication? Figure 5-6 illustrates.

PAGE 100
 
Identifier Reference
Identifier			Description
glColorMaterial
An OpenGL command that causes material properties to track the current color set by 
glColor. It takes two parameters. The first tells which face or faces will be affected. The 
second tells which properties will track the current color. You must enable color tracking for 
this to work.
glLight
An OpenGL command that defines characteristics of an OpenGL light source. You must 
enable lighting and enable the specific light.
glLightModel
An OpenGL command that provides a simplified method of setting up a lighting 
environment instead of defining specific lights.
glMaterial
An OpenGL command for defining material properties.
GL_AMBIENT
An OpenGL constant specifying the ambient portion of light or material properties in 
various OpenGL commands.
GL_BACK
An OpenGL constant specifying the back face of polygons to be affected in various OpenGL 
commands.
GL_COLOR_MATERIAL
The OpenGL constant to pass to enable or disable color tracking.
GL_DIFFUSE
An OpenGL constant specifying the diffuse portion of light or material properties in various 
OpenGL commands.
GL_EMISSION
An OpenGL constant to pass to the glMaterial command, so that the affected surfaces appear 
to be glowing.
GL_FRONT
An OpenGL constant specifying the front face of polygons to be affected in various OpenGL 
commands.
GL_FRONT_AND_
BACK
An OpenGL constant specifying both the front and back faces of polygons to be affected in 
various OpenGL commands.

PAGE 101
 
Identifier			Description
GL_LIGHT0 ..
GL_LIGHT7
OpenGL constants for specifying specific OpenGL light sources.
GL_LIGHTING
An OpenGL constant to pass to glEnable or glDisable to turn on or turn off lighting 
calculations.
GL_LIGHT_MODEL_
AMBIENT
An OpenGL constant to pass to glLightModel to specify global ambient light.
GL_POSITION
An OpenGL constant to pass to glLight to specify the position coordinate of the light.
GL_SHININESS
An OpenGL constant to pass to the glMaterial command for setting the shininess property.
GL_SPECULAR
An OpenGL constant specifying the specular portion of light or material properties in 
various OpenGL commands.
GL_SPOT_CUTOFF
An OpenGL constant to pass to glLight to specify the angle that defines the cone of 
illumination from a spotlight.
GL_SPOT_DIRECTION
An OpenGL constnt to pass to glLight to specify the direction vector of the light.
packed
An Object Pascal reserved word used to make a data structure take the minimum amount of 
memory. The elements of the structure fit together with no unused memory between them 
because the compiler does not attempt to align the data elements on word or double-word 
boundaries.
PGLfloat
A data type which is a pointer to a Glfloat.
Summary
Chapter 5 presented new levels of realism with lighting. Here are the high points:
? Use glEnable(GL_LIGHTING) to inform OpenGL that you will take responsibility for 
lighting specification.
? OpenGL allows at least eight separate light sources.

PAGE 102
 
? All light has a source. Each light source has three components whose color compositions 
can be separately identified:
? Ambient light seems to come from all directions.
? Diffuse light comes from a specific direction and scatters upon reflection, giving a soft, 
flat appearance to surfaces.
? Specular light reflects sharply and appears as a specular highlight on smooth surfaces.
? Specify the color response of a surface to each light component with the glMaterial 
commands.
? Emission is also a material property for OpenGL.
? In addition to the color response to specular light, you must also specify the smoothness 
or shininess of the surface with the GL_SHININESS constant in the glMaterial command.


PAGE 103
 

PAGE 104

