Assignment 3: PathTracer, Spring 2020

Calvin Wong and Chris Park - ART GUD (A Ray Tracer, Greatly Upgraded Development)

256 samples per pixel, 4 light rays, depth 5, focal lens size .06
256 samples per pixel, 4 light rays, depth 5, focal lens size .06
256 samples per pixel, 4 light rays, depth 5, focal lens size .06

Summary: We worked on implementing reflective and refractive BSDFs (mirrors and glass), and simulating depth of field of real life cameras. We learned not only how to simulate these properties, but get a feel for how the physics behind it worked. It was interesting (and certainly very artistically pleasing to see) a breakdown of the "magic" behind what our eyes see, and how we could recreate that. For example, it was intuitive that mirrors reflected back at a similar angle to what went in, but it was not quite so intuitive that glass both reflects and refracts light, and quickly creating such an effect involved another neat application of clever statistics. We were also were able to connect topics from before, such as antialiasing averaging nearby pixels, causing blur being a concept that was sort of baked into how depth of field worked. Pretty impressive that people figured out how vision worked, and pretty pretty, if we might say so ourselves!

Part 1: Mirrors and Glass (It's not smoke and mirrors hehe)

In part 1, we implemented the ability to generate mirror and glass materials. Mirror was basic reflection modeling, whereas glass was more involved, combining reflection, refraction, and statistics to more accurately blend between the two. In terms of the actual implementation:

This was done by first implementing the reflection function to calculate the reflection of the given ray about the normal (0, 0, 1) in object space. The reflection function is then used to render mirror material by dividing the reflectance value of the mirror object with the absolute cosine of the reflected ray and the normal so that the correct reflectance value for the ray is outputted.

To implement refraction, we use Snell's Law. We check for total internal reflection and if there isn't, the output ray's x, y, and z values are set as the inverse of the input ray multiplied by the ratio of old index of refraction to new index of refraction. The ratio of refraction indices in Snell's Law, η, is calculated accordingly based on whether the input ray is entering or exiting the material. We calculated the issue of total internal reflection using:

1 - (η^2 * (1 - wo.z^2)) < 0

The ability to render glass material was also implemented. To do so, we used Schlick's approximation in the case that there is no total internal reflection to calculate the contribution of the Fresnel factor in the specular reflection of light. If there was total interal reflection, we reflected the inputted ray. Otherwise, we either reflected or refracted the incoming ray by flipping a coin using Schlick's approximation, R, as its probability values. The pdf values and our return values were set to the correct values corresponding to the cases listed in the spec. In our Schlick's approximation calculations, we assumed that at least one of the materials involved in our refraction was air, which means that n1 was set to 1. Schlick's approximation was calculated using:

R0 = ((1 - ior) / (1 + ior))^2

R = R0 + (1 - R0) * (1 - abs_cos_theta(wo))^5


The images below are rendered at 256 samples per pixel and 4 samples per light.

At max ray depth of 0, we get a picture of a black screen with only the light source visible. This makes sense, as with zero bounce illumination, we can only see objects that emit light. At the max ray depth of 1, we get one bounce rays that illuminate our black spheres, with a small area of reflection. This is because our spheres require more than one ray depth in order to reflect or refract the incoming INDIRECT light. The only thing that we can do with one bounce is show direct light (so the reflections of the light source do indeed work) and shadows (which aren't physically accurate yet, since light cannot bounce through the spheres).

Max ray depth 0
Max ray depth 1

At max ray depth of 2, we start to get reflections on the mirror sphere while the transparent sphere is still dark, due to some indirect lighting working. The ray depth of 2 allows for rays to reflect back to the mirror surface but doesn't allow refraction because light must first go from the light (1 bounce) onto the sphere's surface, then refract in the sphere (2 bounces), then refract again to outside the sphere (so need this 3rd bounce). We can clearly see this happening in our ray depth of 3. However, the reflection of the right glass sphere on the mirror sphere is still dark since there just hasn't been enough bounces to lighten it fully yet.

Max ray depth 2
Max ray depth 3

At max ray depth of 4, we see that the glass sphere refracts light onto the right wall and that its reflection on the mirror ball is now correct; this is because we needed that 4th bounce to reflect the refraction of the glass ball. With a ray depth of 5, we see that the glass sphere gets brighter because more light is able to get refracted out of the sphere.

Max ray depth 4
Max ray depth 5

At max ray depth of 100, we see that the image is brighter because more light is getting refracted and bounced around with the transparent glass ball. However, this image is pretty similar to that of the image rendered with ray depth of 5, since after 5 bounces, there isn't anything significant to "correct" (compared to for example, ray depth 2 -> 3 correcting refraction).

Max ray depth 100

Part 4: Depth of Field

In this part, we simulated a real life camera's ability (and possibly its curse) of having a certain range where objects were in focus (not blurry), where objects outside the range were out of focus (blurry). This occurs because we now create rays which hit uniformly sampled points on a thin lens (ie: it's a disc), in contrast to a pinhole model where all rays pass through the disc's center.

Visually, it looks like this:

This has no visual effect on points along the plane of focus, since our rays will always hit that point on the plane, and thus get radiance from only that point. However, on points not on the plane of focus, we'll be getting the radiance of a cone of points, centered at that point we are trying to get, so we will effectively be averaging points close to our point of interest.

This leads to the depth of field effect shown below (and in our very top images), since the plane of focus is not blurred, but points not on the plane of focus become blurrier (due to averaging points nearby, rather than just the point of interest).

Previously, our pinhole model always hit the correct point, since we had our rays always passing through the origin, and thus there was no blur/depth of field in comparison to this thin lens model.


The images below are rendered at 64 samples per pixel, 4 samples per light, and 5 bounces.

0.05 lens radius
0.11 lens radius
0.26 lens radius
0.41 lens radius
0.61 lens radius
1.21 lens radius
1.61 lens radius

As evident above, increasing the lens radius decreases the range that is in focus, effectively increasing the blurriness of out of focus objects.


The images below are rendered at 96 samples per pixel, 4 samples per light, 5 bounces, and lens size 0.06.

Focal distance 3.2
Focal distance 3.7
Focal distance 4.2
Focal distance 4.7
Focal distance 5.5
Focal distance 6.4

As evident above, changing the distance changes where the range's "center" is. (eg: The bunny is in focus around distance 4.2 to 4.7)

A Note on Partner Collaboration

We collaborated by taking turns "piloting," where we both discussed approaches, then coded one at a time. This helped reduce merge conflicts and bugs, but, we didn't always have time to meet together, so in particular for this project part 1 was mostly implemented by Chris, and part 4 was implemented by Calvin (we both discussed pseudocode and the relevant slides together for both parts, however). There luckily weren't many hiccups since this project was less intense, and all in all it went pretty well, learned more than we would have alone (eg: connecting to antialiasing)!