Lab 03: BB-8

Goals

By the end of this lab, you will:

  • implement the Phong Reflection Model to shade surfaces in your scenes,
  • add shadows to your ray traced images,
  • (optional) add mirror-like reflections to objects in your scene.

In this lab, you will render BB-8 using the shading techniques we introduced this week. The primary objective is to implement the Phong Reflection Model and add shadows to the scene. An optional part is to make BB-8's eyes look like a mirror.

Please navigate to our replit Team and open the assignment. When you click the "Run" button, you should see a blue background for the sky.

All of the code you will write will be in the renderer.js file which contains a few class definitions. In particular, there are class definitions for a Ray, Sphere, and the Renderer class. A lot of the setup will look similar to what we did last week in Lab 2.

The Renderer class takes in (1) the canvasId (string) of the canvas in which we would like to render our scene, (2) the vertical field-of-view (fov), the objects in the scene and the light information: (1) the ambient color ca, and (2) a light object which as a position and color cl. The objects are all spheres (even the ground) and stored in an array of length 6 (planet, body, head, 2 eyes, and a remote droid).

The Renderer class saves the incoming information (fov, canvas, objects, ca, light), which is then used in the render method to render the scene. Your job primarily consists of modifying the render method as well as the intersect method of the Sphere class.

Before getting started, please have a look at the way the materials are setup in the index.html file. There are 4 materials here for the ground, BB-8, eyes and remote. Each of these is described using JSON (similar to a dictionary/map) and contains fields for the different material properties. At the very least, each of these has a type and the base material albedo (km). The type is a string which is either diffuse, specular or mirror. For specular and mirror types, there is also a ks field for the specular reflection coefficient.

Please note that the section headings below outline which portions of the lab are sufficient for an M status, and which portions would achieve an E status. Specifically, implementing the Phong Reflection Model would achieve an M, whereas adding shadows would achieve an E status. Making the eyes look like mirrors is optional, but good practice for the next lab.

Part 1: Create rays & intersect rays with spheres:

Although this will feel a bit repetitive, it's a good idea to revisit the techniques we used to (1) create rays and (2) intersect these rays with spheres. For each pixel (i, j), please create a ray that passes through the pixel starting from the eye. We are still using the eye as the point (0, 0, 0) in this lab.

Next, complete the intersect function in the Sphere class. You can copy over what you had for the sun from last week's lab (or from the exercise) to get started, but please note that this function now returns something different. In particular, we need to return information about the intersection point: the surface point at the intersection, the normal to the surface at this point and which object is intersected (so we can retrieve the material attribute).

Note that our Ray class has been extended with an evaluate function. Any point along the ray can be obtained as point = ray.evaluate(t) for some $t$ value and Ray object ray. You will need this to calculate the 3d coordinates of the intersection point. You will also need to calculate the normal vector to the surface (please see the notes on how to do this for a sphere).

Next, please complete the hit function of the Renderer class. This function will loop over all the objects in the scene (this.objects) and return the information about the closest intersection point. In other words, you should have a for loop over this.objects that finds the object with the minimum $t$ intersection value (if any) and returns information about the intersection (the same information returned by the Sphere intersect function: surface point, surface normal and object).

Please ensure the $t$ value for the intersection is greater than zero. It's usually a good idea to use a small tolerance (like 1e-5) but using 0 should be okay for this lab.

Part 2: Implement the Phong Reflection Model (M status).

Please complete the computeColor function to account for the lighting sources and materials in the scene. When an intersection occurs, please add the ambient, $I_a$, diffuse ($I_d$) and specular ($I_s$) contributions (if necessary). Note that some materials are diffuse, and some are specular, which can be determined through the ixn.object.material.type property (check if this attribute is diffuse or specular). Materials that have the mirror type should also have the specular ($I_s$) contribution.

To make sure this part works with the previous part, I would suggest starting by only returning the ambient color term: $I_a = c_a k_m$. You should then see something similar to the image below.

Next add the diffuse and specular terms. Please use a shininess exponent of your choice and feel free to adjust some of the parameters in the material definitions (those in index.html).

Part 3: Add shadows (E status).

Finally, please add shadows by checking if a shadowRay cast from the intersection point to the light.position hits an object in the scene and only returning the $I_a$ term in the computeColor function.

Part 4: Making BB-8 look more like BB-8 (optional, but only a few extra lines of code!).

There are two things we need to do to make BB-8 look more realistic:

  1. The sphere for BB-8's head should be cut off below some value. Technically, it should taper towards the body but we'll just cut off the sphere. There is a function that is added to the head object called cutoff (try to find this in index.html). In your Sphere intersect function please add the following line after finding tmin:
const point = ray.evaluate(tmin);
if (this.cutoff && this.cutoff(point)) return undefined;
  1. Instead of using a constant km for the head and body objects, let's make this a function. In fact, there is already a kmFunction defined for these two objects that will return orange or grey, depending on the input surface point. In your computeColor function, try using this variable km value:
let km = ixn.object.material.km;
if (ixn.object.kmFunction) km = ixn.object.kmFunction(ixn.p);

Part 5: Add mirrors (optional, but good practice for the lab next week).

This part is optional, but you will need to add mirror reflections next week, so it's good practice. Please see the pseudocode in the notes for a description of how to reflect an incoming ray off of a mirror-like surface. Then call the computeColor recursively with this new secondary ray. Whatever color is returned from the recursive call, I would suggest attenuating it by some factor (I used 0.25 below) before adding it to the final color. Try zooming in on the eyes to see the reflections of the planet and remote (look for the variable called zb in index.html and change this to -0.5).

Submission

The initial submission for the lab is due on Wednesday 10/04 at 11:59pm EDT. I will then provide feedback within 1 week so you can edit your submission.

When you and your partner are ready, please submit the assignment on replit. I will then make comments (directly on your repl) and enter your current grade status at the top of the index.html file.

Please also remember to submit your reflection for this week in the Google Form below (or click here to access the form).



© Philip Claude Caplan, 2023 (Last updated: 2023-09-28)