CPSC 424 | Computer Graphics | Fall 2025 |
This lab adds shading to the programmable pipeline structure from lab 3.
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/lab4 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/lab4 — if not, fix it!
Copy the directory /classes/cs424/lab4 and its contents into your ~/cs424/workspace directory. You should end up with a folder lab4 inside your workspace directory, with files inside of it.
Make sure that your lab4 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.
Live Preview now appears to be working on all Linux platforms! (VDI, Demarest 002, Lansing 310) Please let me know if you encounter issues.
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 lighting-demo.html file adds basic lighting to the graphics pipeline. The lighting model implemented is similar to what was discussed in class — it includes diffuse and specular terms, supports point and directional lights, and does per-vertex lighting. There is one addition, which is a shader parameter for the light color instead of using a hardcoded white light.
Run/view lighting-demo.html to see what it does, and try different shapes, materials, and light types, positions, and colors. The "user defined" option for each allows you to include your own settings for testing or experimental purposes — look for the TODO comments if you want to fiddle with those. (You don't need to define any user-defined elements; the option is just there if you want it.)
Review lighting-demo.html, noting in particular the elements related to lighting: the vertex and fragment shaders and the steps involved in passing values from the JavaScript program to the shaders. Also note how the code has been organized, both in the vertex shader and in the JavaScript program. Make sure you understand the lighting-related code!
The Lambert illumination model captures diffuse reflection and is appropriate for matte surfaces. The lighting model includes the ambient and diffuse terms discussed in class (no specular term) and is combined with flat or Gouraud shading (per-vertex lighting with interpolated colors).
Handling ambient light: Rather than including a global ambient term as in the lighting model discussed in class, implement an "ambient contribution" from each light source (as it works in OpenGL 1.1). Also take the material's ambient color to be the same as its diffuse color. This means that the ambient term will be md ai Is (where ai is the fraction of the light's intensity that goes towards the overall ambient light in the scene, a value 0-1) instead of ma Ia from class.
Save a copy of lighting-demo.html as lambert.html, then edit lambert.html for this exercise.
Change the page title (in the <title> tags at the beginning of the <head> section) and the displayed header (in the <h2> tags at the beginning of the <body> section) from "Lighting Demo" to "Lambert Shading".
Implement the Lambert illumination model with per-vertex lighting and interpolated colors. Since the provided code already does per-vertex lighting with interpolated colors, you only need to update the lighting model — remove the specular term and add an ambient term, updating the shader parameters and the info passed from JavaScript accordingly.
The Phong illumination model captures specular reflection and is appropriate for shiny surfaces. The lighting model includes all three of the terms discussed in class (ambient, diffuse, and specular) and is typically combined with the Phong interpolation method (per-pixel lighting with interpolated normals).
The ambient term will be handled the same way as in the previous exercise.
Save a copy of lighting-demo.html as phong.html, then edit phong.html for this exercise.
Change the page title (in the <title> tags at the beginning of the <head> section) and the displayed header (in the <h2> tags at the beginning of the <body> section) from "Lighting Demo" to "Phong Shading".
Implement the Phong illumination model with per-pixel lighting and interpolated normals. Do this in two steps:
Add an ambient term to the lighting model. (This will be the same as what you did in the previous exercise for Lambert, just without also removing the specular term.)
Implement per-pixel lighting instead of per-vertex lighting. This means moving the lighting computations (and the associated arguments) from the vertex shader to the fragment shader, and passing the (object coordinates) normal instead of the vertex color from the vertex shader to the fragment shader.
Cel shading was discussed in class on Monday (9/23); see the posted slides.
Save a copy of your phong.html as cel.html, then edit cel.html for this exercise.
Change the page title (in the <title> tags at the beginning of the <head> section) and the displayed header (in the <h2> tags at the beginning of the <body> section) from "Phong" to "Cel Shading".
Cel shading is really just a matter of quantizing the result of some other lighting model. Use the simple quantization scheme described in the slides with the Phong illumination model you already have. Add the number of levels of quantization as a shader parameter; you can hardcode the value sent from JavaScript (pick a value that results in a noticeable and pleasing effect).
This style of shading looks best with a black outline around the edges of the object. How to achieve this was discussed in class — the strategy is to first draw the back faces slightly larger, then draw the front faces normal sized. For smooth surfaces approximated by a polygon mesh, "slightly larger" can be achieved by displacing the vertex coordinates a small amount along their normals. In particular, to achieve the silhouette effect:
Add a bool parameter u_silhouette to the vertex shader. Use it in the body of the shader by adding the if shown at the beginning of main:
vec4 coords = vec4(a_coords,1.0); if ( u_silhouette ) { coords += vec4(.03*a_normal,0); }
Add a bool parameter u_silhouette to the fragment shader. Use it in the body of the shader by setting gl_FragColor to black if u_silhouette is true and using the lighting equation to determine the color if it is false.
In draw(), enable culling, set the cull face to gl.FRONT (so that only back faces are drawn), draw the shape with u_silhouette set to 1 (true), then set the cull face to gl.BACK (so that only front faces are drawn) and draw the shape again with u_silhouette set to 0 (false). Note that to "draw the shape again", you only need to set the shader parameters whose values have changed (u_silhouette) before calling drawElements again. See the slides for how to enable culling and set the cull face.
Gooch shading was discussed in class on Monday (9/23); see the posted slides.
Save a copy of your cel.html as gooch.html, then edit gooch.html for this exercise.
Change the page title (in the <title> tags at the beginning of the <head> section) and the displayed header (in the <h2> tags at the beginning of the <body> section) from "Cel Shading" to "Gooch Shading".
Change the lighting model to use Gooch shading: remove the quantization step applied for cel shading and replace the standard diffuse term in the lighting model with the Gooch diffuse term. b, y, α, β should be added as parameters to the (appropriate) shader. You can hardcode the values sent from JavaScript (pick values that lead to a noticeable and pleasing effect).
This style of shading looks best with a black outline around the edges of the object — apply the same silhouette effect as with cel shading. (Which means there's nothing additional to do here.)