|

OpenGL/ FPC - Chapter 12 - by Delax
Topic this time is lighting in OpenGL. This chapter is about the theory and in the next chapters I will explain the different kinds of light in OpenGL. A word of warning: some math stuff is about to come, but don't worry - I'll try to make it easy.
Look around yourself. Find lightsources and look how they create reflections, lightspots, shadows and so on. All that (and more) has to do with lightning. Of course OpenGL has many ways to light a scene.
Basically there are only 3 kinds of light. There is ambient light, diffuse light and specular light. Everything else is a combination or twisted version of one or more of these types.
Ambient light is easy to understand. It's light which comes from all directions. The lightsource does not matter as (in theory) the light has been reflected many times and comes from all angles and directions. Every side of an object is equally lit by ambient light.

Diffuse light has a lightsource but it gets reflected in every direction if it hits an object. The closer the object is to the lightsource the brighter it will lit.

Specular light is like diffuse light but it is reflected sharply in the original direction. A bright light creates a bright spot on the object. You only notice specular light if the object is between you and the lightsource or the light hits an edge of the object. For example: take a book and hold it in front of you towards a lamp. Now you'll see a bright reflektion of the light on the book.

Normally a lightsource contains all three kinds of light. Every lightsource has ambient, diffuse and specular light to some extent. But this depends on the kind of lightsource you want to create. A red laser for example has no or very little ambient and diffuse portions. But if you create the laser in a dusty room with particles there will be a diffuse part. And if the room is really dark you'll notice a red glow around - this would be the ambient light.
Every lightsource has a color. There are 4 parameters for every type of light: red, green, blue and alpha. We ignore the alpha channel until we get to transparency ;) Basically the red, green and blue stuff is the same we've done before.
The more intense a color the brighter is the light. So a gray light with (0.5,0.5,0.5) would be a white light with half intensity. Our red laser would be something like this:
| Type |
Red |
Green |
Blue |
| Ambient |
0.05 |
0.0 |
0.0 |
| Diffuse |
0.1 |
0.0 |
0.0 |
| Specular |
0.985 |
0.0 |
0.0 |
There you have it. That's the laserpointer for your next ego shooter. In the meantime remember that every light has values from 0.0 (min, dark) to 1.0 (max, bright).
Now to another thing about lightning: the materials of the objects and surfaces. Some kind of metal would reflect a lightsource completely different than paper. We define materials by it's color. A blue surface for example would only reflect the blue portion of the light. The surface under a green light would be black then.
Now we have to tell OpenGL what kind of color should be reflected and how much. To do that we have to split the light in it's RGB portions (attention: don't mix this up with "real" light. We're doing computer gfx after all!).

This means that the red part of the lightsource is multiplied with the red part of the material of the surface. The same goes for the green and blue values.
Example: A lightsource has a red value of 0.8 and the material a red value of 0.9. Therefore the new red value of the surface would be 0.72. Quite simple actually, right? Of course OpenGL does all the computations for us, we only provide the values.
The next thing we have to do is to tell OpenGL in which direction our surfaces reflect the light. Normally our lightray would leave the lightsource and hit our surface. Then it would be reflected in some angle and reach the eye of the viewer. Now we'll have to calculate this angle to make our surfaces light up correctly.

Now it gets dirty. How do you calculate a point on a surface that is defined by vertices? And to make it worse - every point between these vertices will be hit by the light. The last nail in the coffin is: it is imposible to calculate an angle for a point because a point has only one dimension.
Because of all these problems we'll have to use a trick. We define a vector away from our surface. This vector is called "normal" or "normal vector". The normal is heading 90° away from our surface. It is therefore the direction in which our surfaces points.

