| CS 381 Fall 2012 > Lecture Notes for Thursday, October 11, 2012 |
To compute the color of the light reflected from a Lambertian surface,
we need to know the intrinsic color of the surface:
the paint color.
We also need to know the light-source color.
In our lighting code, we took the paint color from gl_Color,
and we assume the light-source color was white.
If we wish to vary the light-source color,
then we can compute the apparent surface color by multiplying
the Lambert cosine by
by a vector formed by the coordinatewise product of
the light-source color with the paint color.
In GLSL, this coordinatewise product is performed by
the “*” operator.
[GLSL]
vec4 finalcolor = lambcos * lightcolor * paintcolor;
Light comes from somewhere.
How does the shader tell where?
We have hard-coded the light-source position into the shader.
It would be nicer to specify the position in the application.
Then, we could pass it to the shaders in a uniform variable.
However, the light source is essentially an object
(although perhaps not a visible object);
it would be convenient to move it around the same way we move other objects,
using the model/view transformation.
The tricky part is that the light source is in a different place from the object it lights; we need to use a different model/view transformation to position it. But our current code only allows for a single model/view transformation.
Fortunately, there is an OpenGL command involved in the FFP lighting that passes a light-source position through the current model/view transformation—that is, the transformation as it is at the time the lighting command is executed, not as it is when the primitive is drawn. I mostly avoid the FFP lighting facilities, but this one function is very useful.
The command is glLight*.
Usually we call glLightfv.
This takes three parameters.
GL_LIGHT0, GL_LIGHT1, etc.
Use GL_LIGHT0 if you have only one light source.GL_POSITION to set a light source’s
position.GL_POSITION this parameter is
a 4-item array of GLfloat values.
It gives the light-source position in object coordinates,
in homogeneous form.
Since we are treating the light source as an object,
and positioning it with the model/view transformation,
we generally place the light source at the origin:
\(\langle 0,0,0,1 \rangle\)
in homogeneous coordinates.The resulting code might be something like this.
[C++]
// The camera transformation should already have been done glPushMatrix(); glTranslated(1., 2., 3.); // Light source at (1, 2, 3) GLfloat origin4[] = { 0., 0., 0., 1. }; glLightfv(GL_LIGHT0, GL_POSITION, origin4); glPopMatrix(); // Now draw an object, using a different transformation
In a shader, light source information is given in the
array gl_LightSource, which is uniform.
Each array item is a struct giving information
about a particular light source.
The information for light source 0 (GL_LIGHT0)
is given in item 0, etc.
We will ignore most of the struct members,
but a couple of them are useful.
In particular, each array item has a member position,
which gives the light-source position, as a vec4,
in homogeneous form.
[GLSL]
vec4 lightpos4 = gl_LightSource[0].position; vec3 lightpos = lightpos4.xyz / lightpos4.w; // Don't forget to convert!
Note that the above value has already been modified by applying the model/view
transformation
at the time of the glLight* call.
It would be nice to have some visible representation of our light source—perhaps a glowing ball.
It is easy to draw something at the light-source position,
since we have already set up the model/view transformation.
Just draw right after the glLight* command.
Note that we probably want to turn shaders off when we do this.
[C++]
... glLightfv(GL_LIGHT0, GL_POSITION, origin4); glUseProgramObjectARB(0); // Shaders off glColor3d(1., 1., 1.); // white glutSolidSphere(0.1, 20, 15); // A glowing ball glPopMatrix(); // Now draw an object, using a different transformation
It is a good idea to move the lighting computations
into a separate function in our shader.
For our current illumination model,
we need five parameters:
light color, paint color, light position, vertex position,
and surface normal.
We can pass the first two as vec4 values,
and the last three as vec3 values,
with the surface normal already normalized.
Our return value can be the apparent surface color: a vec4.
[GLSL]
vec4 lambertLight(vec4 lightcolor, vec4 paintcolor, vec3 lightpos, vec3 vertpos, vec3 surfnorm) // Normalized { ...
Application
useshaders.cpp
and the shaders in
lambertvert_shaders.zip
were modified in line with the above ideas.
The Midterm Exam will cover all course material so far; see the posted lecture notes.
A short general review was done in class.
ggchappell@alaska.edu