Lab 04: Bird Photography

Goals

By the end of this lab, you will:

  • point a camera at a model in your scene,
  • practice some more with shading,
  • transform your model surface points using a model matrix and the surface normals using a normal matrix,
  • add mirror-like reflections in your scene.

I went for a hike recently and saw a great blue heron flying close to a lake. I thought it would make for a fun lab :) But I couldn't find a free model of a heron online, so we're going to use a raven which I found here and read it into our lab scripts using this webgl-obj-loader.

When you run the initial template, you should see a blue canvas, which is the color of the lake.

We're going to develop our own virtual camera in this lab, point it at the bird and take a picture. We'll also model the situation when the bird is rotating and we'll also add a reflection of the bird in the water.

Most of the implementation for this lab will be in the Camera takePicture function as well as the Triangle constructor. Of course, you may want to write additional functions to help in your implementation since some functionalities will be repeated, like calculating the color of the bird from our lighting model.

There are two objects in the scene: the bird and the lake. Each of these has a center and a color attribute. The details of the underlying classes for these two objects are implemented in utils.min.js, which you do not need to inspect in this lab. Just note that both of these objects have an intersect method which takes in a Ray object and returns information about the closest intersection point in JSON format:

{
  t: {Number} // ray parameter of intersection
  p: {vec3} // surface point (which is equal to ray.origin + t * ray.direction)
  n: {vec3} // surface normal (in world space) at intersection
  km: {vec3} // material diffuse reflection coefficient
}

Part 1: Creating camera rays with the view looking at the bird.

A lot of the setup will look similar to what we had in previous labs. In this lab, however, the dimensions of the image plane (width and height) are already calculated for you.

Please create rays and intersect them with the bird object, using the equations in the notes for pointing a camera at a specific target point. The target point is bird.center and the camera is placed at this.eye (of the Camera object). Use an "up" direction of (0, 1, 0). You can either develop the change-of-basis vectors yourself, or use the glMatrix targetTo function (but be careful that it includes a translation).

As a first debugging step, please just check for an intersection with the bird object using ixnB = bird.intersect(ray). If there is an intersection, set the color to ixnB.km, which should look like the following image:

Part 2: Add shading and reflections (for M status).

We will now add a light, which will be modeled as the distant sun, so we only have a direction - use a direction to the light of $\vec{\ell} = (-1, 1, 1)$ (remember to normalize). You can use any light color ($c_l$) and ambient color ($c_a$) you wish. When a ray intersects the bird, add the ambient and diffuse illumination terms (no need for a specular term here).

If the camera ray does not intersect the bird, check for an intersection with the lake. ixnL = lake.intersect(ray). Then cast a reflection ray off of the lake and check if the reflected ray intersects the bird. If so, set the color of the pixel $c$ to a mix of the bird color $c_{\mathrm{bird}}$ (shaded) with the lake color $c_{\mathrm{lake}}$: $c = 0.9 c_{\mathrm{bird}}+ 0.5 c_{\mathrm{lake}}$. No need to make your color calculation recursive since there will only be one bounce for this scene. You also don't need to add shading to the lake - you can just use a fraction of the lake $k_m$ value.

Part 3: Rotating the bird (for E status)

This last part should be implemented entirely in the Triangle constructor. The bird model is represented with a soup of triangles called a mesh which is read from the raven.obj file (OBJ files are a common format for representing 3d models). Each triangle is constructed from three vec3's as we did in Lab 2, which are saved as a, b and c in each Triangle object. The constructor now takes a fourth parameter n which is the outward normal vector to use for the triangle (saved in this.normal). When an intersection occurs, this.normal is returned as the normal to the surface at the intersection point.

The axis of the bird from tail to beak is aligned with the z-axis. Your job in this part is to rotate the bird about the z-axis with the bird center as the center of rotation. Please calculate the model matrix which performs the rotation and then modify the incoming a, b and c points directly in the Triangle constructor. Hint: think about translating, rotating and then translating back. Note that the bird center is printed in the console, which you will need to center the rotation. Here is what a rotation of $22.5^\circ$ ($\frac{\pi}{8}$) about the z-axis looks like.

Yes, this means we are calculating the same transformation matrix for every triangle, but it's okay because it's just a preprocessing step to our rendering algorithm.

Remember to transform the incoming normal vector too! Please see the notes for a description of how to transform normal vectors.

Part 4: Expressing $x_c$ and $y_c$ with a transformation matrix (also for E status).

This question can be answered at any point while working on the lab. Similar to Lab 2, please upload a picture of your response to your repl.

In the notes we have the $x$- and $y-$ coordinates of each pixel relative to the camera as:

$$ x_c = -\frac{w}{2} + w \frac{(i + 0.5)}{n_x}, \quad y_c = -\frac{h}{2} + h \frac{(n_y - 0.5 - j)}{n_y}. $$

Let's omit the $0.5$ part:

$$ x_c = -\frac{w}{2} + i\frac{w}{n_x}, \quad y_c = -\frac{h}{2} + h \frac{(n_y - j)}{n_y}. $$

Please express this as a transformation of the pixel indices i and j in the following form:

$$ \left[\begin{array}{c}x_c\\ y_c \\ 1\end{array}\right] = \mathbf{M}\left[\begin{array}{c}i\\ j\\ 1\end{array}\right] $$

where $\mathbf{M}$ is a 3x3 matrix.

If you want to make the water look realistic! (optional)

There is an additional flag that you can pass to the Lake constructor in index.html. Find where the Lake object is created and change the last parameter passed to the constructor to true. This will take a bit longer to render (about 2x for me) because the km value returned by the lake intersection will be looked up in the water.jpeg image which takes a little extra time. More on techniques to do this in about month! You can also add shadows if you want, but this is also optional since we just practiced with that in Lab 3.

Submission

The initial submission for the lab is due on Wednesday 10/11 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-10-04)