Finding this normal to a surface is not easy. Not every surface is heading horizontally or vertically. Not to mention Polygons, curves or landscapes.
To calculate a normal with at least 3 vertices you'll need to know how to calculate vectors. If you don't know anything about it: fasten your seatbelt. If you are to young (or simply too lazy) for this kind of math (i'd say if you are too lazy for that keep your hands off from gfx coding ;) just use the procedures from the sources.
With 3 points (P1, P2 and P3) in one room, you can determine 2 vectors from each point (V1 and V2). From P1 to P2 and to P3. To get a normal, you have to calculate the cross-product of it (V3 = V1 X V2). Then we get a new vector (V3) which is our normal.

You can calculate the cross-product as follows: |V1 X V2| = (P1)(P2) sin(V1,V2). But because a sin calculation is slow, we seaarch for an alternative way.
Normalx = (vtx1y - vtx2y) * (vtx2z - vtx3z) - (vtx1z - vtx2z) * (vtx2y - vtx3y)
Normaly = (vtx1z - vtx2z) * (vtx2x - vtx3x) - (vtx1x - vtx2x) * (vtx2z - vtx3z)
Normalz = (vtx1x - vtx2x) * (vtx2y - vtx3y) - (vtx1y - vtx2y) * (vtx2x - vtx3x)
Now we got our normal vector. But OpenGL needs the normal with lenght of one unit. So we have to adjust the lenght. This can be done with GL_NORMALIZE, but we'll do it by hand.
So now we'll "normalize our normals". For that, you have to square each vertex value, add them all together and take the squareroot. Now divide all vertice values with this number and you get the normalized vector.
Simple Example: 0.0, 9.0, 0.0. The Lenght of this vector would be 9 units.
0^2 = 0, 9^2 = 81, 0^2 = 0.
0 + 81 + 0 = 81.
81^(1/2) = 9.
0/9 = 0, 81/9 = 1, 0/9 = 0.
So the new lenght would be (0.0, 1.0, 0.0), which is our normal with lenght 1.0.
And just because I don't want to get spammed with "Help me!" mails, I'll give you a simple procedure. It is called with 3 vertices of your polygon. But beware - they have to be CCW (counter clockwise). The result is stored in CNormal.
PROCEDURE FindNormal(v1x, v1y, v1z, v2x, v2y, v2z, v3x, v3y, v3z : real);
const x = 1;
y = 2;
z = 3;
var temp_v1, temp_v2 : array[1..3] of real;
temp_lenght : real;
begin
temp_v1[x] := v1x - v2x;
temp_v1[y] := v1y - v2y;
temp_v1[z] := v1z - v2z;
temp_v2[x] := v2x - v3x;
temp_v2[y] := v2y - v3y;
temp_v2[z] := v2z - v3z;
// calculate cross product
CNormal[x] := temp_v1[y]*temp_v2[z] - temp_v1[z]*temp_v2[y];
CNormal[y] := temp_v1[z]*temp_v2[x] - temp_v1[x]*temp_v2[z];
CNormal[z] := temp_v1[x]*temp_v2[y] - temp_v1[y]*temp_v2[x];
// normalize normal
temp_lenght := (CNormal[x]*CNormal[x])+
(CNormal[y]*CNormal[y])+
(CNormal[z]*CNormal[z]);
temp_lenght := sqrt(temp_lenght);
// prevent n/0
if temp_lenght = 0 then temp_lenght := 1;
CNormal[x] /= temp_lenght;
CNormal[y] /= temp_lenght;
CNormal[z] /= temp_lenght;
end;
Now sit on your ass and tune it :)
Right, I'll give you another tip. We don't use this procedure in the following examples because we only work on one axis. As all vertex values on one axis are equal, the normal is (-)1.0 on this axis.
glVertex3f( 3.0, 3.0,-3.0);
glVertex3f( 3.0, 3.0, 3.0);
glVertex3f( 3.0,-3.0, 3.0);
glVertex3f( 3.0,-3.0,-3.0);
^
|
= glNormal3f(1.0, 0.0, 0.0);
See? And if you don't work on one axis, you'll need the FindNormal procedure. It's that simple.
Now a few remarks. Light is important. A scene only gets realistic if the lighting is properly set. Get yourself a good book on photography and look how good lighting is done. See how they use light to draw attention to specific areas of their pictures. You'll see - if you know how to control your light, your scenes will get more realistic by far.
Delax/ Sundancer Inc.
[delax@sundancerinc.de]
Back to previous page
|