diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9751ea9c8..df52cfd10 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,16 +10,18 @@ Change Log -- Ray Tracing in One Weekend
### _In One Weekend_
- Delete: Remove premature `cstdlib` include; not needed until we use `rand()` (#687)
- Fix: Replace old anti-alias result image with before-and-after image (#679)
- - Fix: Listing 29: Added missing `rtweekend.h` include (#691)
- Fix: Undefined `vup` variable in camera definition (#686)
+ - Fix: Listing 29: Added missing `rtweekend.h` include (#691)
+ - Fix: Listing 33, 39: Add consistent function signature for `trilinear_interp` (#722)
- Fix: Listing 51: Add missing `hittable.h`, `rtweekend.h` includes (#693)
+ - Fix: Listing 59: ["Full glass material"] Diverged from source
- Fix: Fix error in citation section (#721)
- - Fix: Listings 33, 39: Add consistent function signature for `trilinear_interp` (#722)
### _The Next Week_
- Change: `bvh_node` no longer reorders the source vector of scene objects; uses local copy
instead (#701)
- Delete: Remove unused u,v,w variables in initial `perlin::noise()` function (#684)
+ - Fix: Listing 5: Neglected to add ray time for metal and dielectric materials (#133)
- Fix: Listing 15: In `bvh.h`, add missing `hittable_list.h` include (#690)
- Fix: Listing 33, 34, 38: Change implicit casts to explicit ones (#692)
- Fix: Listing 40: Change `perlin.h` in the caption to `texture.h` (#698)
@@ -31,9 +33,8 @@ Change Log -- Ray Tracing in One Weekend
### _The Rest of Your Life_
- Fix: Fix errors in citation section (#721)
-### _The Next Week_
- - Change: `bvh_node` no longer reorders the source vector of scene objects; uses local copy
- instead (#701)
+### _The Rest of Your Life_
+ - Add: Listing 36: Add missing updates to dielectric class for updating specular in scatter record
----------------------------------------------------------------------------------------------------
diff --git a/books/RayTracingInOneWeekend.html b/books/RayTracingInOneWeekend.html
index c1e9bac53..69d8132a9 100644
--- a/books/RayTracingInOneWeekend.html
+++ b/books/RayTracingInOneWeekend.html
@@ -2395,21 +2395,22 @@
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
class dielectric : public material {
public:
- dielectric(double ri) : ref_idx(ri) {}
+ dielectric(double index_of_refraction) : ir(index_of_refraction) {}
virtual bool scatter(
const ray& r_in, const hit_record& rec, color& attenuation, ray& scattered
) const override {
attenuation = color(1.0, 1.0, 1.0);
- double etai_over_etat = rec.front_face ? (1.0 / ref_idx) : ref_idx;
+ double refraction_ratio = rec.front_face ? (1.0/ir) : ir;
vec3 unit_direction = unit_vector(r_in.direction());
- vec3 refracted = refract(unit_direction, rec.normal, etai_over_etat);
+ vec3 refracted = refract(unit_direction, rec.normal, refraction_ratio);
scattered = ray(rec.p, refracted);
return true;
}
- double ref_idx;
+ public:
+ double ir; // Index of Refraction
};
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[Listing [dielectric]: [material.h] Dielectric material class that always refracts]
@@ -2458,7 +2459,7 @@
solution does not exist, the glass cannot refract, and therefore must reflect the ray:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
- if (etai_over_etat * sin_theta > 1.0) {
+ if (refraction_ratio * sin_theta > 1.0) {
// Must Reflect
...
} else {
@@ -2487,7 +2488,7 @@
double cos_theta = fmin(dot(-unit_direction, rec.normal), 1.0);
double sin_theta = sqrt(1.0 - cos_theta*cos_theta);
- if (etai_over_etat * sin_theta > 1.0) {
+ if (refraction_ratio * sin_theta > 1.0) {
// Must Reflect
...
} else {
@@ -2505,34 +2506,36 @@
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
class dielectric : public material {
public:
- dielectric(double ri) : ref_idx(ri) {}
+ dielectric(double index_of_refraction) : ir(index_of_refraction) {}
virtual bool scatter(
const ray& r_in, const hit_record& rec, color& attenuation, ray& scattered
) const override {
attenuation = color(1.0, 1.0, 1.0);
- double etai_over_etat = rec.front_face ? (1.0 / ref_idx) : ref_idx;
+ double refraction_ratio = rec.front_face ? (1.0/ir) : ir;
vec3 unit_direction = unit_vector(r_in.direction());
-
-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
double cos_theta = fmin(dot(-unit_direction, rec.normal), 1.0);
double sin_theta = sqrt(1.0 - cos_theta*cos_theta);
- if (etai_over_etat * sin_theta > 1.0 ) {
+
+ bool cannot_refract = refraction_ratio * sin_theta > 1.0;
+
+ // If the ray cannot refract, then return the reflected path.
+ if (cannot_refract) {
vec3 reflected = reflect(unit_direction, rec.normal);
scattered = ray(rec.p, reflected);
return true;
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
- vec3 refracted = refract(unit_direction, rec.normal, etai_over_etat);
+ vec3 refracted = refract(unit_direction, rec.normal, refraction_ratio);
scattered = ray(rec.p, refracted);
return true;
}
public:
- double ref_idx;
+ double ir; // Index of Refraction
};
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[Listing [dielectric]: [material.h] Dielectric material class with reflection]
@@ -2580,38 +2583,37 @@
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
class dielectric : public material {
public:
- dielectric(double ri) : ref_idx(ri) {}
+ dielectric(double index_of_refraction) : ir(index_of_refraction) {}
virtual bool scatter(
const ray& r_in, const hit_record& rec, color& attenuation, ray& scattered
) const override {
attenuation = color(1.0, 1.0, 1.0);
- double etai_over_etat = rec.front_face ? (1.0 / ref_idx) : ref_idx;
+ double refraction_ratio = rec.front_face ? (1.0/ir) : ir;
vec3 unit_direction = unit_vector(r_in.direction());
double cos_theta = fmin(dot(-unit_direction, rec.normal), 1.0);
double sin_theta = sqrt(1.0 - cos_theta*cos_theta);
- if (etai_over_etat * sin_theta > 1.0 ) {
- vec3 reflected = reflect(unit_direction, rec.normal);
- scattered = ray(rec.p, reflected);
- return true;
- }
+
+ bool cannot_refract = refraction_ratio * sin_theta > 1.0;
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
- double reflect_prob = schlick(cos_theta, etai_over_etat);
- if (random_double() < reflect_prob)
- {
+ // If the ray cannot refract, or if it probabilistically reflects because of its
+ // grazing angle, then return the reflected path.
+ if (cannot_refract || random_double() < schlick(cos_theta, refraction_ratio)) {
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
vec3 reflected = reflect(unit_direction, rec.normal);
scattered = ray(rec.p, reflected);
return true;
}
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
- vec3 refracted = refract(unit_direction, rec.normal, etai_over_etat);
+
+ vec3 refracted = refract(unit_direction, rec.normal, refraction_ratio);
scattered = ray(rec.p, refracted);
return true;
}
public:
- double ref_idx;
+ double ir; // Index of Refraction
};
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[Listing [glass]: [material.h] Full glass material]
diff --git a/books/RayTracingTheNextWeek.html b/books/RayTracingTheNextWeek.html
index dc0982546..9abaf5983 100644
--- a/books/RayTracingTheNextWeek.html
+++ b/books/RayTracingTheNextWeek.html
@@ -271,13 +271,12 @@
Tracking the Time of Ray Intersection
--------------------------------------
-Be sure that in the materials you have the scattered rays be at the time of the incident ray.
+Now that rays have a time property, we need to update the `material::scatter()` methods to account
+for the time of intersection:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
class lambertian : public material {
- public:
- lambertian(const color& a) : albedo(a) {}
-
+ ...
virtual bool scatter(
const ray& r_in, const hit_record& rec, color& attenuation, ray& scattered
) const override {
@@ -288,11 +287,51 @@
attenuation = albedo;
return true;
}
+ ...
+ };
+
+ class metal : public material {
+ ...
+ virtual bool scatter(
+ const ray& r_in, const hit_record& rec, color& attenuation, ray& scattered
+ ) const override {
+ vec3 reflected = reflect(unit_vector(r_in.direction()), rec.normal);
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
+ scattered = ray(rec.p, reflected + fuzz*random_in_unit_sphere(), r_in.time());
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
+ attenuation = albedo;
+ return (dot(scattered.direction(), rec.normal) > 0);
+ }
+ ...
+ };
- color albedo;
+ class dielectric : public material {
+ ...
+ virtual bool scatter(
+ const ray& r_in, const hit_record& rec, color& attenuation, ray& scattered
+ ) const override {
+
+ ...
+ if (cannot_refract || random_double() < schlick(cos_theta, refraction_ratio)) {
+ vec3 reflected = reflect(unit_direction, rec.normal);
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
+ scattered = ray(rec.p, reflected, r_in.time());
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
+ return true;
+ }
+
+ vec3 refracted = refract(unit_direction, rec.normal, etai_over_etat);
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
+ scattered = ray(rec.p, refracted, r_in.time());
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
+ return true;
+ }
+ ...
};
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- [Listing [lambertian-animate]: [material.h] Lambertian matrial for moving objects]
+ [Listing [material-time]: [material.h]
+ Handle ray time in the material::scatter() methods
+ ]
diff --git a/books/RayTracingTheRestOfYourLife.html b/books/RayTracingTheRestOfYourLife.html
index 751d9defd..564bd433a 100644
--- a/books/RayTracingTheRestOfYourLife.html
+++ b/books/RayTracingTheRestOfYourLife.html
@@ -2123,7 +2123,7 @@
We have not yet dealt with specular surfaces, nor instances that mess with the surface normal. But
this design is clean overall, and those are all fixable. For now, I will just fix `specular`. Metal
-is easy to fix.
+and dielectric materials are easy to fix.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
class metal : public material {
@@ -2146,8 +2146,41 @@
color albedo;
double fuzz;
};
+
+ ...
+
+ class dielectric : public material {
+ public:
+ ...
+ virtual bool scatter(
+ const ray& r_in, const hit_record& rec, scatter_record& srec
+ ) const override {
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
+ srec.is_specular = true;
+ srec.pdf_ptr = nullptr;
+ srec.attenuation = color(1.0, 1.0, 1.0);
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
+ double refraction_ratio = rec.front_face ? (1.0/ir) : ir;
+
+ ...
+ if (cannot_refract || random_double() < schlick(cos_theta, refraction_ratio)) {
+ vec3 reflected = reflect(unit_direction, rec.normal);
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
+ srec.specular_ray = ray(rec.p, reflected, r_in.time());
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
+ return true;
+ }
+
+ vec3 refracted = refract(unit_direction, rec.normal, refraction_ratio);
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
+ srec.specular_ray = ray(rec.p, refracted, r_in.time());
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
+ return true;
+ }
+ ...
+ };
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- [Listing [metal-scatter]: [material.h] The metal class scatter method]
+ [Listing [material-scatter]: [material.h] The metal and dielectric scatter methods]
Note that if fuzziness is high, this surface isn’t ideally specular, but the implicit sampling works
diff --git a/src/InOneWeekend/material.h b/src/InOneWeekend/material.h
index 22c23470b..5df932e1e 100644
--- a/src/InOneWeekend/material.h
+++ b/src/InOneWeekend/material.h
@@ -71,33 +71,35 @@ class metal : public material {
class dielectric : public material {
public:
- dielectric(double ri) : ref_idx(ri) {}
+ dielectric(double index_of_refraction) : ir(index_of_refraction) {}
virtual bool scatter(
const ray& r_in, const hit_record& rec, color& attenuation, ray& scattered
) const override {
attenuation = color(1.0, 1.0, 1.0);
- double etai_over_etat = rec.front_face ? (1.0 / ref_idx) : ref_idx;
+ double refraction_ratio = rec.front_face ? (1.0/ir) : ir;
vec3 unit_direction = unit_vector(r_in.direction());
double cos_theta = fmin(dot(-unit_direction, rec.normal), 1.0);
double sin_theta = sqrt(1.0 - cos_theta*cos_theta);
- if ( (etai_over_etat * sin_theta > 1.0)
- || (random_double() < schlick(cos_theta, etai_over_etat))
- ) {
+ bool cannot_refract = refraction_ratio * sin_theta > 1.0;
+
+ // If the ray cannot refract, or if it probabilistically reflects because of its
+ // grazing angle, then return the reflected path.
+ if (cannot_refract || random_double() < schlick(cos_theta, refraction_ratio)) {
vec3 reflected = reflect(unit_direction, rec.normal);
scattered = ray(rec.p, reflected);
return true;
}
- vec3 refracted = refract(unit_direction, rec.normal, etai_over_etat);
+ vec3 refracted = refract(unit_direction, rec.normal, refraction_ratio);
scattered = ray(rec.p, refracted);
return true;
}
public:
- double ref_idx;
+ double ir; // Index of Refraction
};
diff --git a/src/TheNextWeek/material.h b/src/TheNextWeek/material.h
index 1dc49ee25..5fd41ff15 100644
--- a/src/TheNextWeek/material.h
+++ b/src/TheNextWeek/material.h
@@ -76,33 +76,35 @@ class metal : public material {
class dielectric : public material {
public:
- dielectric(double ri) : ref_idx(ri) {}
+ dielectric(double index_of_refraction) : ir(index_of_refraction) {}
virtual bool scatter(
const ray& r_in, const hit_record& rec, color& attenuation, ray& scattered
) const override {
attenuation = color(1.0, 1.0, 1.0);
- double etai_over_etat = (rec.front_face) ? (1.0 / ref_idx) : ref_idx;
+ double refraction_ratio = rec.front_face ? (1.0/ir) : ir;
vec3 unit_direction = unit_vector(r_in.direction());
double cos_theta = fmin(dot(-unit_direction, rec.normal), 1.0);
double sin_theta = sqrt(1.0 - cos_theta*cos_theta);
- if ( (etai_over_etat * sin_theta > 1.0)
- || (random_double() < schlick(cos_theta, etai_over_etat))
- ) {
+ bool cannot_refract = refraction_ratio * sin_theta > 1.0;
+
+ // If the ray cannot refract, or if it probabilistically reflects because of its
+ // grazing angle, then return the reflected path.
+ if (cannot_refract || random_double() < schlick(cos_theta, refraction_ratio)) {
vec3 reflected = reflect(unit_direction, rec.normal);
scattered = ray(rec.p, reflected, r_in.time());
return true;
}
- vec3 refracted = refract(unit_direction, rec.normal, etai_over_etat);
+ vec3 refracted = refract(unit_direction, rec.normal, refraction_ratio);
scattered = ray(rec.p, refracted, r_in.time());
return true;
}
public:
- double ref_idx;
+ double ir; // Index of Refraction
};
diff --git a/src/TheRestOfYourLife/material.h b/src/TheRestOfYourLife/material.h
index 2998b73a6..c5761658b 100644
--- a/src/TheRestOfYourLife/material.h
+++ b/src/TheRestOfYourLife/material.h
@@ -104,7 +104,7 @@ class metal : public material {
class dielectric : public material {
public:
- dielectric(double ri) : ref_idx(ri) {}
+ dielectric(double index_of_refraction) : ir(index_of_refraction) {}
virtual bool scatter(
const ray& r_in, const hit_record& rec, scatter_record& srec
@@ -112,27 +112,29 @@ class dielectric : public material {
srec.is_specular = true;
srec.pdf_ptr = nullptr;
srec.attenuation = color(1.0, 1.0, 1.0);
- double etai_over_etat = (rec.front_face) ? (1.0 / ref_idx) : ref_idx;
+ double refraction_ratio = rec.front_face ? (1.0/ir) : ir;
vec3 unit_direction = unit_vector(r_in.direction());
double cos_theta = fmin(dot(-unit_direction, rec.normal), 1.0);
double sin_theta = sqrt(1.0 - cos_theta*cos_theta);
- if ( (etai_over_etat * sin_theta > 1.0)
- || (random_double() < schlick(cos_theta, etai_over_etat))
- ) {
+ bool cannot_refract = refraction_ratio * sin_theta > 1.0;
+
+ // If the ray cannot refract, or if it probabilistically reflects because of its
+ // grazing angle, then return the reflected path.
+ if (cannot_refract || random_double() < schlick(cos_theta, refraction_ratio)) {
vec3 reflected = reflect(unit_direction, rec.normal);
srec.specular_ray = ray(rec.p, reflected, r_in.time());
return true;
}
- vec3 refracted = refract(unit_direction, rec.normal, etai_over_etat);
+ vec3 refracted = refract(unit_direction, rec.normal, refraction_ratio);
srec.specular_ray = ray(rec.p, refracted, r_in.time());
return true;
}
public:
- double ref_idx;
+ double ir; // Index of Refraction
};