Skip to content

Commit 7fa2506

Browse files
authored
Refactor moving sphere to use ray for center
This basically implements Peter's original alternative of having spheres always support animation, with static spheres as a special case. In this case, using a ray to represent the sphere center allows for pretty trivial selection of the center position at a given time, and simplifies the code.
1 parent 26d2c32 commit 7fa2506

File tree

5 files changed

+59
-83
lines changed

5 files changed

+59
-83
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Change Log / Ray Tracing in One Weekend
1313
- Change -- Include hittable.h from material.h; drop `hit_record` forward declaration (#1609)
1414
- Fix -- Slight improvement to `rotate_y::hit()` function (#1484)
1515
- Fix -- Fixed possible bogus values from `random_unit_vector()` due to underflow (#1606)
16+
- Change -- Refactor sphere to use ray representation for animate center (#1621)
1617

1718
### In One Weekend
1819
- Fix -- Fixed usage of the term "unit cube" for a cube of diameter two (#1555, #1603)

books/RayTracingTheNextWeek.html

Lines changed: 32 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -176,70 +176,67 @@
176176
----------------------
177177
Now to create a moving object. I’ll update the sphere class so that its center moves linearly from
178178
`center1` at time=0 to `center2` at time=1. (It continues on indefinitely outside that time
179-
interval, so it really can be sampled at any time.)
179+
interval, so it really can be sampled at any time.) We'll do this by replacing the 3D center point
180+
with a 3D ray that describes the original position at time=0 and the displacement to the end
181+
position at time=1.
180182

181183
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
182184
class sphere : public hittable {
183185
public:
184186
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
185187
// Stationary Sphere
186-
sphere(const point3& center, double radius, shared_ptr<material> mat)
187-
: center1(center), radius(std::fmax(0,radius)), mat(mat), is_moving(false) {}
188+
sphere(const point3& static_center, double radius, shared_ptr<material> mat)
189+
: center(static_center, vec3(0,0,0)), radius(std::fmax(0,radius)), mat(mat) {}
188190

189191
// Moving Sphere
190192
sphere(const point3& center1, const point3& center2, double radius,
191193
shared_ptr<material> mat)
192-
: center1(center1), radius(std::fmax(0,radius)), mat(mat), is_moving(true)
193-
{
194-
center_vec = center2 - center1;
195-
}
194+
: center(center1, center2 - center1), radius(std::fmax(0,radius)), mat(mat) {}
196195
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
197196

198197
...
199198

200199
private:
201200
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
202-
point3 center1;
201+
ray center;
203202
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
204203
double radius;
205204
shared_ptr<material> mat;
206-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
207-
bool is_moving;
208-
vec3 center_vec;
209205

210-
point3 sphere_center(double time) const {
211-
// Linearly interpolate from center1 to center2 according to time, where t=0 yields
212-
// center1, and t=1 yields center2.
213-
return center1 + time*center_vec;
214-
}
215-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
216206
};
217-
218207
#endif
219208
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
220209
[Listing [moving-sphere]: <kbd>[sphere.h]</kbd> A moving sphere]
221210

222-
An alternative to making special stationary spheres is to just make them all move, but stationary
223-
spheres have the same begin and end position. I’m on the fence about that trade-off between simpler
224-
code and more efficient stationary spheres, so let your design taste guide you.
225-
226211
<div class='together'>
227212
The updated `sphere::hit()` function is almost identical to the old `sphere::hit()` function:
228-
`center` just needs to query a function `sphere_center(time)`:
213+
we just need to now determine the current position of the animated center:
229214

230215
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
231216
class sphere : public hittable {
232217
public:
233218
...
234219
bool hit(const ray& r, interval ray_t, hit_record& rec) const override {
235220
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
236-
point3 center = is_moving ? sphere_center(r.time()) : center1;
221+
point3 current_center = center.at(r.time());
222+
vec3 oc = current_center - r.origin();
237223
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
238-
vec3 oc = center - r.origin();
239224
auto a = r.direction().length_squared();
240225
auto h = dot(r.direction(), oc);
241226
auto c = oc.length_squared() - radius*radius;
227+
242228
...
229+
230+
rec.t = root;
231+
rec.p = r.at(rec.t);
232+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
233+
vec3 outward_normal = (rec.p - current_center) / radius;
234+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
235+
rec.set_face_normal(r, outward_normal);
236+
get_sphere_uv(outward_normal, rec.u, rec.v);
237+
rec.mat = mat;
238+
239+
return true;
243240
}
244241
...
245242
};
@@ -711,12 +708,12 @@
711708
class sphere : public hittable {
712709
public:
713710
// Stationary Sphere
714-
sphere(const point3& center, double radius, shared_ptr<material> mat)
711+
sphere(const point3& static_center, double radius, shared_ptr<material> mat)
715712
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
716-
: center1(center), radius(std::fmax(0,radius)), mat(mat), is_moving(false)
713+
: center(static_center, vec3(0,0,0)), radius(std::fmax(0,radius)), mat(mat)
717714
{
718715
auto rvec = vec3(radius, radius, radius);
719-
bbox = aabb(center1 - rvec, center1 + rvec);
716+
bbox = aabb(static_center - rvec, static_center + rvec);
720717
}
721718
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
722719

@@ -728,11 +725,9 @@
728725
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
729726

730727
private:
731-
point3 center1;
728+
ray center;
732729
double radius;
733730
shared_ptr<material> mat;
734-
bool is_moving;
735-
vec3 center_vec;
736731
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
737732
aabb bbox;
738733
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
@@ -754,17 +749,15 @@
754749
// Moving Sphere
755750
sphere(const point3& center1, const point3& center2, double radius,
756751
shared_ptr<material> mat)
757-
: center1(center1), radius(std::fmax(0,radius)), mat(mat), is_moving(true)
758-
{
759752
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
753+
: center(center1, center2 - center1), radius(std::fmax(0,radius)), mat(mat)
754+
{
760755
auto rvec = vec3(radius, radius, radius);
761-
aabb box1(center1 - rvec, center1 + rvec);
762-
aabb box2(center2 - rvec, center2 + rvec);
756+
aabb box1(center.at(0) - rvec, center.at(0) + rvec);
757+
aabb box2(center.at(1) - rvec, center.at(1) + rvec);
763758
bbox = aabb(box1, box2);
764-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
765-
766-
center_vec = _center2 - _center1;
767759
}
760+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
768761

769762
...
770763
};
@@ -1649,7 +1642,7 @@
16491642

16501643
rec.t = root;
16511644
rec.p = r.at(rec.t);
1652-
vec3 outward_normal = (rec.p - center) / radius;
1645+
vec3 outward_normal = (rec.p - current_center) / radius;
16531646
rec.set_face_normal(r, outward_normal);
16541647
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
16551648
get_sphere_uv(outward_normal, rec.u, rec.v);

books/RayTracingTheRestOfYourLife.html

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3771,14 +3771,15 @@
37713771
if (!this->hit(ray(origin, direction), interval(0.001, infinity), rec))
37723772
return 0;
37733773

3774-
auto cos_theta_max = std::sqrt(1 - radius*radius/(center1 - origin).length_squared());
3774+
auto dist_squared = (center.at(0) - origin).length_squared();
3775+
auto cos_theta_max = std::sqrt(1 - radius*radius/dist_squared);
37753776
auto solid_angle = 2*pi*(1-cos_theta_max);
37763777

37773778
return 1 / solid_angle;
37783779
}
37793780

37803781
vec3 random(const point3& origin) const override {
3781-
vec3 direction = center1 - origin;
3782+
vec3 direction = center.at(0) - origin;
37823783
auto distance_squared = direction.length_squared();
37833784
onb uvw(direction);
37843785
return uvw.transform(random_to_sphere(radius, distance_squared));

src/TheNextWeek/sphere.h

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -17,29 +17,27 @@
1717
class sphere : public hittable {
1818
public:
1919
// Stationary Sphere
20-
sphere(const point3& center, double radius, shared_ptr<material> mat)
21-
: center1(center), radius(std::fmax(0,radius)), mat(mat), is_moving(false)
20+
sphere(const point3& static_center, double radius, shared_ptr<material> mat)
21+
: center(static_center, vec3(0,0,0)), radius(std::fmax(0,radius)), mat(mat)
2222
{
2323
auto rvec = vec3(radius, radius, radius);
24-
bbox = aabb(center1 - rvec, center1 + rvec);
24+
bbox = aabb(static_center - rvec, static_center + rvec);
2525
}
2626

2727
// Moving Sphere
2828
sphere(const point3& center1, const point3& center2, double radius,
2929
shared_ptr<material> mat)
30-
: center1(center1), radius(std::fmax(0,radius)), mat(mat), is_moving(true)
30+
: center(center1, center2 - center1), radius(std::fmax(0,radius)), mat(mat)
3131
{
3232
auto rvec = vec3(radius, radius, radius);
33-
aabb box1(center1 - rvec, center1 + rvec);
34-
aabb box2(center2 - rvec, center2 + rvec);
33+
aabb box1(center.at(0) - rvec, center.at(0) + rvec);
34+
aabb box2(center.at(1) - rvec, center.at(1) + rvec);
3535
bbox = aabb(box1, box2);
36-
37-
center_vec = center2 - center1;
3836
}
3937

4038
bool hit(const ray& r, interval ray_t, hit_record& rec) const override {
41-
point3 center = is_moving ? sphere_center(r.time()) : center1;
42-
vec3 oc = center - r.origin();
39+
point3 current_center = center.at(r.time());
40+
vec3 oc = current_center - r.origin();
4341
auto a = r.direction().length_squared();
4442
auto h = dot(r.direction(), oc);
4543
auto c = oc.length_squared() - radius*radius;
@@ -60,7 +58,7 @@ class sphere : public hittable {
6058

6159
rec.t = root;
6260
rec.p = r.at(rec.t);
63-
vec3 outward_normal = (rec.p - center) / radius;
61+
vec3 outward_normal = (rec.p - current_center) / radius;
6462
rec.set_face_normal(r, outward_normal);
6563
get_sphere_uv(outward_normal, rec.u, rec.v);
6664
rec.mat = mat;
@@ -71,19 +69,11 @@ class sphere : public hittable {
7169
aabb bounding_box() const override { return bbox; }
7270

7371
private:
74-
point3 center1;
72+
ray center;
7573
double radius;
7674
shared_ptr<material> mat;
77-
bool is_moving;
78-
vec3 center_vec;
7975
aabb bbox;
8076

81-
point3 sphere_center(double time) const {
82-
// Linearly interpolate from center1 to center2 according to time, where t=0 yields
83-
// center1, and t=1 yields center2.
84-
return center1 + time*center_vec;
85-
}
86-
8777
static void get_sphere_uv(const point3& p, double& u, double& v) {
8878
// p: a given point on the sphere of radius one, centered at the origin.
8979
// u: returned value [0,1] of angle around the Y axis from X=-1.

src/TheRestOfYourLife/sphere.h

Lines changed: 13 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -18,29 +18,27 @@
1818
class sphere : public hittable {
1919
public:
2020
// Stationary Sphere
21-
sphere(const point3& center, double radius, shared_ptr<material> mat)
22-
: center1(center), radius(std::fmax(0,radius)), mat(mat), is_moving(false)
21+
sphere(const point3& static_center, double radius, shared_ptr<material> mat)
22+
: center(static_center, vec3(0,0,0)), radius(std::fmax(0,radius)), mat(mat)
2323
{
2424
auto rvec = vec3(radius, radius, radius);
25-
bbox = aabb(center1 - rvec, center1 + rvec);
25+
bbox = aabb(static_center - rvec, static_center + rvec);
2626
}
2727

2828
// Moving Sphere
2929
sphere(const point3& center1, const point3& center2, double radius,
3030
shared_ptr<material> mat)
31-
: center1(center1), radius(std::fmax(0,radius)), mat(mat), is_moving(true)
31+
: center(center1, center2 - center1), radius(std::fmax(0,radius)), mat(mat)
3232
{
3333
auto rvec = vec3(radius, radius, radius);
34-
aabb box1(center1 - rvec, center1 + rvec);
35-
aabb box2(center2 - rvec, center2 + rvec);
34+
aabb box1(center.at(0) - rvec, center.at(0) + rvec);
35+
aabb box2(center.at(1) - rvec, center.at(1) + rvec);
3636
bbox = aabb(box1, box2);
37-
38-
center_vec = center2 - center1;
3937
}
4038

4139
bool hit(const ray& r, interval ray_t, hit_record& rec) const override {
42-
point3 center = is_moving ? sphere_center(r.time()) : center1;
43-
vec3 oc = center - r.origin();
40+
point3 current_center = center.at(r.time());
41+
vec3 oc = current_center - r.origin();
4442
auto a = r.direction().length_squared();
4543
auto h = dot(r.direction(), oc);
4644
auto c = oc.length_squared() - radius*radius;
@@ -61,7 +59,7 @@ class sphere : public hittable {
6159

6260
rec.t = root;
6361
rec.p = r.at(rec.t);
64-
vec3 outward_normal = (rec.p - center) / radius;
62+
vec3 outward_normal = (rec.p - current_center) / radius;
6563
rec.set_face_normal(r, outward_normal);
6664
get_sphere_uv(outward_normal, rec.u, rec.v);
6765
rec.mat = mat;
@@ -78,33 +76,26 @@ class sphere : public hittable {
7876
if (!this->hit(ray(origin, direction), interval(0.001, infinity), rec))
7977
return 0;
8078

81-
auto cos_theta_max = std::sqrt(1 - radius*radius/(center1 - origin).length_squared());
79+
auto dist_squared = (center.at(0) - origin).length_squared();
80+
auto cos_theta_max = std::sqrt(1 - radius*radius/dist_squared);
8281
auto solid_angle = 2*pi*(1-cos_theta_max);
8382

8483
return 1 / solid_angle;
8584
}
8685

8786
vec3 random(const point3& origin) const override {
88-
vec3 direction = center1 - origin;
87+
vec3 direction = center.at(0) - origin;
8988
auto distance_squared = direction.length_squared();
9089
onb uvw(direction);
9190
return uvw.transform(random_to_sphere(radius, distance_squared));
9291
}
9392

9493
private:
95-
point3 center1;
94+
ray center;
9695
double radius;
9796
shared_ptr<material> mat;
98-
bool is_moving;
99-
vec3 center_vec;
10097
aabb bbox;
10198

102-
point3 sphere_center(double time) const {
103-
// Linearly interpolate from center1 to center2 according to time, where t=0 yields
104-
// center1, and t=1 yields center2.
105-
return center1 + time*center_vec;
106-
}
107-
10899
static void get_sphere_uv(const point3& p, double& u, double& v) {
109100
// p: a given point on the sphere of radius one, centered at the origin.
110101
// u: returned value [0,1] of angle around the Y axis from X=-1.

0 commit comments

Comments
 (0)