We massively leveled up our raytracer in this assignment. First, we added support for mirror and glass surfaces. Then, we opened things up to all sorts of new microfaceted materials. Then we were mesmerized by environment lights and finally captivated by being able to change the depth of field
Part 1: Mirror and Glass
Implementation notes:

For mirror/ glass reflection was a simple one line transformation.

For Glass materials, we had to implement refraction. We used the refraction index and Snell's law to change the direction of the incoming ray.

For the last part, we covered three cases, when total internal reflection occurs, normal reflection and refraction. We check for total internal reflection, then if it doesn't happen we essentially filp a coin with a special probability to determine if we should reflect or refract.
Notice how with 0 bounces the balls and ceiling are black. This is because we only have direct lighting

This one is super interesting. The ceiling and balls are now colored, but the reflection of them is not. This is because it takes an extra bounce for the reflected materials to be colored. It's as if in the reflected world, there is only direct lighting.

For 2 bounces, we finally have the full picture. Or so I thought! The back ball is actually reflecting what the front ball looked like in the previous render

It takes 3 bounces to see the caustic in the bottom of the glass ball. This is expected as the light hits the glass ball, refracts inside the ball, and refracts back out to show light.

Now we are starting to converge, there doesn't seem to be any weird additional effects

One interesting thing to note about 4 vs 5 is that 5 seems to be noisier. That makes sense as more bounces introduces more reflections/refractions that could cause noise.

Thanks to Russian Roullete, 100 is not very different from 5.

Part 2: Microfacet
Implementation notes:
We implemented three main functions here:
one to get the Normal Distribution Function, one to get the Fresnel term, and one to sample the BRDF.

The normal distribution function defines the normals of the microfacets. We use the Beckmann distribution to figure out our normal distribution. We also use alpha, where a higher alpha means our surface is more diffuse, and lower alpha means our surface is glossier.

We simplified this part by only calculating the fresnel term at the the red, green and blue wavelengths.

Finally we replaced cosine sampling with imprtance sampling. Pdfs resembling the normal distribution function are given higher priority, because that reduces noise and makes convergence faster.
alpha=.005 It's wayy to loud in this picture, hopefully a higher alpha will reduce the noise,

alpha=.05 Noise is a lot lower in this picture, but there are still a lot of roughness in the picture.

alpha=.25 Much less noise in this picture. It is much easier to see the actual material of the dragon here.

alpha=.5 Here, the matteness is very clear and the shape of the dragon is fully discernible. We also have mucccch less noise.

Bunny with uniform cosine hemisphere samplining: hella noise and practically no convergence.

Bunny with importance samplining: remarkably nicer

Sodium with some low specs

Sodium with some better specs

Part 3: environment light
Implementation notes:
This part progressed in 3 phases

First, we implemented uniform sampling, by just randomly sampling from the sphere and checking the environment map for the corresponding value.

Then we calculated probability tables for the marginal and conditional distributions. This was particularly error prone process with a lot of steps that I had difficulty keeping straight.

Finally we sampled using those probability tables so we could focus on the probabilitically important features of the scene.
Field scene jpg

Field scene probability distribution

Basic Uniform sampling is noisy, with very little convergence. You can't really see any of the details of the bunny with any level of clarity.

Basic Importance sampling looks a lot better! Way less noise, and the bunny's features can actually be seen. Especially around the tail.

CU Microfacet Uniform doesn't seem to terrible. Like it's really noisy, but you can kinda make stuff out.

CU Microfacet Importance Then, you look at this one and you can tell where light is reflecting and you can tell what kind of material it is.

Part 4: Depth of Field
For depth of field, we used the diagram in the spec to visualize the way a thin lens operates. So far all of our stuff has been rendered assuming a pinhole camera, but our eyes work very different from that. This helps us model our eyes more cloesly. Pinhole cameras also show everything in the view of camera as perfectly in focus. The ideal thinlens has no actual thickness. It allows us to show focus and blur in a picture.
Focus stack 4.9, 5.3, 6.0, 6.7
Aperture stack 0.25, 0.3533, 0.5, 0.707