By the end of this lab, you will:
JavaScript
and glMatrix
,To access the lab template, please click the link in the Lab 2 Assignment Link
post on our Ed discussion board.
In this lab, you will start writing your own ray tracer. The objects in our scene will be defined in 3d, but we will mostly be rendering 2d-like scenes. We'll make our scenes look more 3d-like when we talk about shading techniques next week.
When you open up the initial web page (via Go Live
) you should see a sunset-like background which transitions from blue (top) to pink at the bottom:
All of the code you will write will be in the sunset.js
file which contains a few class definitions. In particular, there are class definitions for a Ray
, Sphere
, Triangle
and the SunsetRenderer
class.
Note that the SunsetRenderer
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
), as well as the objects in the scene. The first object is an instance of the Sphere
class which represents the sun. Next, there is an array of Triangle
objects which has a length of 3, for the three triangles representing the Green Mountains.
The SunsetRenderer
class saves the incoming information (fov
, canvas
, sun
and mountains
), 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
and Triangle
classes.
Note that there is already a loop over the pixels in the scene in the SunsetRenderer
render
method. The relative y-coordinate is used as the interpolation variable between blue and pink to define the initial background sky color.
Please review the notes and exercise from this week on how to create rays from a camera that pass through a particular pixel. Create a ray within the body of the nested for-loop (i.e. for each pixel). You should use the fov
as well as the image (or canvas) dimensions (and aspect ratio) to calculate the image plane dimensions (
Please complete the Sphere
intersect
method to detect intersections between the ray you compute in Part 1 and the sun. The sun is centered at
const cSun = vec3.fromValues(1.0, 0.894, 0.71);
When the ray intersects the sun, please set the pixel color to cSun
. Your image should look like the image on the left:
Part 3A: Now complete the Triangle
intersect
method to detect intersections between a ray and a single triangle. Remember to check that all three barycentric coordinates (
Recall that glMatrix
doesn't allow you to pass two indices (i.e. row and column) when accessing elements in a matrix. There is only one index, which corresponds to the entry in the 1d array that stores the matrix entries in column-major order. This means that a 3x3 matrix like this:
would be assigned to a glMatrix
mat3
like this:
let M = mat3.create();
M[0] = a;
M[1] = d;
M[2] = g;
M[3] = b;
M[4] = e;
M[5] = h;
M[6] = c;
M[7] = f;
M[8] = i;
In other words, think about going one column at a time when indexing the mat3
(as a 1d array) to access the elements.
Part 3B: Now set the pixel color according to the closest intersection point (minimum ray cMtn1
and cMtn2
, already defined for you right after the definition of cSky
. The first and third triangles have a color of cMtn1
and the second triangle (this.mountains[1]
of the SunsetRenderer
class) has a color of cMtn2
.
Once this is complete, your image should look like the rightmost image above.
The mountains kind of look like a green staircase, which is because of jaggies:
Recall the 0.5 embedded in the equations for Math.random()
to generate random numbers between 0 and 1. This will now sample each pixel at a random point within the pixel instead of the center. The objects in your scene should look pretty rough with a single sample. Next, add a loop (nested within the i
and j
loops for each pixel) to send several random rays within each pixel. The final color you assign to the pixel should be the average of all the colors you compute with your random samples - here is a description of this algorithm:
1. for each pixel:
a. set color = (0, 0, 0) (this will be the variable we use for the final color)
b. for some # samples (spp):
i. cast a ray through this pixel (at a random sample location)
ii. compute the color from this sample
iii. add the sample color to the final color
c. divide the final color by the # of samples (to compute the average)
d. set pixel color to the final color
You should start to see the edges of the mountains look smoother at higher spp. Notice there is a console.log
message at the end of the render function which reports the elapsed time to render the scene (in milliseconds) and saves this in the elapsed
variable. Please calculate the framerate (in FPS), and complete the table in the README.md
file with the framerate for 1spp, 4spp, 8spp, 16spp and 32spp.
The README.md
file is written in Markdown. Please see this page for an overview of Markdown syntax.
This part involves a theoretical question and can be completed at any point while working on the lab.
In the implementation for this lab, we set
With the eye at the origin
Please show that
Please enter your derivation in the README.md
file in your repository.
Math equations can be typeset using LaTeX (enclosed within either two $
for inline expressions or within two $$
for equations on new lines). You can also take a picture of your equations and upload the picture to your repository.
The initial submission for the lab is due on Thursday 3/6 at 11:59pm EST. Please see the Setup page for instructions on how to commit and push your work to your GitHub repository and then submit your repository to Gradescope (in the Lab 2 assignment). I will then provide feedback within 1 week so you can edit your submission.