Depth of field in PerspectiveCamera

105 views
Skip to first unread message

Nigel Fisher

unread,
Nov 10, 2009, 10:39:38 AM11/10/09
to pbrt
First up, if Matt or Greg are reading: thanks for the great book!

I am trying to work out how the depth of field in the
PerspectiveCamera class works, but neither the 1.04 code or the code
in the book seems to correspond to the description in the text or
figures.

The following lines of code from the book (<Update ray for effect of
lens> p272) seem to be moving the origin of the ray around the hither
plane not the lens:
ray->o.x = lensU;
ray->o.y = lensV;

Without the "+=" I have no idea what the equivalent lines of code in
the 1.04 are doing:
ray->o.x = lensU * (FocalDistance - ClipHither) / FocalDistance;
ray->o.y = lensV * (FocalDistance - ClipHither) / FocalDistance;

The ray origin is still on the hither plane, but always originating
from a small circle in the middle of it rather than passing through
the appropriate sample.imageX and sample.imageY on the raster. Am I
understanding (or not understanding, as the case may be) each of these
pieces of code correctly?

Maybe all this confusion is because the origin of the ray is on the
hither plan rather than the lens. Does it make any difference to the
rendered result if we make the origin of the ray on the lens (z=0 in
camera space) and change ray->mint so that intersections only start
after the hither plane? If so does the following code make sense? It's
kind of a cross between the two versions above. Is the book code in
error? Is the 1.04 code in error? Or am I missing something?

float PerspectiveCamera::GenerateRay(const Sample &sample,
Ray *ray) const {
// Generate raster and camera samples
Point Pras(sample.imageX, sample.imageY, 0);
Point Pcamera;
RasterToCamera(Pras, &Pcamera);

// Change:
// ray->o = Pcamera;
ray->o = Point(0.0, 0.0, 0.0);
ray->d = Vector(Pcamera.x, Pcamera.y, Pcamera.z);

// Set ray time value
ray->time = Lerp(sample.time, ShutterOpen, ShutterClose);
// Modify ray for depth of field
if (LensRadius > 0.) {
// Sample point on lens
float lensU, lensV;
ConcentricSampleDisk(sample.lensU, sample.lensV,
&lensU, &lensV);
lensU *= LensRadius;
lensV *= LensRadius;
// Compute point on plane of focus

// Change:
//float ft = (FocalDistance - ClipHither) / ray->d.z;
float ft = FocalDistance / ray->d.z;

Point Pfocus = (*ray)(ft);
// Update ray for effect of lens

// Change (x2):
//ray->o.x = lensU * (FocalDistance - ClipHither) / FocalDistance;
//ray->o.y = lensV * (FocalDistance - ClipHither) / FocalDistance;
ray->o.x = lensU;
ray->o.y = lensV;

ray->d = Pfocus - ray->o;
}
ray->d = Normalize(ray->d);

// Change (x2):
//ray->mint = 0.;
//ray->maxt = (ClipYon - ClipHither) / ray->d.z;
ray->mint = ClipHither / ray->d.z;
ray->maxt = ClipYon / ray->d.z;

CameraToWorld(*ray, ray);
return 1.f;
}
















First question: why do generated rays start at the hither plane rather
than the lens? Is there any problem with starting the ray at z=0
(camera coordinates) and setting a ray->mint so the ray starts at the
hither plane?



This would seem to make the logic easier as rays have to travel
through the lens at <0,0,0> in

