|
|
|
Basic
Light-Sourcing
A useful
addition If instead of using a vector from the camera to the object, we position a light source in global space, then transform it's position into camera space, we can then use a light-source-to-object vector in conjunction with the face normal to calculate how much light is hitting the face. This is a fairly simple form of lighting, only considering the angle the face makes with the direction to the light source. This element is typically called diffuse light. It is also possible to consider both the camera-to-object vector and the light-source-to-object vector to compute specular lighting. This tutorial will only deal with the diffuse lighting element. Also, I will leave up to you the implementation of how to use the lighting values we will produce, since that is platform dependent, and depends on the type of shading used. Basically the following calculation will give a floating point value between 0.0 and 1.0 for the amount of light hitting a face. It is also very simple to extend this method to take into account the distance between the object and light-source. The details
- what is a light? So, we have a light at x, y, z and a triangle with a face normal already calculated. The first thing we need to do is to compute a vector from the light-source to the triangle. This means we need to transform the light's position into camera space. An alternative to this method would be to use the lights position in global space and the triangles position in global space. There is little advantage to either method, so either will do. The good news is that we already have a matrix for transforming a point from global space into camera space. We can simply use the same matrix we used when we transformed our triangles from global to camera space. So lets look at a data structure we could use to store a light. struct
light And naturally the code for transforming the light using the matrix is the same as we saw before in the matrices tutorial, so there's no need to repeat it here. We now need to use the transformed light coordinates cx, cy and cz to compute a vector from the light to any point on our triangle. The other data we will use is the camera space coordinates of the triangle and the face normal, again in camera space. So we can work out the vector like so- //
Local variables
// Take Vector from light source to point 0 of our
triangle
where omni is of type light, lv is the light-source to object vector we are calculating, v is an array of vertices, v[0] is the first vertex of our triangle (in camera space), and v[0].cx is the camera space x-coordinate of the first transformed vertex. All we are doing here is calculating a vector as we've seen before, then calculating it's length so we can normalize it (make it length 1.0). Now all that remains is to work out the angle between the vector lv and the face normal. Here's how- //
Compute angle between vectors
// Check if surface is facing away from light source where cnx,
cny and cnz
are the x, y and z component of the camera space face normal associated
with our triangle, and v.brightness
is a floating point value for storing the overall brightness of a face.
As you can see, we have used the dot product again, which computes
the cosine of the angle between the two vectors. In backface culling,
we were only interested whether the result was positive (triangle gets
culled) or negative (triangle is drawn), whereas in this case we are interested
in the actual magnitude of the result. Again, any result > 0
means the face is facing away from the light source, and hence receives
no light from it. If the result is less than 0 we simply store it
in the variable brightness and flip the sign. As the dot product
calculates the cosine of the angle, which will naturally be in the range
-1.0 to 1.0 there is no need to extract the actual angle, since we already
have what we were looking for, a brightness value between 0.0 and 1.0.
All we need to do is switch the sign as we store the value. Conclusion In the next tutorial I intend to briefly leave behind platform independance and show you the techniques and code we have been through so far in action in a real program, probably using DirectDraw7. This should help fill in any gaps that the previous tutorials may have left. |
|
All
content copyright © Simon Brown 1999-2005. |