Skip to content

Commit 07f11f5

Browse files
authored
Merge branch 'dev-patch' into refactor/rotate-y-hit
2 parents 276cf1c + c6f803f commit 07f11f5

File tree

7 files changed

+63
-57
lines changed

7 files changed

+63
-57
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,18 @@ Change Log / Ray Tracing in One Weekend
1212
height for code listings in both print and browser.
1313
- Change -- Include hittable.h from material.h; drop `hit_record` forward declaration (#1609)
1414
- Fix -- Slight improvement to `rotate_y::hit()` function (#1484)
15+
- Fix -- Fixed possible bogus values from `random_unit_vector()` due to underflow (#1606)
1516

1617
### In One Weekend
1718
- Fix -- Fixed usage of the term "unit cube" for a cube of diameter two (#1555, #1603)
1819
- Fix -- Fixed broken highlighting on some code listings (#1600)
1920

2021
### The Next Week
22+
- Fix -- Add missing ellipsis in listing 2.62 (#1612)
2123

2224
### The Rest of Your Life
2325
- Fix -- Fix typo of "arbitrary" (#1589)
26+
- New -- Added a bit more explanation of Buffon's needle problem (#1529)
2427

2528

2629
----------------------------------------------------------------------------------------------------

books/RayTracingInOneWeekend.html

Lines changed: 38 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2292,24 +2292,29 @@
22922292
surprisingly complicated to understand, and quite a bit complicated to implement. Instead, we'll use
22932293
what is typically the easiest algorithm: A rejection method. A rejection method works by repeatedly
22942294
generating random samples until we produce a sample that meets the desired criteria. In other words,
2295-
keep rejecting samples until you find a good one.
2295+
keep rejecting bad samples until you find a good one.
22962296

22972297
There are many equally valid ways of generating a random vector on a hemisphere using the rejection
22982298
method, but for our purposes we will go with the simplest, which is:
22992299

2300-
1. Generate a random vector inside of the unit sphere
2301-
2. Normalize this vector
2300+
1. Generate a random vector inside the unit sphere
2301+
2. Normalize this vector to extend it to the sphere surface
23022302
3. Invert the normalized vector if it falls onto the wrong hemisphere
23032303

23042304
<div class='together'>
23052305
First, we will use a rejection method to generate the random vector inside the unit sphere (that is,
23062306
a sphere of radius 1). Pick a random point inside the cube enclosing the unit sphere (that is, where
2307-
$x$, $y$, and $z$ are all in the range $[-1,+1]$). If this point lies outside (or on) the unit
2308-
sphere, then generate a new one until we find one that lies inside the unit sphere.
2307+
$x$, $y$, and $z$ are all in the range $[-1,+1]$). If this point lies outside the unit
2308+
sphere, then generate a new one until we find one that lies inside or on the unit sphere.
23092309

2310-
![Figure [sphere-vec]: Two vectors were rejected before finding a good one
2310+
![Figure [sphere-vec]: Two vectors were rejected before finding a good one (pre-normalization)
23112311
](../images/fig-1.11-sphere-vec.jpg)
23122312

2313+
![Figure [sphere-vec]: The accepted random vector is normalized to produce a unit vector
2314+
](../images/fig-1.12-sphere-unit-vec.jpg)
2315+
2316+
Here's our first draft of the function:
2317+
23132318
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
23142319
...
23152320

@@ -2319,49 +2324,45 @@
23192324

23202325

23212326
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
2322-
inline vec3 random_in_unit_sphere() {
2327+
inline vec3 random_unit_vector() {
23232328
while (true) {
23242329
auto p = vec3::random(-1,1);
2325-
if (p.length_squared() < 1)
2326-
return p;
2330+
auto lensq = p.length_squared();
2331+
if (lensq <= 1)
2332+
return p / sqrt(lensq);
23272333
}
23282334
}
23292335
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2330-
[Listing [random-in-unit-sphere]: <kbd>[vec3.h]</kbd> The random_in_unit_sphere() function]
2336+
[Listing [random-in-unit-sphere]: <kbd>[vec3.h]</kbd> The random_unit_vector() function, version one]
23312337

23322338
</div>
23332339

2334-
<div class='together'>
2335-
Once we have a random vector in the unit sphere we need to normalize it to get a vector _on_ the
2336-
unit sphere.
2340+
Sadly, we have a small floating-point abstraction leak to deal with. Since floating-point numbers
2341+
have finite precision, a very small value can underflow to zero when squared. So if all three
2342+
coordinates are small enough (that is, very near the center of the sphere), the norm of the vector
2343+
will be zero, and thus normalizing will yield the bogus vector $[\pm\infty, \pm\infty, \pm\infty]$.
2344+
To fix this, we'll also reject points that lie inside this "black hole" around the center. With
2345+
double precision (64-bit floats), we can safely support values greater than $10^{-160}$.
23372346

2338-
![Figure [sphere-vec]: The accepted random vector is normalized to produce a unit vector
2339-
](../images/fig-1.12-sphere-unit-vec.jpg)
2347+
Here's our more robust function:
23402348

23412349
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
2342-
...
2343-
2344-
inline vec3 random_in_unit_sphere() {
2350+
inline vec3 random_unit_vector() {
23452351
while (true) {
23462352
auto p = vec3::random(-1,1);
2347-
if (p.length_squared() < 1)
2348-
return p;
2349-
}
2350-
}
2351-
2352-
2353+
auto lensq = p.length_squared();
23532354
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
2354-
inline vec3 random_unit_vector() {
2355-
return unit_vector(random_in_unit_sphere());
2355+
if (1e-160 < lensq && lensq <= 1)
2356+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
2357+
return p / sqrt(lensq);
2358+
}
23562359
}
23572360
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2358-
[Listing [random-unit-vec]: <kbd>[vec3.h]</kbd> Random vector on the unit sphere]
2359-
2360-
</div>
2361+
[Listing [random-in-unit-sphere]: <kbd>[vec3.h]</kbd> The random_unit_vector() function, version one]
23612362

23622363
<div class='together'>
2363-
And now that we have a random vector on the surface of the unit sphere, we can determine if it is on
2364-
the correct hemisphere by comparing against the surface normal:
2364+
Now that we have a random vector on the surface of the unit sphere, we can determine if it is on the
2365+
correct hemisphere by comparing against the surface normal:
23652366

23662367
![Figure [normal-hor]: The normal vector tells us which hemisphere we need
23672368
](../images/fig-1.13-surface-normal.jpg)
@@ -2377,7 +2378,12 @@
23772378
...
23782379

23792380
inline vec3 random_unit_vector() {
2380-
return unit_vector(random_in_unit_sphere());
2381+
while (true) {
2382+
auto p = vec3::random(-1,1);
2383+
auto lensq = p.length_squared();
2384+
if (1e-160 < lensq && lensq <= 1)
2385+
return p / sqrt(lensq);
2386+
}
23812387
}
23822388

23832389

books/RayTracingTheNextWeek.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3537,6 +3537,7 @@
35373537
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
35383538

35393539
class quad : public hittable {
3540+
...
35403541
};
35413542

35423543

books/RayTracingTheRestOfYourLife.html

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,13 @@
9494
Estimating Pi
9595
--------------
9696
The canonical example of a Monte Carlo algorithm is estimating $\pi$, so let's do that. There are
97-
many ways to estimate $\pi$, with the Buffon Needle problem being a classic case study. We’ll do a
98-
variation inspired by this method. Suppose you have a circle inscribed inside a square:
97+
many ways to estimate $\pi$, with _Buffon's needle problem_ being a classic case study. In Buffon's
98+
needle problem, one is presented with a floor made of parallel strips of floor board, each of the
99+
same width. If a needle is randomly dropped onto the floor, what is the probability that the needle
100+
will lie across two boards? (You can find more information on this problem with a simple Internet
101+
search.)
102+
103+
We’ll do a variation inspired by this method. Suppose you have a circle inscribed inside a square:
99104

100105
![Figure [circ-square]: Estimating $\pi$ with a circle inside a square
101106
](../images/fig-3.01-circ-square.jpg)
@@ -449,8 +454,8 @@
449454

450455
One Dimensional Monte Carlo Integration
451456
====================================================================================================
452-
Our Buffon Needle example is a way of calculating $\pi$ by solving for the ratio of the area of the
453-
circle and the area of the circumscribed square:
457+
Our variation of Buffon's needle problem is a way of calculating $\pi$ by solving for the ratio of
458+
the area of the circle and the area of the circumscribed square:
454459

455460
$$ \frac{\operatorname{area}(\mathit{circle})}{\operatorname{area}(\mathit{square})}
456461
= \frac{\pi}{4}

src/InOneWeekend/vec3.h

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -121,18 +121,15 @@ inline vec3 random_in_unit_disk() {
121121
}
122122
}
123123

124-
inline vec3 random_in_unit_sphere() {
124+
inline vec3 random_unit_vector() {
125125
while (true) {
126126
auto p = vec3::random(-1,1);
127-
if (p.length_squared() < 1)
128-
return p;
127+
auto lensq = p.length_squared();
128+
if (1e-160 < lensq && lensq <= 1.0)
129+
return p / sqrt(lensq);
129130
}
130131
}
131132

132-
inline vec3 random_unit_vector() {
133-
return unit_vector(random_in_unit_sphere());
134-
}
135-
136133
inline vec3 random_on_hemisphere(const vec3& normal) {
137134
vec3 on_unit_sphere = random_unit_vector();
138135
if (dot(on_unit_sphere, normal) > 0.0) // In the same hemisphere as the normal

src/TheNextWeek/vec3.h

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -121,18 +121,15 @@ inline vec3 random_in_unit_disk() {
121121
}
122122
}
123123

124-
inline vec3 random_in_unit_sphere() {
124+
inline vec3 random_unit_vector() {
125125
while (true) {
126126
auto p = vec3::random(-1,1);
127-
if (p.length_squared() < 1)
128-
return p;
127+
auto lensq = p.length_squared();
128+
if (1e-160 < lensq && lensq <= 1.0)
129+
return p / sqrt(lensq);
129130
}
130131
}
131132

132-
inline vec3 random_unit_vector() {
133-
return unit_vector(random_in_unit_sphere());
134-
}
135-
136133
inline vec3 random_on_hemisphere(const vec3& normal) {
137134
vec3 on_unit_sphere = random_unit_vector();
138135
if (dot(on_unit_sphere, normal) > 0.0) // In the same hemisphere as the normal

src/TheRestOfYourLife/vec3.h

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -121,18 +121,15 @@ inline vec3 random_in_unit_disk() {
121121
}
122122
}
123123

124-
inline vec3 random_in_unit_sphere() {
124+
inline vec3 random_unit_vector() {
125125
while (true) {
126126
auto p = vec3::random(-1,1);
127-
if (p.length_squared() < 1)
128-
return p;
127+
auto lensq = p.length_squared();
128+
if (1e-160 < lensq && lensq <= 1.0)
129+
return p / sqrt(lensq);
129130
}
130131
}
131132

132-
inline vec3 random_unit_vector() {
133-
return unit_vector(random_in_unit_sphere());
134-
}
135-
136133
inline vec3 random_on_hemisphere(const vec3& normal) {
137134
vec3 on_unit_sphere = random_unit_vector();
138135
if (dot(on_unit_sphere, normal) > 0.0) // In the same hemisphere as the normal

0 commit comments

Comments
 (0)