float PerspectiveCamera::GenerateRay(const Sample &sample,
Ray *ray) const {
// Generate raster and camera samples
Point Pras(sample.imageX, sample.imageY, 0);
Point Pcamera;
RasterToCamera(Pras, &Pcamera);
ray->o = Point
(0,0,0); //Changed
ray->d = Vector(Pcamera.x, Pcamera.y, Pcamera.z);
// Set ray time value
ray->time = Lerp(sample.time, ShutterOpen, ShutterClose);
// Modify ray for depth of field
if (LensRadius > 0.) {
// Sample point on lens
float lensU, lensV;
ConcentricSampleDisk(sample.lensU, sample.lensV,
&lensU, &lensV);
lensU *= LensRadius;
lensV *= LensRadius;
// Compute point on plane of focus
float ft = FocalDistance / ray->d.z; //
Changed. Equivalent to FocalDistance/ClipHither
Point Pfocus = (*ray)(ft);
// Update ray for effect of lens
ray->o.x = lensU; // changed
ray->o.y = lensV; // changed
ray->d = Pfocus - ray->o;
}
ray->d = Normalize(ray->d);
ray->mint = ClipHither / ray->d.z; // Changed
ray->maxt = ClipYon / ray->d.z; // Changed
CameraToWorld(*ray, ray);
return 1.f;
}

Nigel Fisher

unread,
Nov 10, 2009, 10:48:11 AM11/10/09
to pbrt
Oops, please ignore the last bit from:

> First question: why do generated rays start at the hither plane rather
> than the lens? Is there any problem with starting the ray at z=0
> (camera coordinates) and setting a ray->mint so the ray starts at the
> hither plane?
>

...

And the second version of the code.

This was an earlier edit I forgot to delete off the end!

Nigel Fisher

unread,
Nov 10, 2009, 8:10:52 PM11/10/09
to pbrt
Actually, now I look at it, the 1.04 code achieves the same thing,
only with the ray origin on the hither plane as my changed code, IF
the '=' are changed to '+='. I think. It still is confusing as the
book talks abut points on the lens not the hither plane.

ray->o.x += lensU * (FocalDistance - ClipHither) / FocalDistance;
ray->o.y += lensV * (FocalDistance - ClipHither) / FocalDistance;

Any feedback? Am I on the wrong track?

On Nov 11, 1:48 am, Nigel Fisher <nigel.fis...@googlemail.com> wrote:
> Oops, please ignore the last bit from:
>
> > First question: why do generated rays start at the hither plane rather
> > than the lens? Is there any problem with starting the ray at z=0
> > (camera coordinates) and setting a ray->mint so the ray starts at the
> > hither plane?
>
> ...
>
> And the second version of the code.
>
> This was an earlier edit I forgot to delete off the end!
>
> On Wed, Nov 11, 2009 at 1:39 AM, Nigel Fisher
>

Raghuram.O.S.

unread,
Nov 10, 2009, 10:09:05 PM11/10/09
to pb...@googlegroups.com
Just reiterating. Even I had the same question in mind. Maybe I am missing something conceptual. Would be happy to know what the mistake is.
--
Regards,
Raghuram.O.S

mljack

unread,
Nov 11, 2009, 12:01:11 PM11/11/09
to pbrt
Check the comments by humper and matt in the page below:
http://www.pbrt.org/bugtracker/view.php?id=7

You need register an account to access the page.

Nigel Fisher

unread,
Nov 11, 2009, 1:27:02 PM11/11/09
to pbrt
Thanks mljack - lots of good discussion there.

Still confused though. In the bug tracker, Martin Kraus came to the
same conclusions as I did. But his proposal was not followed. The
final comment by 'mmp' suggests that Matrin made a typo by using '+='
instead of '=', but I can't see how. My natural thought was that it
would have to be a '+=' but this was because it seemed to me that this
code was being used to MOVE the ray origin on the HITHER plane.

The original ray origin was at the intersection of a line between the
camera's origin and a point on the focal plane, and the hither plane.
Having generated a new point on the lens that is not at the camera's
origin a new line is formed between this point on the lens and the
same point on the focal plane. The point at which this line intersects
the hither plane should be the new origin of the ray. This is found by
translating (adding to) the ray's origin by a portion of the lens
point's offset depending on how far the hither plane is from the
camera.

And then I read the book text and I wasn't so sure. But now I think I
am a bit more sure again, and Martin explains it so much better than I
ever could. I can understand that his '+=' LOOKS wrong because the
original intent was to RELOCATE the ray origin on the LENS, but the
ray origin is not on the lens. But 'mmp' is Matt Pharr, right? So at
the same time I still think I might be missing something.

