In the last lab we introduced basic lighting by creating materials and defining surface normals for our objects using a simple directional light. The light source we created was a single directional light source which was assumed to be at an infinite distance will all light rays parallel and thus striking the object at the same angle, e.g. the sun. While this produced a shading effect as the various faces of the cube rotated into and away from the light source, the effect was not very realistic (particularly considering we had created somewhat reflective materials). To extend the lighting effects, we will now consider how to create different light sources, position them within the scene, and tweak the normals per vertex.

0. Getting Started

Download CS370_Lab13.zip, saving it into the labs directory.

Double-click on CS370_Lab13.zip and extract the contents of the archive into a subdirectory called CS370_Lab13

Navigate into the CS370_Lab13 directory and double-click on CS370_Lab13.sln (the file with the little Visual Studio icon with the 12 on it).

If the source file is not already open in the main window, open the source file by expanding the Source Files item in the Solution Explorer window and double-clicking vertexNormalCube.cpp.

If the header file is not already open in the main window, open the header file by expanding the Header Files item in the Solution Explorer window and double-clicking lighting.h.

If the shader files are not already open in the main window, open the shader files by expanding the Resource Files item in the Solution Explorer window and double-click lightvert.vs and lightfrag.fs.

1. Point Light Sources

Another type of light source that we may wish to add to our scenes is a point light source, e.g. light bulb. This is a local light source, i.e. it has a position in world coordinates, that radiates light omnidirectionally, i.e. equally in all directions. Thus in addition to specifying the color channels for the ambient, diffuse, and specular components, we also will specify a position of the light source (note that this position is represented as a four element array with the last element 1.0). Additionally, we can also incorporate attenuation for these light sources such that the further away from the light source an object is, the dimmer the light will be on that object.

We then set the location property for a point light with the command

glLightfv(source, GL_POSITION, *pos);

where source is the light source symbolic constant (GL_LIGHT0, etc.) and pos is the array containing the position (x, y, z, 1.0). In lighting.h is a utility function that will set all the parameters for a point light

void set_PointLight(GLenum source, LightType *light, GLfloat *position);

which takes as arguments the source that we want to set, a LightType structure by reference for the color channels, and a position array for the location of the light source. (Note there is also a function named set_PointLightAttenuated( ) that takes additional attenuation parameters to create an attenuated point light source.)

Tasks

2. Spot Light Sources

Spotlights are similar to point lights in that they have a position in the scene, but instead of being omnidirectional they are unidirectional, i.e. they only shine in one direction. Furthermore, their illumination is limited to a cone eminating from the source as shown in the figure below

image

Thus to create a spotlight we need to specify the position of the light, the direction in which the light is pointing, and how wide the cone of illumination is. We set the position parameter similarly to point lights, and set the direction parameter using the command

glLightfv(source, GL_SPOT_DIRECTION, *dir);

where source is the source and dir is a 3 element array containing a vector direction (NOT a "look at" point).

The width of the cone is specified by a cutoff angle that is set by the command

glLightf(source, GL_SPOT_CUTOFF, angle);

where source is the source and angle is the angle of the light cone projecting from the light's position in the light's direction.

Finally we can control how the light intensity varies across the cone so that objects near the axis of the cone appear to be illuminated more brightly than objects near the edge of the cone using an attenuation exponent (which is computed similarly to the shininess coefficient for materials). The default value for the exponent is 0 which produces uniform illumination throughout the illumination area. Positive values will produce brighter illumination at the center (directly in front of the light) and weaker illumination towards the boundary of the light cone. NOTE: Be careful when making very focused spotlights (e.g. a "laser pointer") as the geometry of the scene may not have sufficient detail to render the point effect. Also remember that if your light (or material) has no specular components, the spotlight may have little to no effect. The spotlight exponent is set using the command

glLightf(source, GL_SPOT_EXPONENT, exp);

where source is the source and exp is the spotlight attenuation exponent.

In lighting.h is a utility function that will set all the parameters for a spot light

void set_SpotLight(GLenum source, LightType *light, GLfloat *position
                   GLfloat *dir, GLfloat cutoff, GLfloat exp);

which takes as arguments the source that we want to set, a LightType structure by reference for the color channels, a position array for the location of the spotlight, a dir array for the direction the spotlight is pointing, a cutoff angle for the cutoff angle, and an exp for the attenuation exponent. (Note there is also a function named set_SpotLightAttenuated( ) that takes additional attenuation parameters to create an attenuated spotlight source.)

Tasks

3. Vertex Normals

In the last lab we defined the normals for an entire surface, i.e. the normals for each vertex defining a surface were identical. This produces a uniform lighting effect across the surface and is useful for small surfaces where the variation of the surface orientation with respect to the light is negligible. However, for larger surfaces or ones with significant contour, having a continuously varying normal across the surface produces a more realistic effect. This is accomplished by setting different normals per vertex that the graphics pipeline will interpolate across the surface. Thus just like when different colors are set per vertex (which is essentially what varying the normal does) the resulting effect will be a gradient shading of the surface depending on which vertex normals are pointing more directly at the light source.

Tasks

4. Shader Light Sources

Much of the computations for point and spot light sources is similar to directional lighting, except now we will need to compute the l vector as the difference between the light source position and the vertex (giving a vector pointing from the vertex to the light source). Also for spotlights, we will need to determine if the vertex is inside the cone of illumination for the spotlight and apply any attenuation depending on where in the cone the vertex falls. Note that we will assume the viewer is far away and thus can set a default eye vector. For more accurate lighting effects, we can compute the eye position (known as a local viewer).

Tasks

Compiling and running the program

Once you have completed typing in the code, you can build and run the program in one of two ways:

(On Linux/OSX: In a terminal window, navigate to the directory containing the source file and simply type make. To run the program type ./vertexNormalCube.exe)

The output should look similar to below

image

To quit the program simply close the window.

While using vertex normals and additional light source effects improves the scene, it still does not appear quite realistic. This is most evident by what appears to be "bursting" of the light along the diagonals of the cube faces. This is an artifact of the large, flat surface being coarsely tesselated and thus having minimal geometry for the lighting effect. Unfortunately the only way to improve this situation is by adding additional geometry primarily to the flat surfaces (and accepting the resulting performance penalty that will be incurred). Rather than adding additional vertices manually, next time we will see how to automate the geometry generation through recursive subdivision which will dramatically improve the lighting effects (particularly for spotlights).