(256 * clamp(b, 0.0, 0.999)) << '\n';
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- [Listing [write-color-gamma]: [vec3.h] write_color(), with gamma correction]
+ [Listing [write-color-gamma]: [color.h] write_color(), with gamma correction]
That yields light grey, as we desire:
-
-
- ![Diffuse sphere, with gamma correction](../images/img.gamma-correct.jpg)
-
-
+ ![Diffuse sphere, with gamma correction](../images/img.gamma-correct.jpg class=pixel)
@@ -1730,11 +1730,7 @@
After rendering we get a similar image:
-
-
- ![Correct rendering of Lambertian spheres](../images/img.correct-lambertian.png)
-
-
+ ![Correct rendering of Lambertian spheres](../images/img.correct-lambertian.png class=pixel)
It's hard to tell the difference between these two diffuse methods, given that our scene of two
spheres is so simple, but you should be able to notice two important visual differences:
@@ -1813,11 +1809,8 @@
Gives us the following image:
-
-
- ![Rendering of diffuse spheres with hemispherical scattering](../images/img.rand-hemispherical.png)
-
-
+ ![Rendering of diffuse spheres with hemispherical scattering
+ ](../images/img.rand-hemispherical.png class=pixel)
@@ -2127,7 +2120,7 @@
ray r = cam.get_ray(u, v);
pixel_color += ray_color(r, world, max_depth);
}
- pixel_color.write_color(std::cout, samples_per_pixel);
+ write_color(std::cout, pixel_color, samples_per_pixel);
}
}
@@ -2140,11 +2133,7 @@
Which gives:
-
-
- ![Shiny metal](../images/img.metal-shiny.png)
-
-
+ ![Shiny metal](../images/img.metal-shiny.png class=pixel)
@@ -2197,11 +2186,7 @@
We can try that out by adding fuzziness 0.3 and 1.0 to the metals:
-
-
- ![Fuzzed metal](../images/img.metal-fuzz.png)
-
-
+ ![Fuzzed metal](../images/img.metal-fuzz.png class=pixel)
@@ -2222,11 +2207,7 @@
there is a refraction ray at all. For this project, I tried to put two glass balls in our scene, and
I got this (I have not told you how to do this right or wrong yet, but soon!):
-
-
- ![Glass first](../images/img.glass-first.png)
-
-
+ ![Glass first](../images/img.glass-first.png class=pixel)
@@ -2330,11 +2311,7 @@
This gives us the following result:
-
-
- ![Glass sphere that always refracts](../images/img.glass-always-refract.png)
-
-
+ ![Glass sphere that always refracts](../images/img.glass-always-refract.png class=pixel)
Total Internal Reflection
@@ -2385,7 +2362,7 @@
$$ \cos\theta = \mathbf{R} \cdot \mathbf{n} $$
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
- double cos_theta = ffmin(dot(-unit_direction, rec.normal), 1.0);
+ 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) {
// Must Reflect
@@ -2416,7 +2393,7 @@
vec3 unit_direction = unit_vector(r_in.direction());
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
- double cos_theta = ffmin(dot(-unit_direction, rec.normal), 1.0);
+ 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);
@@ -2455,11 +2432,7 @@
We get:
-
-
- ![Glass sphere that sometimes refracts](../images/img.glass-sometimes-refract.png)
-
-
+ ![Glass sphere that sometimes refracts](../images/img.glass-sometimes-refract.png class=pixel)
@@ -2495,7 +2468,7 @@
double etai_over_etat = (rec.front_face) ? (1.0 / ref_idx) : (ref_idx);
vec3 unit_direction = unit_vector(r_in.direction());
- double cos_theta = ffmin(dot(-unit_direction, rec.normal), 1.0);
+ 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);
@@ -2544,11 +2517,7 @@
This gives:
-
-
- ![A hollow glass sphere](../images/img.glass-hollow.png)
-
-
+ ![A hollow glass sphere](../images/img.glass-hollow.png class=pixel)
@@ -2625,11 +2594,7 @@
gives:
-
-
- ![A wide-angle view](../images/img.wide-view.png)
-
-
+ ![A wide-angle view](../images/img.wide-view.png class=pixel)
@@ -2713,11 +2678,7 @@
to get:
-
-
- ![A distant view](../images/img.view-distant.png)
-
-
+ ![A distant view](../images/img.view-distant.png class=pixel)
And we can change field of view:
@@ -2728,11 +2689,7 @@
to get:
-
-
- ![Zooming in](../images/img.view-zoom.png)
-
-
+ ![Zooming in](../images/img.view-zoom.png class=pixel)
@@ -2874,11 +2831,7 @@
We get:
-
-
- ![Spheres with depth-of-field](../images/img.depth-of-field.png)
-
-
+ ![Spheres with depth-of-field](../images/img.depth-of-field.png class=pixel)
@@ -2955,11 +2908,8 @@
This gives:
-
-
![Final scene](../images/img.book1-final.jpg)
-
An interesting thing you might note is the glass balls don’t really have shadows which makes them
diff --git a/books/RayTracingTheNextWeek.html b/books/RayTracingTheNextWeek.html
index d55af51a..eb6aa558 100644
--- a/books/RayTracingTheNextWeek.html
+++ b/books/RayTracingTheNextWeek.html
@@ -41,12 +41,12 @@
Motion Blur
====================================================================================================
-When you decided to ray trace, you decided visual quality was worth more run-time. In your fuzzy
-reflection and defocus blur you needed multiple samples per pixel. Once you have taken a step down
-that road, the good news is that almost all effects can be brute-forced. Motion blur is certainly
-one of those. In a real camera, the shutter opens and stays open for a time interval, and the camera
-and objects may move during that time. Its really an average of what the camera sees over that
-interval that we want.
+When you decided to ray trace, you decided that visual quality was worth more than run-time. In your
+fuzzy reflection and defocus blur you needed multiple samples per pixel. Once you have taken a step
+down that road, the good news is that almost all effects can be brute-forced. Motion blur is
+certainly one of those. In a real camera, the shutter opens and stays open for a time interval, and
+the camera and objects may move during that time. Its really an average of what the camera sees over
+that interval that we want.
Introduction of SpaceTime Ray Tracing
@@ -352,11 +352,7 @@
gives the following result:
-
-
- ![Bouncing spheres](../images/img.bouncing-spheres.jpg)
-
-
+ ![Bouncing spheres](../images/img.bouncing-spheres.jpg class=pixel)
@@ -584,12 +580,12 @@
bool hit(const ray& r, double tmin, double tmax) const {
for (int a = 0; a < 3; a++) {
- auto t0 = ffmin((_min[a] - r.origin()[a]) / r.direction()[a],
- (_max[a] - r.origin()[a]) / r.direction()[a]);
- auto t1 = ffmax((_min[a] - r.origin()[a]) / r.direction()[a],
- (_max[a] - r.origin()[a]) / r.direction()[a]);
- tmin = ffmax(t0, tmin);
- tmax = ffmin(t1, tmax);
+ auto t0 = fmin((_min[a] - r.origin()[a]) / r.direction()[a],
+ (_max[a] - r.origin()[a]) / r.direction()[a]);
+ auto t1 = fmax((_min[a] - r.origin()[a]) / r.direction()[a],
+ (_max[a] - r.origin()[a]) / r.direction()[a]);
+ tmin = fmax(t0, tmin);
+ tmax = fmin(t1, tmax);
if (tmax <= tmin)
return false;
}
@@ -603,10 +599,6 @@
[Listing [aabb]: [aabb.h] Axis-aligned bounding box class]
-Note that we use the simple custom `ffmax()` function (defined in `rtweekend.h`) instead of the C++
-standard library `fmax()` utility. `ffmax()` is quite a bit faster because it doesn’t worry about
-`NaN`s and other exceptions.
-
An Optimized AABB Hit Method
-----------------------------
@@ -722,13 +714,13 @@
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
aabb surrounding_box(aabb box0, aabb box1) {
- point3 small(ffmin(box0.min().x(), box1.min().x()),
- ffmin(box0.min().y(), box1.min().y()),
- ffmin(box0.min().z(), box1.min().z()));
+ point3 small(fmin(box0.min().x(), box1.min().x()),
+ fmin(box0.min().y(), box1.min().y()),
+ fmin(box0.min().z(), box1.min().z()));
- point3 big(ffmax(box0.max().x(), box1.max().x()),
- ffmax(box0.max().y(), box1.max().y()),
- ffmax(box0.max().z(), box1.max().z()));
+ point3 big(fmax(box0.max().x(), box1.max().x()),
+ fmax(box0.max().y(), box1.max().y()),
+ fmax(box0.max().z(), box1.max().z()));
return aabb(small,big);
}
@@ -1058,11 +1050,7 @@
We get:
-
-
- ![Spheres on checkered ground](../images/img.checker-ground.jpg)
-
-
+ ![Spheres on checkered ground](../images/img.checker-ground.jpg class=pixel)
@@ -1105,11 +1093,7 @@
We get:
-
-
- ![Checkered spheres](../images/img.checker-spheres.jpg)
-
-
+ ![Checkered spheres](../images/img.checker-spheres.jpg class=pixel)
@@ -1122,19 +1106,11 @@
To get cool looking solid textures most people use some form of Perlin noise. These are named after
their inventor Ken Perlin. Perlin texture doesn’t return white noise like this:
-
-
- ![White noise](../images/img.white-noise.jpg)
-
-
+ ![White noise](../images/img.white-noise.jpg class=pixel)
Instead it returns something similar to blurred white noise:
-
-
- ![White noise, blurred](../images/img.white-noise-blur.jpg)
-
-
+ ![White noise, blurred](../images/img.white-noise-blur.jpg class=pixel)
@@ -1150,11 +1126,7 @@
We could just tile all of space with a 3D array of random numbers and use them in blocks. You get
something blocky where the repeating is clear:
-
-
- ![Tiled random patterns](../images/img.tile-random.jpg)
-
-
+ ![Tiled random patterns](../images/img.tile-random.jpg class=pixel)
@@ -1284,11 +1256,7 @@
Add the hashing does scramble as hoped:
-
-
- ![Hashed random texture](../images/img.hash-random.jpg)
-
-
+ ![Hashed random texture](../images/img.hash-random.jpg class=pixel)
@@ -1343,11 +1311,7 @@
And we get:
-
-
- ![Perlin texture with trilinear interpolation](../images/img.perlin-trilerp.jpg)
-
-
+ ![Perlin texture with trilinear interpolation](../images/img.perlin-trilerp.jpg class=pixel)
@@ -1385,11 +1349,8 @@
This gives a smoother looking image:
-
-
- ![Perlin texture, trilinearly interpolated, smoothed](../images/img.perlin-tlerp-smooth.jpg)
-
-
+ ![Perlin texture, trilinearly interpolated, smoothed
+ ](../images/img.perlin-tlerp-smooth.jpg class=pixel)
@@ -1420,11 +1381,7 @@
which gives:
-
-
- ![Perlin texture, higher frequency](../images/img.perlin-hifreq.jpg)
-
-
+ ![Perlin texture, higher frequency](../images/img.perlin-hifreq.jpg class=pixel)
@@ -1564,11 +1521,7 @@
This finally gives something more reasonable looking:
-
-
- ![Perlin texture, shifted off integer values](../images/img.perlin-shift.jpg)
-
-
+ ![Perlin texture, shifted off integer values](../images/img.perlin-shift.jpg class=pixel)
@@ -1607,11 +1560,7 @@
Used directly, turbulence gives a sort of camouflage netting appearance:
-
-
- ![Perlin texture with turbulence](../images/img.perlin-turb.jpg)
-
-
+ ![Perlin texture with turbulence](../images/img.perlin-turb.jpg class=pixel)
@@ -1646,11 +1595,7 @@
Which yields:
-
-
- ![Perlin noise, marbled texture](../images/img.perlin-marble.jpg)
-
-
+ ![Perlin noise, marbled texture](../images/img.perlin-marble.jpg class=pixel)
@@ -1827,11 +1772,7 @@
I just grabbed a random earth map from the web -- any standard projection will do for our purposes.
-
-
- ![earthmap.jpg](../images/earthmap.jpg)
-
-
+ ![earthmap.jpg](../images/earthmap.jpg class=pixel)
@@ -1857,11 +1798,7 @@
To test this, assign it to a sphere, and then temporarily cripple the `ray_color()` function in main
to just return attenuation. You should get something like:
-
-
- ![Earth-mapped sphere](../images/img.earth-sphere.jpg)
-
-
+ ![Earth-mapped sphere](../images/img.earth-sphere.jpg class=pixel)
@@ -2084,11 +2021,7 @@
We get:
-
-
- ![Scene with rectangle light source](../images/img.rect-light.jpg)
-
-
+ ![Scene with rectangle light source](../images/img.rect-light.jpg class=pixel)
@@ -2097,11 +2030,7 @@
Fool around with making some spheres lights too.
-
-
- ![Scene with rectangle and sphere light sources](../images/img.rect-sph-light.jpg)
-
-
+ ![Scene with rectangle and sphere light sources](../images/img.rect-sph-light.jpg class=pixel)
@@ -2252,11 +2181,7 @@
We get:
-
-
- ![Empty Cornell box](../images/img.cornell-first.jpg)
-
-
+ ![Empty Cornell box](../images/img.cornell-first.jpg class=pixel)
@@ -2330,11 +2255,7 @@
And voila:
-
-
- ![Empty Cornell box with fixed walls](../images/img.cornell-empty.jpg)
-
-
+ ![Empty Cornell box with fixed walls](../images/img.cornell-empty.jpg class=pixel)
@@ -2402,11 +2323,7 @@
This gives:
-
-
- ![Cornell box with two blocks](../images/img.cornell-blocks.jpg)
-
-
+ ![Cornell box with two blocks](../images/img.cornell-blocks.jpg class=pixel)
@@ -2564,8 +2481,8 @@
vec3 tester(newx, y, newz);
for (int c = 0; c < 3; c++) {
- min[c] = ffmin(min[c], tester[c]);
- max[c] = ffmax(max[c], tester[c]);
+ min[c] = fmin(min[c], tester[c]);
+ max[c] = fmax(max[c], tester[c]);
}
}
}
@@ -2634,11 +2551,7 @@
Which yields:
-
-
- ![Standard Cornell box scene](../images/img.cornell-box.jpg)
-
-
+ ![Standard Cornell box scene](../images/img.cornell-box.jpg class=pixel)
@@ -2832,11 +2745,7 @@
We get:
-
-
- ![Cornell box with blocks of smoke](../images/img.cornell-smoke.jpg)
-
-
+ ![Cornell box with blocks of smoke](../images/img.cornell-smoke.jpg class=pixel)
@@ -2924,12 +2833,8 @@
Running it with 10,000 rays per pixel yields:
-
-
![Final scene](../images/img.book2-final.jpg)
-
-
Now go off and make a really cool image of your own! See https://in1weekend.blogspot.com/ for
diff --git a/books/RayTracingTheRestOfYourLife.html b/books/RayTracingTheRestOfYourLife.html
index 25f3f0f6..98c99d68 100644
--- a/books/RayTracingTheRestOfYourLife.html
+++ b/books/RayTracingTheRestOfYourLife.html
@@ -798,11 +798,7 @@
At 500×500 my code produces this image in 10min on 1 core of my Macbook:
-
-
- ![Cornell box, refactored](../images/img.cornell-refactor1.jpg)
-
-
+ ![Cornell box, refactored](../images/img.cornell-refactor1.jpg class=pixel)
Reducing that noise is our goal. We’ll do that by constructing a PDF that sends more rays to the
light.
@@ -945,11 +941,7 @@
And again I _should_ get the same picture except with different variance, but I don’t!
-
-
- ![Cornell box, with different sampling strategy](../images/img.cornell-refactor2.jpg)
-
-
+ ![Cornell box, with different sampling strategy](../images/img.cornell-refactor2.jpg class=pixel)
@@ -1317,11 +1309,8 @@
Which produces:
-
-
- ![Cornell box, with orthonormal basis scatter function](../images/img.cornell-ortho.jpg)
-
-
+ ![Cornell box, with orthonormal basis scatter function
+ ](../images/img.cornell-ortho.jpg class=pixel)
Is that right? We still don’t know for sure. Tracking down bugs is hard in the absence of reliable
reference solutions. Let’s table that for now and get rid of some of that noise.
@@ -1426,11 +1415,8 @@
With 10 samples per pixel this yields:
-
-
- ![Cornell box, sampling only the light, 10 samples per pixel](../images/img.cornell-samplight.jpg)
-
-
+ ![Cornell box, sampling only the light, 10 samples per pixel
+ ](../images/img.cornell-samplight.jpg class=pixel)
@@ -1461,11 +1447,8 @@
We also need to flip the light so its normals point in the -y direction. This gives us:
-
-
- ![Cornell box, light emitted only in the downward direction](../images/img.cornell-lightdown.jpg)
-
-
+ ![Cornell box, light emitted only in the downward direction
+ ](../images/img.cornell-lightdown.jpg class=pixel)
@@ -1614,11 +1597,7 @@
This yields an apparently matching result so all we’ve done so far is refactor where `pdf` is
computed:
-
-
- ![Cornell box with a cosine density _pdf_](../images/img.cornell-cospdf.jpg)
-
-
+ ![Cornell box with a cosine density _pdf_](../images/img.cornell-cospdf.jpg class=pixel)
@@ -1746,11 +1725,8 @@
At 10 samples per pixel we get:
-
-
- ![Cornell box, sampling a hittable light, 10 samples per pixel](../images/img.cornell-samphit.jpg)
-
-
+ ![Cornell box, sampling a hittable light, 10 samples per pixel
+ ](../images/img.cornell-samphit.jpg class=pixel)
@@ -1841,11 +1817,8 @@
1000 samples per pixel yields:
-
-
- ![Cornell box, mixture density of cosine and light sampling](../images/img.cornell-coslight.jpg)
-
-
+ ![Cornell box, mixture density of cosine and light sampling
+ ](../images/img.cornell-coslight.jpg class=pixel)
@@ -2176,11 +2149,7 @@
The resulting image has a noisy reflection on the ceiling because the directions toward the box are
not sampled with more density.
-
-
- ![Cornell box with arbitrary PDF functions](../images/img.cornell-flexpdf.jpg)
-
-
+ ![Cornell box with arbitrary PDF functions](../images/img.cornell-flexpdf.jpg class=pixel)
@@ -2325,11 +2294,8 @@
sampling the light did for my code. This is probably because those rays that hit the glass are
expensive!
-
-
- ![Cornell box with glass sphere, using new PDF functions](../images/img.cornell-glass.jpg)
-
-
+ ![Cornell box with glass sphere, using new PDF functions
+ ](../images/img.cornell-glass.jpg class=pixel)
@@ -2375,11 +2341,8 @@
And we get a decent image with 1000 samples as before:
-
-
- ![Cornell box, using a mixture of glass & light PDFs](../images/img.cornell-glasslight.jpg)
-
-
+ ![Cornell box, using a mixture of glass & light PDFs
+ ](../images/img.cornell-glasslight.jpg class=pixel)
@@ -2404,24 +2367,28 @@
So big decision: sweep this bug under the rug and check for `NaN`s, or just kill `NaN`s and hope
this doesn't come back to bite us later. I will always opt for the lazy strategy, especially when I
know floating point is hard. First, how do we check for a `NaN`? The one thing I always remember
-for `NaN`s is that a `NaN` does not equal itself. Using this trick, we update the
-`vec3::write_color()` function to replace any NaN components with zero:
+for `NaN`s is that a `NaN` does not equal itself. Using this trick, we update the `write_color()`
+function to replace any NaN components with zero:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
- void write_color(std::ostream &out, int samples_per_pixel) {
+ void write_color(std::ostream &out, color pixel_color, int samples_per_pixel) {
+ auto r = pixel_color.x();
+ auto g = pixel_color.y();
+ auto b = pixel_color.z();
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
- // Replace NaN component values with zero.
- if (e[0] != e[0]) e[0] = 0.0;
- if (e[1] != e[1]) e[1] = 0.0;
- if (e[2] != e[2]) e[2] = 0.0;
+ // Replace NaN components with zero. See explanation in Ray Tracing: The Rest of Your Life.
+ if (r != r) r = 0.0;
+ if (g != g) g = 0.0;
+ if (b != b) b = 0.0;
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
- // Divide the color total by the number of samples and gamma-correct
- // for a gamma value of 2.0.
+ // Divide the color by the number of samples and gamma-correct for gamma=2.0.
auto scale = 1.0 / samples_per_pixel;
- auto r = sqrt(scale * e[0]);
- auto g = sqrt(scale * e[1]);
- auto b = sqrt(scale * e[2]);
+ r = sqrt(scale * r);
+ g = sqrt(scale * g);
+ b = sqrt(scale * b);
// Write the translated [0,255] value of each color component.
out << static_cast(256 * clamp(r, 0.0, 0.999)) << ' '
@@ -2429,17 +2396,13 @@
<< static_cast(256 * clamp(b, 0.0, 0.999)) << '\n';
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- [Listing [write-color-nan]: [vec3.h] NaN-tolerant write_color function]
+ [Listing [write-color-nan]: [color.h] NaN-tolerant write_color function]
Happily, the black specks are gone:
-
-
- ![Cornell box with anti-acne color function](../images/img.book3-final.jpg)
-
-
+ ![Cornell box with anti-acne color function](../images/img.book3-final.jpg class=pixel)
diff --git a/src/InOneWeekend/main.cc b/src/InOneWeekend/main.cc
index 8b9db1b7..86f271b1 100644
--- a/src/InOneWeekend/main.cc
+++ b/src/InOneWeekend/main.cc
@@ -12,6 +12,7 @@
#include "rtweekend.h"
#include "camera.h"
+#include "color.h"
#include "hittable_list.h"
#include "material.h"
#include "sphere.h"
@@ -118,7 +119,7 @@ int main() {
ray r = cam.get_ray(u, v);
pixel_color += ray_color(r, world, max_depth);
}
- pixel_color.write_color(std::cout, samples_per_pixel);
+ write_color(std::cout, pixel_color, samples_per_pixel);
}
}
diff --git a/src/InOneWeekend/material.h b/src/InOneWeekend/material.h
index 3a2ba8b2..9b1c4ef6 100644
--- a/src/InOneWeekend/material.h
+++ b/src/InOneWeekend/material.h
@@ -43,7 +43,7 @@ class dielectric : public material {
double etai_over_etat = (rec.front_face) ? (1.0 / ref_idx) : (ref_idx);
vec3 unit_direction = unit_vector(r_in.direction());
- double cos_theta = ffmin(dot(-unit_direction, rec.normal), 1.0);
+ 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);
diff --git a/src/TheNextWeek/hittable.h b/src/TheNextWeek/hittable.h
index 06a838d9..5cffe4ae 100644
--- a/src/TheNextWeek/hittable.h
+++ b/src/TheNextWeek/hittable.h
@@ -149,8 +149,8 @@ rotate_y::rotate_y(shared_ptr p, double angle) : ptr(p) {
vec3 tester(newx, y, newz);
for (int c = 0; c < 3; c++) {
- min[c] = ffmin(min[c], tester[c]);
- max[c] = ffmax(max[c], tester[c]);
+ min[c] = fmin(min[c], tester[c]);
+ max[c] = fmax(max[c], tester[c]);
}
}
}
diff --git a/src/TheNextWeek/main.cc b/src/TheNextWeek/main.cc
index d5da711c..f371c572 100644
--- a/src/TheNextWeek/main.cc
+++ b/src/TheNextWeek/main.cc
@@ -14,6 +14,7 @@
#include "box.h"
#include "bvh.h"
#include "camera.h"
+#include "color.h"
#include "constant_medium.h"
#include "hittable_list.h"
#include "material.h"
@@ -447,7 +448,7 @@ int main() {
ray r = cam.get_ray(u, v);
pixel_color += ray_color(r, background, world, max_depth);
}
- pixel_color.write_color(std::cout, samples_per_pixel);
+ write_color(std::cout, pixel_color, samples_per_pixel);
}
}
diff --git a/src/TheNextWeek/material.h b/src/TheNextWeek/material.h
index 730dd1d8..3470c759 100644
--- a/src/TheNextWeek/material.h
+++ b/src/TheNextWeek/material.h
@@ -47,7 +47,7 @@ class dielectric : public material {
double etai_over_etat = (rec.front_face) ? (1.0 / ref_idx) : (ref_idx);
vec3 unit_direction = unit_vector(r_in.direction());
- double cos_theta = ffmin(dot(-unit_direction, rec.normal), 1.0);
+ 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);
diff --git a/src/TheRestOfYourLife/hittable.h b/src/TheRestOfYourLife/hittable.h
index ee68c009..aeec5f82 100644
--- a/src/TheRestOfYourLife/hittable.h
+++ b/src/TheRestOfYourLife/hittable.h
@@ -157,8 +157,8 @@ rotate_y::rotate_y(shared_ptr p, double angle) : ptr(p) {
vec3 tester(newx, y, newz);
for (int c = 0; c < 3; c++) {
- min[c] = ffmin(min[c], tester[c]);
- max[c] = ffmax(max[c], tester[c]);
+ min[c] = fmin(min[c], tester[c]);
+ max[c] = fmax(max[c], tester[c]);
}
}
}
diff --git a/src/TheRestOfYourLife/main.cc b/src/TheRestOfYourLife/main.cc
index 7a120ea0..34e675fe 100644
--- a/src/TheRestOfYourLife/main.cc
+++ b/src/TheRestOfYourLife/main.cc
@@ -14,6 +14,7 @@
#include "aarect.h"
#include "box.h"
#include "camera.h"
+#include "color.h"
#include "hittable_list.h"
#include "material.h"
#include "sphere.h"
@@ -127,7 +128,7 @@ int main() {
ray r = cam.get_ray(u, v);
pixel_color += ray_color(r, background, world, lights, max_depth);
}
- pixel_color.write_color(std::cout, samples_per_pixel);
+ write_color(std::cout, pixel_color, samples_per_pixel);
}
}
diff --git a/src/TheRestOfYourLife/material.h b/src/TheRestOfYourLife/material.h
index ca1d01ea..42a077f9 100644
--- a/src/TheRestOfYourLife/material.h
+++ b/src/TheRestOfYourLife/material.h
@@ -68,7 +68,7 @@ class dielectric : public material {
double etai_over_etat = (rec.front_face) ? (1.0 / ref_idx) : (ref_idx);
vec3 unit_direction = unit_vector(r_in.direction());
- double cos_theta = ffmin(dot(-unit_direction, rec.normal), 1.0);
+ 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);
diff --git a/src/common/aabb.h b/src/common/aabb.h
index b81fbc16..d126668f 100644
--- a/src/common/aabb.h
+++ b/src/common/aabb.h
@@ -24,12 +24,12 @@ class aabb {
bool hit(const ray& r, double tmin, double tmax) const {
for (int a = 0; a < 3; a++) {
- auto t0 = ffmin((_min[a] - r.origin()[a]) / r.direction()[a],
- (_max[a] - r.origin()[a]) / r.direction()[a]);
- auto t1 = ffmax((_min[a] - r.origin()[a]) / r.direction()[a],
- (_max[a] - r.origin()[a]) / r.direction()[a]);
- tmin = ffmax(t0, tmin);
- tmax = ffmin(t1, tmax);
+ auto t0 = fmin((_min[a] - r.origin()[a]) / r.direction()[a],
+ (_max[a] - r.origin()[a]) / r.direction()[a]);
+ auto t1 = fmax((_min[a] - r.origin()[a]) / r.direction()[a],
+ (_max[a] - r.origin()[a]) / r.direction()[a]);
+ tmin = fmax(t0, tmin);
+ tmax = fmin(t1, tmax);
if (tmax <= tmin)
return false;
}
@@ -61,13 +61,13 @@ class aabb {
};
aabb surrounding_box(aabb box0, aabb box1) {
- vec3 small(ffmin(box0.min().x(), box1.min().x()),
- ffmin(box0.min().y(), box1.min().y()),
- ffmin(box0.min().z(), box1.min().z()));
+ vec3 small(fmin(box0.min().x(), box1.min().x()),
+ fmin(box0.min().y(), box1.min().y()),
+ fmin(box0.min().z(), box1.min().z()));
- vec3 big (ffmax(box0.max().x(), box1.max().x()),
- ffmax(box0.max().y(), box1.max().y()),
- ffmax(box0.max().z(), box1.max().z()));
+ vec3 big (fmax(box0.max().x(), box1.max().x()),
+ fmax(box0.max().y(), box1.max().y()),
+ fmax(box0.max().z(), box1.max().z()));
return aabb(small,big);
}
diff --git a/src/common/color.h b/src/common/color.h
new file mode 100644
index 00000000..41a75f46
--- /dev/null
+++ b/src/common/color.h
@@ -0,0 +1,42 @@
+#ifndef COLOR_H
+#define COLOR_H
+//==============================================================================================
+// Originally written in 2020 by Peter Shirley
+//
+// To the extent possible under law, the author(s) have dedicated all copyright and related and
+// neighboring rights to this software to the public domain worldwide. This software is
+// distributed without any warranty.
+//
+// You should have received a copy (see file COPYING.txt) of the CC0 Public Domain Dedication
+// along with this software. If not, see .
+//==============================================================================================
+
+#include "vec3.h"
+
+#include
+
+
+void write_color(std::ostream &out, color pixel_color, int samples_per_pixel) {
+ auto r = pixel_color.x();
+ auto g = pixel_color.y();
+ auto b = pixel_color.z();
+
+ // Replace NaN components with zero. See explanation in Ray Tracing: The Rest of Your Life.
+ if (r != r) r = 0.0;
+ if (g != g) g = 0.0;
+ if (b != b) b = 0.0;
+
+ // Divide the color by the number of samples and gamma-correct for gamma=2.0.
+ auto scale = 1.0 / samples_per_pixel;
+ r = sqrt(scale * r);
+ g = sqrt(scale * g);
+ b = sqrt(scale * b);
+
+ // Write the translated [0,255] value of each color component.
+ out << static_cast(256 * clamp(r, 0.0, 0.999)) << ' '
+ << static_cast(256 * clamp(g, 0.0, 0.999)) << ' '
+ << static_cast(256 * clamp(b, 0.0, 0.999)) << '\n';
+}
+
+
+#endif
diff --git a/src/common/rtweekend.h b/src/common/rtweekend.h
index 6cda44c8..0bd5a2f7 100644
--- a/src/common/rtweekend.h
+++ b/src/common/rtweekend.h
@@ -19,6 +19,7 @@
using std::shared_ptr;
using std::make_shared;
+using std::sqrt;
// Constants
@@ -31,9 +32,6 @@ inline double degrees_to_radians(double degrees) {
return degrees * pi / 180.0;
}
-inline double ffmin(double a, double b) { return a <= b ? a : b; }
-inline double ffmax(double a, double b) { return a >= b ? a : b; }
-
inline double clamp(double x, double min, double max) {
if (x < min) return min;
if (x > max) return max;
diff --git a/src/common/vec3.h b/src/common/vec3.h
index 8a770aa5..689a8424 100644
--- a/src/common/vec3.h
+++ b/src/common/vec3.h
@@ -11,8 +11,10 @@
// along with this software. If not, see .
//==============================================================================================
+#include
#include
+using std::sqrt;
class vec3 {
public:
@@ -173,7 +175,7 @@ vec3 reflect(const vec3& v, const vec3& n) {
}
vec3 refract(const vec3& uv, const vec3& n, double etai_over_etat) {
- auto cos_theta = ffmin(dot(-uv, n), 1.0);
+ auto cos_theta = fmin(dot(-uv, n), 1.0);
vec3 r_out_parallel = etai_over_etat * (uv + cos_theta*n);
vec3 r_out_perp = -sqrt(1.0 - r_out_parallel.length_squared()) * n;
return r_out_parallel + r_out_perp;
diff --git a/style/book.css b/style/book.css
index 80da808d..f6996431 100644
--- a/style/book.css
+++ b/style/book.css
@@ -152,10 +152,6 @@ div.indented {
width: 72ex;
}
-.md div.render img {
- image-rendering: pixelated;
-}
-
.md div.image {
margin-bottom: 1em;
}