Cheers,
Nigel.

Matt Pharr

unread,
Nov 11, 2009, 8:01:48 PM11/11/09
to pb...@googlegroups.com
FWIW I think the confusion with all this is compounded by the hither/
yon plane stuff and pbrt v1's insistence of starting rays on the
hither plane. The pbrt-v2 code (http://github.com/mmp/pbrt-v2) gets
rid of the hither/yon stuff, which makes things a bit simpler. In
particular, it does the same as your code below, just without the
extra steps to set the ray's mint and maxt so that the ray goes only
from the hither to yon planes.

The intent of the pbrt-1.04 code was to compute the point on the
hither plane by computing the distance along the ray from the lens to
the focal plane and then moving back along the ray from the point on
the lens to the hither plane to set the ray's origin right on the
hither plane. (So the 1.04 code has mint as zero versus yours being
non-zero.) Does the 1.04 code versus your code below compute a
noticeably different result? (i.e. are the two implementations just
computing two different points along the same ray?)

(To state the obvious, I will caveat that I'm apparently cursed with
respect to getting these few lines of code right , so don't believe
anything I say. :-) )

Thanks,
-matt

Nigel Fisher

unread,
Nov 12, 2009, 3:07:02 AM11/12/09
to pbrt
Thanks Matt!

I checked out the v2 code and feel much relieved.

Still, I think the current 1.04 code needs the '+=' for the two
different points to be along the same ray. But as my own caveat,
that's possibly just because I'm stubborn and there is still something
I am not understanding. I'll preset my case again anyway.

"The intent of the pbrt-1.04 code was to compute the point on the
hither plane by computing the distance along the ray from the lens to
the focal plane and then moving back along the ray from the point on
the lens to the hither plane to set the ray's origin right on the
hither plane."

The way you describe it would lead me (correctly or not) to the
following code (I'll just do the ray->o.x):

ray->o.x =
lensU * (FocalDistance - ClipHither) / FocalDistance
+ Pfocus.x * (1.0 - (FocalDistance - ClipHither) /
FocalDistance);

Which equals:

ray->o.x += lensU * (FocalDistance - ClipHither) / FocalDistance;

No more caveats from me. I'm generally so far out of my depth with
this stuff that I can't afford to apologise for it all the time :)

Thanks again,
Nigel.

Nigel Fisher

unread,
Nov 15, 2009, 1:04:37 AM11/15/09
to pbrt
I ran a few tests to support my case.

http://img2.pict.com/35/de/33/1984778/0/dof.jpg (Sorry if the link
doesn't work)

It's a simple scene using a perspective camera. I set 'hither' to 10
and 'yon' to 20.

The left-hand image uses the original 1.04 code without setting a
value for 'lensradius'.
The middle image uses the original 1.04 code with a 'lensradius' of
0.0001.
The right-hand image uses the 1.04 code with a 'lensradius' of 0.0001
and each '=' changed to '+='.

The script file I used was:

Camera
"perspective"
# "perspective2"
"float fov" [45]
# "float focaldistance" [14]
# "float lensradius" [0.0001]
"float hither" [10] "float yon" [20]

Film "image" "integer xresolution" [300] "integer yresolution" [300]
# "string filename" ["perspective.exr"]
# "string filename" ["perspective2.exr"]
"string filename" ["pinhole.exr"]

Sampler "bestcandidate" "integer pixelsamples" [16]
PixelFilter "box" "float xwidth" [.5] "float ywidth" [.5]

WorldBegin

LightSource "point" "color I" [20 20 20] "point from" [0 0 12]

Material "matte" "color Kd" [1 .2 .2 ]

Translate 0 0 12

AttributeBegin
Translate -4 -4 0
Shape "sphere"
Translate 0 0 2
Shape "sphere"
Translate 0 0 2
Shape "sphere"
Translate 0 0 2
Shape "sphere"
AttributeEnd

AttributeBegin
Translate -4 0 0
Shape "sphere"
Translate 0 0 2
Shape "sphere"
Translate 0 0 2
Shape "sphere"
Translate 0 0 2
Shape "sphere"
AttributeEnd

AttributeBegin
Translate -4 4 0
Shape "sphere"
Translate 0 0 2
Shape "sphere"
Translate 0 0 2
Shape "sphere"
Translate 0 0 2
Shape "sphere"
AttributeEnd

AttributeBegin
Translate 0 -4 0
Shape "sphere"
Translate 0 0 2
Shape "sphere"
Translate 0 0 2
Shape "sphere"
Translate 0 0 2
Shape "sphere"
AttributeEnd

AttributeBegin
Translate 0 4 0
Shape "sphere"
Translate 0 0 2
Shape "sphere"
Translate 0 0 2
Shape "sphere"
Translate 0 0 2
Shape "sphere"
AttributeEnd

AttributeBegin
Translate 4 -4 0
Shape "sphere"
Translate 0 0 2
Shape "sphere"
Translate 0 0 2
Shape "sphere"
Translate 0 0 2
Shape "sphere"
AttributeEnd

AttributeBegin
Translate 4 0 0
Shape "sphere"
Translate 0 0 2
Shape "sphere"
Translate 0 0 2
Shape "sphere"
Translate 0 0 2
Shape "sphere"
AttributeEnd

AttributeBegin
Translate 4 4 0
Shape "sphere"
Translate 0 0 2
Shape "sphere"
Translate 0 0 2
Shape "sphere"
Translate 0 0 2
Shape "sphere"
AttributeEnd

WorldEnd





Matt Pharr

unread,
Nov 17, 2009, 11:37:21 PM11/17/09
to pb...@googlegroups.com
That is an excellent test case/demonstration. I concur that you're right and that I've gotten this wrong once more..

I'll fix this in the pbrt-v1 code. (I will happily note that the pbrt-v2 code does get this right with your test case here.)

Thanks,
-matt
> --~--~---------~--~----~------------~-------~--~----~
> You received this message because you are subscribed to the Google Groups "pbrt" group.
> To post to this group, send email to pb...@googlegroups.com
> To unsubscribe from this group, send email to pbrt+uns...@googlegroups.com
> For more options, visit this group at http://groups.google.com/group/pbrt?hl=en
> -~----------~----~----~----~------~----~------~--~---
>

Nigel Fisher

unread,
Nov 18, 2009, 7:21:39 AM11/18/09
to pbrt
Thanks Matt, happy to be able to help :)

On Nov 18, 2:37 pm, Matt Pharr <matt.ph...@gmail.com> wrote:
> That is an excellent test case/demonstration.  I concur that you're right and that I've gotten this wrong once more..
>
> I'll fix this in the pbrt-v1 code.  (I will happily note that the pbrt-v2 code does get this right with your test case here.)
>
> Thanks,
> -matt
>
> On Nov 14, 2009, at 10:04 PM, Nigel Fisher wrote:
>
>
>
> > I ran a few tests to support my case.
>
> >http://img2.pict.com/35/de/33/1984778/0/dof.jpg(Sorry if the link

Matt Pharr

unread,
Nov 18, 2009, 1:28:19 PM11/18/09
to pb...@googlegroups.com
I've updated the pbrt-v1 code with this fix (only available for now from http://github.com/mmp/pbrt-v1, but click the 'download' button and it'll give you a zip or tar file.) Will do a proper 1.05 release one of these days.. (After fixing that sphere bug and other small ones..)

Thanks again!

-matt
> --
>
> You received this message because you are subscribed to the Google Groups "pbrt" group.
> To post to this group, send email to pb...@googlegroups.com.
> To unsubscribe from this group, send email to pbrt+uns...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/pbrt?hl=.
>
>

Reply all
Reply to author
Forward
0 new messages