I'm following Peter Shirley book, Ray tracing in one weekend. In the last chapter, he talks about how to make defocus blur by using thin len approximation. His camera class accepts a parameter called focus_dist, which he then uses to compute the distance to the image plane.

lower_left_corner = origin  - half_width*focus_dist*u -half_height*focus_dist*v - focus_dist*w; // lower_left_corner of image plane

This subject was new to me, so I went searching around online. From what i understand, the distance to the image plane is governed by the equation 1/f = 1/d + 1/d prime with f being focal length and d being the focus distance. d prime will be the answer. The lack of this equation in Peter Shirley's code and the fact that he seems to be putting the image plane at the focus distance makes me completely lost. Can anyone explain it to me?

Here's the link to the code I talked about. https://github.com/petershirley/raytracinginoneweekend/blob/master/camera.h


2 Answers 2


You don't need that equation.

You know your focus point (point on y-axis with focus_distance as z component) and the ray origin. All that needs to be done then is to compute the ray direction from the origin through that focus point.

For what it's worth, I think a camera space formulation of a thin lens makes it easier to understand the concept:

float fov = 2.0f * atan((sensorWidth / (2.0f * focal_length))); // in radians
float tan_fov = tanf(fov / 2.0f);

// create point on lens, screen space samples are in [-1, 1] domain
vec3 p(screen_space_sample_x * tan_fov, screen_space_sample_y * tan_fov, 1.0);

// calculate direction vector from origin to point on lens
dir = normalize(p - origin);

// get uniformly distributed points on the unit disk
vec2 lens(0.0, 0.0);
concentricDiskSample(random1, random2, &lens);

// scale points in [-1, 1] domain to actual aperture radius
lens *= apertureRadius;

// new origin is these points on the lens
origin.x = lens.x;
origin.y = lens.y;
origin.z = 0.0;

// Compute point on plane of focus, intersection on z axis
float intersection = std::abs(focus_distance / dir.z);
vec3 focusPoint = dir * intersection;
dir = normalize(focusPoint - origin);

// now looking down -Z (optional depending on your coordinate system)
dir.z *= -1.0;

Exactly the same happened to me as it did to you: I was reading the book, and quite following everything that I was reading, until that bit with the defocus blur. My issue was very specifically with the following bits:

lower_left_corner: look_from - half_width*focus_dist*u - half_height*focus_dist*v - focus_dist*w,
horizontal: 2.0*half_width*focus_dist*u,
vertical: 2.0*half_height*focus_dist*v

It was very clear to me that we should multiply -w by focus_dist, -w being the vector pointing towards the subject. But I couldn't figure out why we should multiply the u and v vectors by focus_dist too. That made no sense to me. Was it about the dimensions of the focus plane or something? I couldn't make sense of it. But removing it made the generated image wrong, so it was really needed.

The insight I had was 'where do we get half_width from?' And the answer is...

let half_height = tan(theta/2.0);
let half_width = aspect * half_height;

And NOW everything makes sense! That is obviously wrong, as the code computing half_height assumes a distance of 1.0 to the target! Therefore half_height was miscalculated in the book, and the author compensated a little later by multiplying by focus_dist when using half_width...

Therefore the correct code (despite the fact that the original code worked 100%) would be:

let half_height = tan(theta/2.0) * focus_dist;
let half_width = aspect * half_height;
// ...
lower_left_corner: look_from - half_width*u - half_height*v - focus_dist*w,
horizontal: 2.0*half_width*u,
vertical: 2.0*half_height*v,

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.