CPSC 424 | Computer Graphics | Fall 2025 |
Lab 4 focused on incorporating shading (applying a lighting model to determine pixel colors) into the graphics pipeline. This lab builds on that foundation and shifts the focus more to lighting in the scene — multiple lights, different types of lights, and placing lights in the scene.
Successful completion of this lab means that you:
This is an individual lab. You may get technical help from others, but the effort and ideas that go into producing solutions should be yours.
You may use AI as outlined in
the Use of AI policy —
Copilot's inline coding suggestions, explain, fix, review and
comment features but not code generation from English prompts.
Also:
Hand in your work by copying your ~/cs424/lab5 folder into your handin folder (/classes/cs424/handin/username, where username is your username).
Check that the result is that your files are contained in /classes/cs424/handin/username/lab5 — if not, fix it!
Copy the directory /classes/cs424/lab5 and its contents into your ~/cs424/workspace directory. You should end up with a folder lab5 inside your workspace directory, with files inside of it.
Make sure that your lab5 directory is named exactly like that and is at the same level in your workspace directory. This is important so that the relative path names used to access common files remain the same so your program doesn't break when you hand it in.
Your best reference sources are the slides from class (which pull out and organize the key points), the examples from class (which put all the pieces together), and the textbook.
The provided lab5.html file contains the same diffuse+specular per-vertex shading as the provided lighting-demo.html from lab 4 with one addition — an enabled flag for the light so the light can be turned on and off.
Run lab5.html to see how it works. You can toggle light 0 on and off, but the other checkboxes currently have no effect.
Look at the shader code to locate the enabled flag. Make sure you understand how it is used.
You are encouraged to replace the provided shaders with your Phong shaders from lab 4, but if you don't have those working or would rather not use them for some other reason, it is fine to continue with the provided shaders. Note that you will need to add support for the enabled flag to your shaders.
To manage the many shader parameters needed for materials and for lights, it is convenient to bundle related properties into structs.
Edit lab5.html for this exercise.
Replace the current sets of light and material properties uniforms in the shader with the following:
struct LightProperties { vec4 position; // light position (EC) vec3 color; // light color (intensity) bool enabled; // is the light enabled? }; struct MaterialProperties { vec3 diffuse; // diffuse color vec3 specular; // specular color float shininess;// specular exponent (shininess) }; // light properties uniform LightProperties u_light; // material properties uniform MaterialProperties u_material;
If you are using your own Phong shaders, include the ambient intensity in LightProperties.
Update the shader to reflect the new structs and field names. At a minimum this means updating the places where the old names were used (in main). Optionally, you could also take advantage of the convenience of the structs for passing multiple values in one unit and change the parameters to diffuse() and specular() to take MaterialProperties and LightProperties parameters instead of the individual values needed.
Update the JavaScript program to reflect the new structure and parameter names. Even though you could still get away with the same separate location variables, make that structure mimic the GLSL data structures — use JavaScript objects to group the location variables for a single light and a single material.
When you are done, the program should work just like it did before you started this part.
To support multiple lights, we change the single light parameter to an array of lights in the shader.
Continue editing lab5.html for this part.
Change the single light to an array of 8 lights in the shader:
uniform LightProperties u_lights[8];
(Note the change of name from u_light to u_lights.)
Update the shader to work with the array of lights: this means that the final color computed should be the sum of the diffuse and specular terms for each enabled light.
Update the JavaScript program to reflect the new structure (an array of lights) and parameter name. Continue to reflect the GLSL data structures in the JavaScript program organization — use an array to hold the objects holding the individual shader parameter location values for the light properties. Note that there's a constant MAX_LIGHTS that you can use instead of hardcoding the value 8.
Also be sure to update setLights to set the shader parameters for all of the lights and not just the first one (locate the TODO: use only the first light for now comment).
You should now be able to toggle the checkboxes for all of the lights. Look at the getLights function to see how the lights are configured. Leave the first four as they are, but feel free to set up your own lights for the others (see the TODO: configure the remaining lights as desired comment).
We have so far only been working with point lights and directional lights, and also haven't captured the notion of attenuation. Spotlights and attenuation were discussed in class — they add additional properties (a0, a1, and a2 for attenuation; direction, cutoff angle, and an exponent capturing the falloff of light intensity away from the spotlight's direction for spotlights) and are handled by multipliers on the diffuse and specular terms in the lighting equation.
Technical note:
Continue editing lab5.html for this part.
Add the necessary properties for spotlights and attenuation to LightProperties in the shader. To keep things organized because not all of the properties are applicable to all kinds of lights, consider the following:
struct PositionalProperties { vec3 attenuation; // a0, a1, a2 }; struct SpotProperties { vec3 direction; // spot direction in eye coords float cutoff; // spot cutoff angle in degrees (0 to 180) float falloff; // spot exponent }; struct LightProperties { vec4 position; // eye coords, w=0 for directional lights, w=1 for positional lights vec3 color; bool enabled; // is the light enabled? PositionalProperties point; bool isSpot; SpotProperties spot; };
(Also include the ambient intensity in LightProperties if you are using your Phong shaders.) The isSpot boolean isn't strictly necessary because the cutoff angle could be set to 180 — a point light is a spotlight whose cone of illumination extends in all directions — but it makes it a little more user-friendly when it comes to defining lights. (One only has to consider setting values for the spotlight properties when defining spotlights, for example.)
Update the lighting equation in the shader to include spotlights and attenuation. Remember that attenuation only applies to positional lights, not directional lights. And, of course, the spotlight multiplier should only be a factor for spotlights.
Update the JavaScript program to pass appropriate values for the shader parameters. Leave the first four lights defined in getLights as is (though you should add appropriate settings for a0, a1, and a2 so there is no attenuation effect) but feel free to modify the configuration of the other four lights so you can test spotlights and attenuation.
The provided lighting equation in lab5.html has no ambient term, and the lighting equation from lab 4 includes an ambient contribution from each light in the scene. Optionally add a global ambient light type — these light(s) would contribute only ambient terms to the equation, not (also) diffuse and specular.
The provided scene.html contains a scene with several objects, including a rotating teapot. It is similar to lab5.html in terms of the lighting model and shaders — the difference in the program structure is the addition of animation.
The user interface provides a radio button to select the lighting in the scene. In the provided code, only the default option (viewer light) does anything.
Edit scene.html for this exercise.
Locate the three TODO comments in the "set light" section in draw, and implement them to set up a white light with the type (directional or point) and positioning indicated.