Skip to content

Commit 1f50cce

Browse files
authored
Merge pull request #764 from RayTracing/fix-degenerate-scatter
lambertian::scatter degenerate direction vec test
2 parents 1159d7f + 3912593 commit 1f50cce

File tree

7 files changed

+97
-8
lines changed

7 files changed

+97
-8
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@ Change Log -- Ray Tracing in One Weekend
1111
- Fix: Synchronize copies of `hittable_list.h`, `material.h`, `sphere.h`
1212

1313
### In One Weekend
14+
- Fix: Catch cases where `lambertian::scatter()` yields degenerate scatter rays (#619)
1415

1516
### The Next Week
17+
- Fix: Catch cases where `lambertian::scatter()` yields degenerate scatter rays (#619)
1618

1719
### The Rest of Your Life
1820
- Fix: Missing `override` keyword for `xz_rect::pdf_value()` and `xz_rect::random()` methods

books/RayTracingInOneWeekend.html

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2065,7 +2065,7 @@
20652065
virtual bool scatter(
20662066
const ray& r_in, const hit_record& rec, color& attenuation, ray& scattered
20672067
) const override {
2068-
vec3 scatter_direction = rec.normal + random_unit_vector();
2068+
auto scatter_direction = rec.normal + random_unit_vector();
20692069
scattered = ray(rec.p, scatter_direction);
20702070
attenuation = albedo;
20712071
return true;
@@ -2081,6 +2081,57 @@
20812081
Note we could just as well only scatter with some probability $p$ and have attenuation be
20822082
$albedo/p$. Your choice.
20832083

2084+
If you read the code above carefully, you'll notice a small chance of mischief. If the random unit
2085+
vector we generate is exactly opposite the normal vector, the two will sum to zero, which will
2086+
result in a zero scatter direction vector. This leads to bad scenarios later on (infinities and
2087+
NaNs), so we need to intercept the condition before we pass it on.
2088+
2089+
<div class='together'>
2090+
In service of this, we'll create a new vector method -- `vec3::near_zero()` -- that returns true if
2091+
the vector is very close to zero in all dimensions.
2092+
2093+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
2094+
class vec3 {
2095+
...
2096+
bool near_zero() const {
2097+
// Return true if the vector is close to zero in all dimensions.
2098+
const auto s = 1e-8;
2099+
return (fabs(e[0]) < s) && (fabs(e[1]) < s) && (fabs(e[2]) < s);
2100+
}
2101+
...
2102+
};
2103+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2104+
[Listing [vec3-near-zero]: <kbd>[vec3.h]</kbd> The vec3::near_zero() method]
2105+
2106+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
2107+
class lambertian : public material {
2108+
public:
2109+
lambertian(const color& a) : albedo(a) {}
2110+
2111+
virtual bool scatter(
2112+
const ray& r_in, const hit_record& rec, color& attenuation, ray& scattered
2113+
) const override {
2114+
auto scatter_direction = rec.normal + random_unit_vector();
2115+
2116+
2117+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
2118+
// Catch degenerate scatter direction
2119+
if (scatter_direction.near_zero())
2120+
scatter_direction = rec.normal;
2121+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
2122+
2123+
scattered = ray(rec.p, scatter_direction);
2124+
attenuation = albedo;
2125+
return true;
2126+
}
2127+
2128+
public:
2129+
color albedo;
2130+
};
2131+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2132+
[Listing [lambertian-catch-zero]: <kbd>[material.h]</kbd> Lambertian scatter, bullet-proof]
2133+
</div>
2134+
20842135

20852136
Mirrored Light Reflection
20862137
--------------------------

books/RayTracingTheNextWeek.html

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,13 @@
280280
virtual bool scatter(
281281
const ray& r_in, const hit_record& rec, color& attenuation, ray& scattered
282282
) const override {
283-
vec3 scatter_direction = rec.normal + random_unit_vector();
283+
auto scatter_direction = rec.normal + random_unit_vector();
284+
285+
// Catch degenerate scatter direction
286+
if (scatter_direction.near_zero())
287+
scatter_direction = rec.normal;
288+
289+
284290
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
285291
scattered = ray(rec.p, scatter_direction, r_in.time());
286292
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
@@ -1252,7 +1258,12 @@
12521258
virtual bool scatter(
12531259
const ray& r_in, const hit_record& rec, color& attenuation, ray& scattered
12541260
) const override {
1255-
vec3 scatter_direction = rec.normal + random_unit_vector();
1261+
auto scatter_direction = rec.normal + random_unit_vector();
1262+
1263+
// Catch degenerate scatter direction
1264+
if (scatter_direction.near_zero())
1265+
scatter_direction = rec.normal;
1266+
12561267
scattered = ray(rec.p, scatter_direction, r_in.time());
12571268
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
12581269
attenuation = albedo->value(rec.u, rec.v, rec.p);

books/RayTracingTheRestOfYourLife.html

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -858,15 +858,23 @@
858858
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
859859
class lambertian : public material {
860860
public:
861+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
861862
lambertian(const color& a) : albedo(make_shared<solid_color>(a)) {}
862863
lambertian(shared_ptr<texture> a) : albedo(a) {}
864+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
863865

864-
865-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
866866
virtual bool scatter(
867+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
867868
const ray& r_in, const hit_record& rec, color& alb, ray& scattered, double& pdf
869+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
868870
) const override {
869-
auto direction = rec.normal + random_unit_vector();
871+
auto scatter_direction = rec.normal + random_unit_vector();
872+
873+
// Catch degenerate scatter direction
874+
if (scatter_direction.near_zero())
875+
scatter_direction = rec.normal;
876+
877+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
870878
scattered = ray(rec.p, unit_vector(direction), r_in.time());
871879
alb = albedo->value(rec.u, rec.v, rec.p);
872880
pdf = dot(rec.normal, scattered.direction()) / pi;

src/InOneWeekend/material.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,12 @@ class lambertian : public material {
3232
virtual bool scatter(
3333
const ray& r_in, const hit_record& rec, color& attenuation, ray& scattered
3434
) const override {
35-
vec3 scatter_direction = rec.normal + random_unit_vector();
35+
auto scatter_direction = rec.normal + random_unit_vector();
36+
37+
// Catch degenerate scatter direction
38+
if (scatter_direction.near_zero())
39+
scatter_direction = rec.normal;
40+
3641
scattered = ray(rec.p, scatter_direction);
3742
attenuation = albedo;
3843
return true;

src/TheNextWeek/material.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,12 @@ class lambertian : public material {
3737
virtual bool scatter(
3838
const ray& r_in, const hit_record& rec, color& attenuation, ray& scattered
3939
) const override {
40-
vec3 scatter_direction = rec.normal + random_unit_vector();
40+
auto scatter_direction = rec.normal + random_unit_vector();
41+
42+
// Catch degenerate scatter direction
43+
if (scatter_direction.near_zero())
44+
scatter_direction = rec.normal;
45+
4146
scattered = ray(rec.p, scatter_direction, r_in.time());
4247
attenuation = albedo->value(rec.u, rec.v, rec.p);
4348
return true;

src/common/vec3.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <iostream>
1616

1717
using std::sqrt;
18+
using std::fabs;
1819

1920
class vec3 {
2021
public:
@@ -55,6 +56,12 @@ class vec3 {
5556
return e[0]*e[0] + e[1]*e[1] + e[2]*e[2];
5657
}
5758

59+
bool near_zero() const {
60+
// Return true if the vector is close to zero in all dimensions.
61+
const auto s = 1e-8;
62+
return (fabs(e[0]) < s) && (fabs(e[1]) < s) && (fabs(e[2]) < s);
63+
}
64+
5865
inline static vec3 random() {
5966
return vec3(random_double(), random_double(), random_double());
6067
}

0 commit comments

Comments
 (0)