JavaScript EditorFree JavaScript Editor     Ajax Editor 



Main Page
Previous Page
Next Page

9.2. Light Sources

The lighting computations defined by OpenGL are somewhat involved. Let's start by defining a function for each of the types of light sources defined by OpenGL: directional lights, point lights, and spotlights. We pass in variables that store the total ambient, diffuse, and specular contributions from all light sources. These must be initialized to 0 before any of the light source computation routines are called.

9.2.1. Directional Lights

A directional light is assumed to be at an infinite distance from the objects being lit. According to this assumption, all light rays from the light source are parallel when they reach the scene. Therefore a single direction vector can be used for every point in the scene. This assumption simplifies the math, so the code to implement a directional light source is simpler and typically runs faster than the code for other types of lights. Because the light source is assumed to be infinitely far away, the direction of maximum highlights is the same for every point in the scene. This direction vector can be computed ahead of time for each light source i and stored in gl_LightSource[i].halfVector. This type of light source is useful for mimicking the effects of a light source like the sun.

The directional light function shown in Listing 9.6 computes the cosine of the angle between the surface normal and the light direction, as well as the cosine of the angle between the surface normal and the half angle between the light direction and the viewing direction. The former value is multiplied by the light's diffuse color to compute the diffuse component from the light. The latter value is raised to the power indicated by gl_FrontMaterial.shininess before being multiplied by the light's specular color.

Listing 9.6. Directional light source computation

void DirectionalLight(in int i,
                      in vec3 normal,
                      inout vec4 ambient,
                      inout vec4 diffuse,
                      inout vec4 specular)
{
     float nDotVP;         // normal . light direction
     float nDotHV;         // normal . light half vector
     float pf;             // power factor

     nDotVP = max(0.0, dot(normal,
                   normalize(vec3(gl_LightSource[i].position))));
     nDotHV = max(0.0, dot(normal, vec3(gl_LightSource[i].halfVector)));

     if (nDotVP == 0.0)
         pf = 0.0;
     else
         pf = pow(nDotHV, gl_FrontMaterial.shininess);

     ambient  += gl_LightSource[i].ambient;
     diffuse  += gl_LightSource[i].diffuse * nDotVP;
     specular += gl_LightSource[i].specular * pf;
}

The only way either a diffuse reflection component or a specular reflection component can be present is if the angle between the light source direction and the surface normal is in the range [-90°, 90°]. We determine the angle by examining nDotVP. This value is set to the greater of 0 and the cosine of the angle between the light source direction and the surface normal. If this value ends up being 0, the value that determines the amount of specular reflection is set to 0 as well. Our directional light function assumes that the vectors of interest are normalized, so the dot product between two vectors results in the cosine of the angle between them.

9.2.2. Point Lights

Point lights mimic lights that are near the scene or within the scene, such as lamps or ceiling lights or street lights. There are two main differences between point lights and directional lights. First, with a point light source, the direction of maximum highlights must be computed at each vertex rather than with the precomputed value from gl_LightSource[i].halfVector. Second, light received at the surface is expected to decrease as the point light source gets farther and farther away. This is called ATTENUATION. Each light source has constant, linear, and quadratic attenuation factors that are taken into account when the lighting contribution from a point light is computed.

These differences show up in the first few lines of the point light function (see Listing 9.7). The first step is to compute the vector from the surface to the light position. We compute this distance by using the length function. Next, we normalize VP so that we can use it in a dot product operation to compute a proper cosine value. We then compute the attenuation factor and the direction of maximum highlights as required. The remaining code is the same as for our directional light function except that the ambient, diffuse, and specular terms are multiplied by the attenuation factor.

Listing 9.7. Point light source computation

