WARNING
Content Under Development
See release page for latest official PDF version.
**Chapter 11: Defocus Blur**
Now our final feature: defocus blur. Note, all photographers will call it “depth of field” so be
aware of only using “defocus blur” among friends.
The reason we defocus blur in real cameras is because they need a big hole (rather than just a
pinhole) to gather light. This would defocus everything, but if we stick a lens in the hole, there
will be a certain distance where everything is in focus. The distance to that plane where things are
in focus is controlled by the distance between the lens and the film/sensor. That is why you see the
lens move relative to the camera when you change what is in focus (that may happen in your phone
camera too, but the sensor moves). The “aperture” is a hole to control how big the lens is
effectively. For a real camera, if you need more light you make the aperture bigger, and will get
more defocus blur. For our virtual camera, we can have a perfect sensor and never need more light,
so we only have an aperture when we want defocus blur.
A real camera has a compound lens that is complicated. For our code we could simulate the order:
sensor, then lens, then aperture, and figure out where to send the rays and flip the image once
computed (the image is projected upside down on the film). Graphics people usually use a thin lens
approximation.

We also don’t need to simulate any of the inside of the camera. For the purposes of rendering an
image outside the camera, that would be unnecessary complexity. Instead I usually start rays from
the surface of the lens, and send them toward a virtual film plane, by finding the projection of the
film on the plane that is in focus (at the distance `focus_dist`).

For that we just need to have the ray origins be on a disk around `lookfrom` rather than from a
point:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
#ifndef CAMERAH
#define CAMERAH
#include "random.h"
#include "ray.h"
vec3 random_in_unit_disk() {
vec3 p;
do {
p = 2.0*vec3(random_double(),random_double(),0) - vec3(1,1,0);
} while (dot(p,p) >= 1.0);
return p;
}
class camera {
public:
camera(vec3 lookfrom, vec3 lookat, vec3 vup, float vfov, float aspect,
float aperture, float focus_dist)
{
lens_radius = aperture / 2;
float theta = vfov*M_PI/180;
float half_height = tan(theta/2);
float half_width = aspect * half_height;
origin = lookfrom;
w = unit_vector(lookfrom - lookat);
u = unit_vector(cross(vup, w));
v = cross(w, u);
lower_left_corner = origin
- half_width * focus_dist * u
- half_height * focus_dist * v
- focus_dist * w;
horizontal = 2*half_width*focus_dist*u;
vertical = 2*half_height*focus_dist*v;
}
ray get_ray(float s, float t) {
vec3 rd = lens_radius*random_in_unit_disk();
vec3 offset = u * rd.x() + v * rd.y();
return ray(origin + offset,
lower_left_corner + s*horizontal + t*vertical
- origin - offset);
}
vec3 origin;
vec3 lower_left_corner;
vec3 horizontal;
vec3 vertical;
vec3 u, v, w;
float lens_radius;
};
#endif
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Using a big aperture:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
vec3 lookfrom(3,3,2);
vec3 lookat(0,0,-1);
float dist_to_focus = (lookfrom-lookat).length();
float aperture = 2.0;
camera cam(lookfrom, lookat, vec3(0,1,0), 20,
float(nx)/float(ny), aperture, dist_to_focus);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
We get:
