Skip to content

Commit ae8d3ed

Browse files
authored
Merge pull request #536 from RayTracing/fix-camera
Update camera code
2 parents 87eb416 + f54ee9b commit ae8d3ed

File tree

4 files changed

+144
-101
lines changed

4 files changed

+144
-101
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ Change Log -- Ray Tracing in One Weekend
33

44
# v3.1.1 (in progress)
55

6+
### Common
7+
- Change: Camera code improvements to make it more robust when any particular value changes. Also,
8+
the code develops in a smoother series of iterations as the book progresses. (#536)
9+
610
### _In One Weekend_
711
- Fix: Camera initialization with explicit up vector (#537)
812
- Change: The C++ `<random>` version of `random_double()` no longer depends on `<functional>`

books/RayTracingInOneWeekend.html

Lines changed: 102 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -466,12 +466,22 @@
466466
that returns the color of the background (a simple gradient).
467467

468468
I’ve often gotten into trouble using square images for debugging because I transpose $x$ and $y$ too
469-
often, so I’ll stick with a 200×100 image. I’ll put the “eye” (or camera center if you think of a
470-
camera) at $(0,0,0)$. I will have the y-axis go up, and the x-axis to the right. In order to respect
471-
the convention of a right handed coordinate system, into the screen is the negative z-axis. I will
472-
traverse the screen from the lower left hand corner, and use two offset vectors along the screen
473-
sides to move the ray endpoint across the screen. Note that I do not make the ray direction a unit
474-
length vector because I think not doing that makes for simpler and slightly faster code.
469+
often, so I’ll use a non-square image. For now we'll use a 16:9 aspect ratio, since that's so
470+
common.
471+
472+
In addition to setting up the pixel dimensions for the rendered image, we also need to set up a
473+
virtual viewport through which to pass our scene rays. For the standard square pixel spacing, the
474+
viewport's aspect ratio should be the same as our rendered image. We'll just pick a viewport two
475+
units in height. We'll also set the distance between the projection plane and the projection point
476+
to be one unit. This is referred to as the “focal length”, not to be confused with “focus distance”,
477+
which we'll present later.
478+
479+
I’ll put the “eye” (or camera center if you think of a camera) at $(0,0,0)$. I will have the y-axis
480+
go up, and the x-axis to the right. In order to respect the convention of a right handed coordinate
481+
system, into the screen is the negative z-axis. I will traverse the screen from the lower left hand
482+
corner, and use two offset vectors along the screen sides to move the ray endpoint across the
483+
screen. Note that I do not make the ray direction a unit length vector because I think not doing
484+
that makes for simpler and slightly faster code.
475485

476486
![Figure [cam-geom]: Camera geometry](../images/fig.cam-geom.jpg)
477487

@@ -497,19 +507,25 @@
497507

498508
std::cout << "P3\n" << image_width << " " << image_height << "\n255\n";
499509

510+
500511
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
501-
point3 origin(0.0, 0.0, 0.0);
502-
vec3 horizontal(4.0, 0.0, 0.0);
503-
vec3 vertical(0.0, 2.25, 0.0);
504-
point3 lower_left_corner = origin - horizontal/2 - vertical/2 - vec3(0,0,1);
512+
auto viewport_height = 2.0;
513+
auto viewport_width = aspect_ratio * viewport_height;
514+
auto focal_length = 1.0;
515+
516+
auto origin = point3(0, 0, 0);
517+
auto horizontal = vec3(viewport_width, 0, 0);
518+
auto vertical = vec3(0, viewport_height, 0);
519+
auto lower_left_corner = origin - horizontal/2 - vertical/2 - vec3(0, 0, focal_length);
505520
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
521+
506522
for (int j = image_height-1; j >= 0; --j) {
507523
std::cerr << "\rScanlines remaining: " << j << ' ' << std::flush;
508524
for (int i = 0; i < image_width; ++i) {
509525
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
510526
auto u = double(i) / (image_width-1);
511527
auto v = double(j) / (image_height-1);
512-
ray r(origin, lower_left_corner + u*horizontal + v*vertical);
528+
ray r(origin, lower_left_corner + u*horizontal + v*vertical - origin);
513529
color pixel_color = ray_color(r);
514530
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
515531
write_color(std::cout, pixel_color);
@@ -1201,10 +1217,14 @@
12011217

12021218
std::cout << "P3\n" << image_width << ' ' << image_height << "\n255\n";
12031219

1204-
point3 lower_left_corner(-2.0, -1.0, -1.0);
1205-
vec3 horizontal(4.0, 0.0, 0.0);
1206-
vec3 vertical(0.0, 2.0, 0.0);
1207-
point3 origin(0.0, 0.0, 0.0);
1220+
auto viewport_height = 2.0;
1221+
auto viewport_width = aspect_ratio * viewport_height;
1222+
auto focal_length = 1.0;
1223+
1224+
auto origin = point3(0, 0, 0);
1225+
auto horizontal = vec3(viewport_width, 0, 0);
1226+
auto vertical = vec3(0, viewport_height, 0);
1227+
auto lower_left_corner = origin - horizontal/2 - vertical/2 - vec3(0, 0, focal_length);
12081228

12091229

12101230
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
@@ -1313,7 +1333,8 @@
13131333
</div>
13141334

13151335
<div class='together'>
1316-
Putting that all together yields a camera class encapsulating our simple axis-aligned camera from
1336+
Now's a good time to create a `camera` class to manage our virtual camera and the related tasks of
1337+
scene scampling. The following class implements a simple camera using the axis-aligned camera from
13171338
before:
13181339

13191340
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
@@ -1325,10 +1346,15 @@
13251346
class camera {
13261347
public:
13271348
camera() {
1328-
lower_left_corner = point3(-2.0, -1.0, -1.0);
1329-
horizontal = vec3(4.0, 0.0, 0.0);
1330-
vertical = vec3(0.0, 2.0, 0.0);
1331-
origin = point3(0.0, 0.0, 0.0);
1349+
auto aspect_ratio = 16.0 / 9.0;
1350+
auto viewport_height = 2.0;
1351+
auto viewport_width = aspect_ratio * viewport_height;
1352+
auto focal_length = 1.0;
1353+
1354+
origin = point3(0, 0, 0);
1355+
horizontal = vec3(viewport_width, 0.0, 0.0);
1356+
vertical = vec3(0.0, viewport_height, 0.0);
1357+
lower_left_corner = origin - horizontal/2 - vertical/2 - vec3(0, 0, focal_length);
13321358
}
13331359

13341360
ray get_ray(double u, double v) const {
@@ -1399,9 +1425,11 @@
13991425
world.add(make_shared<sphere>(point3(0,0,-1), 0.5));
14001426
world.add(make_shared<sphere>(point3(0,-100.5,-1), 100));
14011427

1428+
14021429
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
14031430
camera cam;
14041431
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
1432+
14051433
for (int j = image_height-1; j >= 0; --j) {
14061434
std::cerr << "\rScanlines remaining: " << j << ' ' << std::flush;
14071435
for (int i = 0; i < image_width; ++i) {
@@ -1882,7 +1910,6 @@
18821910
double t;
18831911
bool front_face;
18841912

1885-
18861913
inline void set_face_normal(const ray& r, const vec3& outward_normal) {
18871914
front_face = dot(r.direction(), outward_normal) < 0;
18881915
normal = front_face ? outward_normal :-outward_normal;
@@ -2107,6 +2134,7 @@
21072134
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
21082135

21092136
camera cam;
2137+
21102138
for (int j = image_height-1; j >= 0; --j) {
21112139
std::cerr << "\rScanlines remaining: " << j << ' ' << std::flush;
21122140
for (int i = 0; i < image_width; ++i) {
@@ -2551,16 +2579,17 @@
25512579
double vfov, // vertical field-of-view in degrees
25522580
double aspect_ratio
25532581
) {
2554-
origin = point3(0.0, 0.0, 0.0);
2555-
25562582
auto theta = degrees_to_radians(vfov);
2557-
auto half_height = tan(theta/2);
2558-
auto half_width = aspect_ratio * half_height;
2583+
auto h = tan(theta/2);
2584+
auto viewport_height = 2.0 * h;
2585+
auto viewport_width = aspect_ratio * viewport_height;
25592586

2560-
lower_left_corner = point3(-half_width, -half_height, -1.0);
2587+
auto focal_length = 1.0;
25612588

2562-
horizontal = vec3(2*half_width, 0.0, 0.0);
2563-
vertical = vec3(0.0, 2*half_height, 0.0);
2589+
origin = point3(0, 0, 0);
2590+
horizontal = vec3(viewport_width, 0.0, 0.0);
2591+
vertical = vec3(0.0, viewport_height, 0.0);
2592+
lower_left_corner = origin - horizontal/2 - vertical/2 - vec3(0, 0, focal_length);
25642593
}
25652594
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
25662595

@@ -2626,29 +2655,28 @@
26262655
public:
26272656
camera(
26282657
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
2629-
point3 lookfrom, point3 lookat, vec3 vup,
2658+
point3 lookfrom,
2659+
point3 lookat,
2660+
vec3 vup,
26302661
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
26312662
double vfov, // vertical field-of-view in degrees
26322663
double aspect_ratio
26332664
) {
2634-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
2635-
origin = lookfrom;
2636-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
2637-
vec3 u, v, w;
2638-
26392665
auto theta = degrees_to_radians(vfov);
2640-
auto half_height = tan(theta/2);
2641-
auto half_width = aspect_ratio * half_height;
2666+
auto h = tan(theta/2);
2667+
auto viewport_height = 2.0 * h;
2668+
auto viewport_width = aspect_ratio * viewport_height;
26422669

2643-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
2644-
w = unit_vector(lookfrom - lookat);
2645-
u = unit_vector(cross(vup, w));
2646-
v = cross(w, u);
26472670

2648-
lower_left_corner = origin - half_width*u - half_height*v - w;
2671+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
2672+
auto w = unit_vector(lookfrom - lookat);
2673+
auto u = unit_vector(cross(vup, w));
2674+
auto v = cross(w, u);
26492675

2650-
horizontal = 2*half_width*u;
2651-
vertical = 2*half_height*v;
2676+
origin = lookfrom;
2677+
horizontal = viewport_width * u;
2678+
vertical = viewport_height * v;
2679+
lower_left_corner = origin - horizontal/2 - vertical/2 - w;
26522680
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
26532681
}
26542682

@@ -2701,16 +2729,19 @@
27012729
The reason we defocus blur in real cameras is because they need a big hole (rather than just a
27022730
pinhole) to gather light. This would defocus everything, but if we stick a lens in the hole, there
27032731
will be a certain distance where everything is in focus. You can think of a lens this way: all light
2704-
rays coming _from_ a specific point at the focal distance -- and that hit the lens -- will be bent
2732+
rays coming _from_ a specific point at the focus distance -- and that hit the lens -- will be bent
27052733
back _to_ a single point on the image sensor.
27062734

2707-
In a physical camera, the distance to that plane where things are in focus is controlled by the
2708-
distance between the lens and the film/sensor. That is why you see the lens move relative to the
2709-
camera when you change what is in focus (that may happen in your phone camera too, but the sensor
2710-
moves). The “aperture” is a hole to control how big the lens is effectively. For a real camera, if
2711-
you need more light you make the aperture bigger, and will get more defocus blur. For our virtual
2712-
camera, we can have a perfect sensor and never need more light, so we only have an aperture when we
2713-
want defocus blur.
2735+
We call the distance between the projection point and the plane where everything is in perfect focus
2736+
the _focus distance_. Be aware that the focus distance is not the same as the focal length -- the
2737+
_focal length_ is the distance between the projection point and the image plane.
2738+
2739+
In a physical camera, the focus distance is controlled by the distance between the lens and the
2740+
film/sensor. That is why you see the lens move relative to the camera when you change what is in
2741+
focus (that may happen in your phone camera too, but the sensor moves). The “aperture” is a hole to
2742+
control how big the lens is effectively. For a real camera, if you need more light you make the
2743+
aperture bigger, and will get more defocus blur. For our virtual camera, we can have a perfect
2744+
sensor and never need more light, so we only have an aperture when we want defocus blur.
27142745

27152746

27162747
A Thin Lens Approximation
@@ -2728,8 +2759,8 @@
27282759
<div class="together">
27292760
We don’t need to simulate any of the inside of the camera. For the purposes of rendering an image
27302761
outside the camera, that would be unnecessary complexity. Instead, I usually start rays from the
2731-
surface of the lens, and send them toward a virtual film plane, by finding the projection of the
2732-
film on the plane that is in focus (at the distance `focus_dist`).
2762+
lens, and send them toward the focus plane (`focus_dist` away from the lens), where everything on
2763+
that plane is in perfect focus.
27332764

27342765
![Figure [cam-film-plane]: Camera focus plane](../images/fig.cam-film-plane.jpg)
27352766

@@ -2759,31 +2790,35 @@
27592790
class camera {
27602791
public:
27612792
camera(
2762-
point3 lookfrom, point3 lookat, vec3 vup,
2793+
point3 lookfrom,
2794+
point3 lookat,
2795+
vec3 vup,
27632796
double vfov, // vertical field-of-view in degrees
2797+
double aspect_ratio,
27642798
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
2765-
double aspect_ratio, double aperture, double focus_dist
2766-
) {
2767-
origin = lookfrom;
2768-
lens_radius = aperture / 2;
2799+
double aperture,
2800+
double focus_dist
27692801
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
2770-
2802+
) {
27712803
auto theta = degrees_to_radians(vfov);
2772-
auto half_height = tan(theta/2);
2773-
auto half_width = aspect_ratio * half_height;
2804+
auto h = tan(theta/2);
2805+
auto viewport_height = 2.0 * h;
2806+
auto viewport_width = aspect_ratio * viewport_height;
27742807

2808+
2809+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
27752810
w = unit_vector(lookfrom - lookat);
27762811
u = unit_vector(cross(vup, w));
27772812
v = cross(w, u);
2813+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
27782814

2815+
origin = lookfrom;
27792816
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
2780-
lower_left_corner = origin
2781-
- half_width * focus_dist * u
2782-
- half_height * focus_dist * v
2783-
- focus_dist * w;
2817+
horizontal = focus_dist * viewport_width * u;
2818+
vertical = focus_dist * viewport_height * v;
2819+
lower_left_corner = origin - horizontal/2 - vertical/2 - focus_dist*w;
27842820

2785-
horizontal = 2*half_width*focus_dist*u;
2786-
vertical = 2*half_height*focus_dist*v;
2821+
lens_radius = aperture / 2;
27872822
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
27882823
}
27892824

books/RayTracingTheNextWeek.html

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -108,35 +108,37 @@
108108
class camera {
109109
public:
110110
camera(
111-
point3 lookfrom, point3 lookat, vec3 vup,
111+
point3 lookfrom,
112+
point3 lookat,
113+
vec3 vup,
112114
double vfov, // vertical field-of-view in degrees
113-
double aspect_ratio, double aperture, double focus_dist,
115+
double aspect_ratio,
116+
double aperture,
117+
double focus_dist,
114118
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
115-
double t0 = 0, double t1 = 0
119+
double t0 = 0,
120+
double t1 = 0
116121
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
117122
) {
118-
origin = lookfrom;
119-
lens_radius = aperture / 2;
120-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
121-
time0 = t0;
122-
time1 = t1;
123-
124-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
125123
auto theta = degrees_to_radians(vfov);
126-
auto half_height = tan(theta/2);
127-
auto half_width = aspect * half_height;
124+
auto h = tan(theta/2);
125+
auto viewport_height = 2.0 * h;
126+
auto viewport_width = aspect_ratio * viewport_height;
128127

129128
w = unit_vector(lookfrom - lookat);
130129
u = unit_vector(cross(vup, w));
131130
v = cross(w, u);
132131

133-
lower_left_corner = origin
134-
- half_width*focus_dist*u
135-
- half_height*focus_dist*v
136-
- focus_dist*w;
132+
origin = lookfrom;
133+
horizontal = focus_dist * viewport_width * u;
134+
vertical = focus_dist * viewport_height * v;
135+
lower_left_corner = origin - horizontal/2 - vertical/2 - focus_dist*w;
137136

138-
horizontal = 2*half_width*focus_dist*u;
139-
vertical = 2*half_height*focus_dist*v;
137+
lens_radius = aperture / 2;
138+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
139+
time0 = t0;
140+
time1 = t1;
141+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
140142
}
141143

142144
ray get_ray(double s, double t) const {

0 commit comments

Comments
 (0)