void PointLight(in int i,
                in vec3 eye,
                in vec3 ecPosition3,
                in vec3 normal,
                inout vec4 ambient,
                inout vec4 diffuse,
                inout vec4 specular)
{
    float nDotVP;         // normal . light direction
    float nDotHV;         // normal . light half vector
    float pf;             // power factor
    float attenuation;    // computed attenuation factor
    float d;              // distance from surface to light source
    vec3  VP;             // direction from surface to light position
    vec3  halfVector;     // direction of maximum highlights

    // Compute vector from surface to light position
    VP = vec3(gl_LightSource[i].position) - ecPosition3;

    // Compute distance between surface and light position
    d = length(VP);

    // Normalize the vector from surface to light position
    VP = normalize(VP);

    // Compute attenuation
    attenuation = 1.0 / (gl_LightSource[i].constantAttenuation +
                         gl_LightSource[i].linearAttenuation * d +
                         gl_LightSource[i].quadraticAttenuation * d * d);

    halfVector = normalize(VP + eye);

    nDotVP = max(0.0, dot(normal, VP));
    nDotHV = max(0.0, dot(normal, halfVector));

    if (nDotVP == 0.0)
        pf = 0.0;
    else
        pf = pow(nDotHV, gl_FrontMaterial.shininess);

    ambient += gl_LightSource[i].ambient * attenuation;
    diffuse += gl_LightSource[i].diffuse * nDotVP * attenuation;
    specular += gl_LightSource[i].specular * pf * attenuation;
}

One optimization that we could make is to have two point light functions, one that computes the attenuation factor and one that does not. If the values for the constant, linear, and quadratic attenuation factors are (1, 0, 0) (the default values), we could use the function that does not compute attenuation and get better performance.

9.2.3. Spotlights

In stage and cinema, spotlights project a strong beam of light that illuminates a well-defined area. The illuminated area can be further shaped through the use of flaps or shutters on the sides of the light. OpenGL includes light attributes that simulate a simple type of spotlight. Whereas point lights are modeled as sending light equally in all directions, OpenGL models spotlights as light sources that are restricted to producing a cone of light in a particular direction.

The first and last parts of our spotlight function (see Listing 9.8) look the same as our point light function (see Listing 9.7). The differences occur in the middle of the function. A spotlight has a focus direction (gl_LightSource[i].spotDirection), and this direction is dotted with the vector from the light position to the surface (VP). The resulting cosine value is compared to the precomputed cosine cutoff value (gl_LightSource[i].spotCosCutoff) to determine whether the position on the surface is inside or outside the spotlight's cone of illumination. If it is outside, the spotlight attenuation is set to 0; otherwise, this value is raised to a power specified by gl_LightSource[i].spotExponent. The resulting spotlight attenuation factor is multiplied by the previously computed attenuation factor to give the overall attenuation factor. The remaining lines of code are the same as they were for point lights.

Listing 9.8. Spotlight computation

void SpotLight(in int i,
               in vec3 eye,
               in vec3 ecPosition3,
               in vec3 normal,
               inout vec4 ambient,
               inout vec4 diffuse,
               inout vec4 specular)
{
    float nDotVP;           // normal . light direction
    float nDotHV;           // normal . light half vector
    float pf;               // power factor
    float spotDot;          // cosine of angle between spotlight
    float spotAttenuation;  // spotlight attenuation factor
    float attenuation;      // computed attenuation factor
    float d;                // distance from surface to light source
    vec3 VP;                // direction from surface to light position
    vec3 halfVector;        // direction of maximum highlights

    // Compute vector from surface to light position
    VP = vec3(gl_LightSource[i].position) - ecPosition3;

    // Compute distance between surface and light position
    d = length(VP);

    // Normalize the vector from surface to light position
    VP = normalize(VP);

    // Compute attenuation
    attenuation = 1.0 / (gl_LightSource[i].constantAttenuation +
                         gl_LightSource[i].linearAttenuation * d +
                         gl_LightSource[i].quadraticAttenuation * d * d);

    // See if point on surface is inside cone of illumination
    spotDot = dot(-VP, normalize(gl_LightSource[i].spotDirection));

    if (spotDot < gl_LightSource[i].spotCosCutoff)
        spotAttenuation = 0.0; // light adds no contribution
    else
        spotAttenuation = pow(spotDot, gl_LightSource[i].spotExponent);

    // Combine the spotlight and distance attenuation.
    attenuation *= spotAttenuation;

    halfVector = normalize(VP + eye);

    nDotVP = max(0.0, dot(normal, VP));
    nDotHV = max(0.0, dot(normal, halfVector));

    if (nDotVP == 0.0)
        pf = 0.0;
    else
        pf = pow(nDotHV, gl_FrontMaterial.shininess);

    ambient  += gl_LightSource[i].ambient * attenuation;
    diffuse  += gl_LightSource[i].diffuse * nDotVP * attenuation;
    specular += gl_LightSource[i].specular * pf * attenuation;
}


Previous Page
Next Page




JavaScript EditorAjax Editor     JavaScript Editor