This image has a resolution of 1920x1080, and it was rendered with 256 samples/pixel, 4 samples/light, and max ray depth equal to 12. It takes about 25 minutes on a desktop using 8 threads
Introduction
In this part, you're going to simulate a thin lens to enable the depth of field effect.
Previously, we were using ideal pinhole cameras. With the pinhole model, everything is in focus. However, real cameras, including human eyes, are actually modeled as lenses with finite apertures defined by lensRadius
. With the lens model, objects are in focus only if they're within a plane that is at focalDistance
from the lens.
We're going to simulate lens in this part. To simplify your task, let's implement a thin lens, i.e. we ignore its actual thickness, but still assume that it refracts like a lens.
Once you've implemented this part, you can use the hotkeys k
and l
in the GUI to change the lens radius and ;
and '
to change the focal distance. You can also set these parameters in the command line using b
and d
respectively. Setting lens radius to zero will reduce to the pinhole camera model you were using previously. Use the hotkey d
and the command line flag c
to dump and load camera settings for doing command line rendering from different points of view and with different lens settings.
Basic principles
Let's first define the scene in camera space. We assume that the lens is located at position $z=0$, the plane of focus is at $z=\mathrm{focalDistance}$, and the image plane is at $z=1$.
In project 31 where there's no lens, we shoot a ray from $(0, 0, 0)$ towards some direction $(X, Y, 1)$ (red segment in the figure). This indicates that the point pFilm
receiving radiance on the image plane should be $(X, Y, 1)$.
Now, with the thin lens, pFilm
no longer receives radiance just from $(0, 0, 0)$. Instead, it is able to receive radiance from any point on the thin lens (yellow segment in the figure). So, we can uniformly sample the lens, which is a disk of radius lensRadius
, to get the sampled point pLens
on the lens at $(s_x,s_y,0)$.
The next step is to calculate the direction of a ray from pLens
towards the scene (blue segment in the figure). But how does the yellow segment refract through the thin lens? That can be complicated. However, we can take advantage of these two properties:

Rays from the same point on the plane of focus will always be focused to the same point on the image plane, no matter where they pass through the lens.

The ray passing though the lens' center won't change direction.
We can thus calculate just the intersection pFocus
of the red segment and the plane of focus. We then immediately know that the blue segment must go through pFocus
as well.
The blue ray is what we will generate in this task. Compared to the red ray for pinhole cameras, its origin is no longer $(0, 0, 0)$ but randomly sampled. Its direction also varies but is decided once its origin is sampled.
Task 0: Refactoring 31 Code
In order to enable depth of field effects, you'll need to change the raygenerating function called by PathTracer::raytrace_pixel()
.
If you're using your code from 31, replace the generate_ray()
function in PathTracer::raytrace_pixel()
with your generate_ray_for_thin_lens()
function. You can use gridSampler>get_sample()
to generate two random values in the range $[0, 1)$, and multiply one of them by $2\pi$ to get your inputs for rndR
and rndTheta
(the other inputs are the same as before).
Vector2D samplesForLens = gridSampler>get_sample();
r = camera>generate_ray_for_thin_lens(/*same*/, /*same*/, samplesForLens.x, samplesForLens.y * 2.0 * PI);
If you're using the staff library, you can enable the thin lens by setting the aperture to anything other than 0. This will tell raytrace_tile()
to call your precompiled generate_ray_for_thin_lens()
function with the proper inputs.
Task 1: Generating Rays for a Thin Lens
In this task, you're required to implement the Camera::generate_ray_for_thin_lens()
function. This function takes the same parameters x
and y
as in Camera::generate_ray()
, but also receives two additional parameters rndR
and rndTheta
that are random numbers uniformly distributed in $[0, 1)$ and $[0, 2\pi)$ respectively. They are used to uniformly sample the thin lens.
Specifically, your can follow these steps:
 Look up your code from Project 31, Part 1 to figure out the generated ray direction (red segment in the figure).
 Uniformly sample the disk representing the thin lens at $\mathrm{pLens}=(\mathrm{lensRadius}\ \sqrt{rndR} \cdot \cos(\mathrm{rndTheta}), \mathrm{lensRadius}\ \sqrt{rndR} \cdot \sin(\mathrm{rndTheta}), 0)$.
 Calculate
pFocus
by intersecting the plane of focus with the red segment.  Calculate the ray that originates from
pLens
, and set its direction towardspFocus
(blue segment in the figure).  Normalize the direction of the ray, perform the cameratoworld conversion for both its origin and direction, add
pos
to the ray's origin, and set the near and far clips to be the same as in Project 31, Part 1.
This Part's Writeup
For these subparts, we recommend using a microfacet BSDF scene to show off the cool out of focus effects you can get with depth of field!
 In a few sentences, explain the differences between a pinhole camera model and a thinlens camera model.
 Show a "focus stack" where you focus at 4 visibly different depths through a scene.
 Show a sequence of 4 pictures with visibly different aperture sizes, all focused at the same point in a scene.