Skip to content

Commit

Permalink
Merge pull request #510 from RayTracing/write-color
Browse files Browse the repository at this point in the history
Move vec3::write_color to standalone helper
  • Loading branch information
hollasch authored May 3, 2020
2 parents de29d24 + 094f74b commit 5f76429
Show file tree
Hide file tree
Showing 7 changed files with 123 additions and 51 deletions.
13 changes: 4 additions & 9 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Change Log -- Ray Tracing in One Weekend
====================================================================================================

----------------------------------------------------------------------------------------------------

# v3.1.0 (in progress)

### Common
Expand All @@ -10,6 +10,7 @@ Change Log -- Ray Tracing in One Weekend
- New: Add explanation for padding `aarect` in the zero dimension (#488)
- Change: Minor change to use new `point3` and `color` type aliases for `vec3` (#422)
- Change: Renamed `constant_texture` to `solid_color`, add RGB constructor (#452)
- Change: Moved `vec3::write_color()` method to utility function in `color.h` header (#502)
- Change: Math notation to bold uppercase points, bold lowercase no-barb vectors (#412)
- Change: Switch from `ffmin`/`ffmax` to standard `fmin`/`fmax` (#444, #491)

Expand All @@ -21,6 +22,8 @@ Change Log -- Ray Tracing in One Weekend
- Fix: Improve image size and aspect ratio calculation to make size changes easier
- Fix: Added `t` parameter back into `hit_record` at correct place
- Fix: image basic vectors off by one
- Fix: Correct typo in "What's next?" list to rejoin split paragraph on "Lights." Adjust numbering
in rest of list.
- Change: First image size changed to 256x256
- Change: Default image sizes changed from 200x100 to 384x216
- Change: Define image aspect ratio up front, then image height from that and the image width
Expand All @@ -29,14 +32,6 @@ Change Log -- Ray Tracing in One Weekend
- Change: Large rewrite of the `image_texture` class. Now handles image loading too. (#434)


---------------------------------------------------------------------------------------------------
# v3.0.3 (in progress)

### _In One Weekend_
- Fix: Correct typo in "What's next?" list to rejoin split paragraph on "Lights." Adjust numbering
in rest of list.


----------------------------------------------------------------------------------------------------
# v3.0.2 (2020-04-11)

Expand Down
80 changes: 54 additions & 26 deletions books/RayTracingInOneWeekend.html
Original file line number Diff line number Diff line change
Expand Up @@ -274,13 +274,6 @@
return e[0]*e[0] + e[1]*e[1] + e[2]*e[2];
}

void write_color(std::ostream &out) {
// Write the translated [0,255] value of each color component.
out << static_cast<int>(255.999 * e[0]) << ' '
<< static_cast<int>(255.999 * e[1]) << ' '
<< static_cast<int>(255.999 * e[2]) << '\n';
}

public:
double e[3];
};
Expand Down Expand Up @@ -351,10 +344,38 @@
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[Listing [vec3-utility]: <kbd>[vec3.h]</kbd> vec3 utility functions]


Color Utility Functions
------------------------
Using our new `vec3` class, we'll create a utility function to write a single pixel's color out to
the standard output stream.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
#ifndef COLOR_H
#define COLOR_H

#include "vec3.h"

#include <iostream>

void write_color(std::ostream &out, color pixel_color) {
// Write the translated [0,255] value of each color component.
out << static_cast<int>(255.999 * pixel_color.x()) << ' '
<< static_cast<int>(255.999 * pixel_color.y()) << ' '
<< static_cast<int>(255.999 * pixel_color.z()) << '\n';
}

#endif
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[Listing [color]: <kbd>[color.h]</kbd> color utility functions]



<div class='together'>
Now we can change our main to use this:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
#include "color.h"
#include "vec3.h"

#include <iostream>
Expand All @@ -370,7 +391,7 @@
for (int i = 0; i < image_width; ++i) {
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
color pixel_color(double(i)/(image_width-1), double(j)/(image_height-1), 0.25);
pixel_color.write_color(std::cout);
write_color(std::cout, pixel_color);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
}
}
Expand Down Expand Up @@ -491,7 +512,7 @@
ray r(origin, lower_left_corner + u*horizontal + v*vertical);
color pixel_color = ray_color(r);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
pixel_color.write_color(std::cout);
write_color(std::cout, pixel_color);
}
}

Expand Down Expand Up @@ -1204,7 +1225,7 @@
color pixel_color = ray_color(r, world);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++

pixel_color.write_color(std::cout);
write_color(std::cout, pixel_color);
}
}

Expand Down Expand Up @@ -1328,7 +1349,7 @@
[Listing [camera-initial]: <kbd>[camera.h]</kbd> The camera class]
</div>

To handle the multi-sampled color computation, we update the `vec3::write_color()` function. Rather
To handle the multi-sampled color computation, we'll update the `write_color()` function. Rather
than adding in a fractional contribution each time we accumulate more light to the color, just add
the full color each iteration, and then perform a single divide at the end (by the number of
samples) when writing out the color. In addition, we'll add a handy utility function to the
Expand All @@ -1344,20 +1365,24 @@
[Listing [clamp]: <kbd>[rtweekend.h]</kbd> The clamp() utility function]

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 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();

// Divide the color total by the number of samples.
auto scale = 1.0 / samples_per_pixel;
auto r = scale * e[0];
auto g = scale * e[1];
auto b = scale * e[2];
r *= scale;
g *= scale;
b *= scale;

// Write the translated [0,255] value of each color component.
out << static_cast<int>(256 * clamp(r, 0.0, 0.999)) << ' '
<< static_cast<int>(256 * clamp(g, 0.0, 0.999)) << ' '
<< static_cast<int>(256 * clamp(b, 0.0, 0.999)) << '\n';
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[Listing [write-color-clamped]: <kbd>[vec3.h]</kbd> The write_color() function]
[Listing [write-color-clamped]: <kbd>[color.h]</kbd> The multi-sample write_color() function]

<div class='together'>
Main is also changed:
Expand Down Expand Up @@ -1391,7 +1416,7 @@
ray r = cam.get_ray(u, v);
pixel_color += ray_color(r, world);
}
pixel_color.write_color(std::cout, samples_per_pixel);
write_color(std::cout, pixel_color, samples_per_pixel);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
}
}
Expand Down Expand Up @@ -1568,7 +1593,7 @@
pixel_color += ray_color(r, world, max_depth);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
}
pixel_color.write_color(std::cout, samples_per_pixel);
write_color(std::cout, pixel_color, samples_per_pixel);
}
}

Expand Down Expand Up @@ -1599,14 +1624,17 @@
square-root:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 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
// Divide the color total by the number of samples and gamma-correct
// for a gamma value of 2.0.
// Divide the color total 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);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++

// Write the translated [0,255] value of each color component.
Expand All @@ -1615,7 +1643,7 @@
<< static_cast<int>(256 * clamp(b, 0.0, 0.999)) << '\n';
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[Listing [write-color-gamma]: <kbd>[vec3.h]</kbd> write_color(), with gamma correction]
[Listing [write-color-gamma]: <kbd>[color.h]</kbd> write_color(), with gamma correction]

</div>

Expand Down Expand Up @@ -2092,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);
}
}

Expand Down
30 changes: 17 additions & 13 deletions books/RayTracingTheRestOfYourLife.html
Original file line number Diff line number Diff line change
Expand Up @@ -2367,32 +2367,36 @@
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<int>(256 * clamp(r, 0.0, 0.999)) << ' '
<< static_cast<int>(256 * clamp(g, 0.0, 0.999)) << ' '
<< static_cast<int>(256 * clamp(b, 0.0, 0.999)) << '\n';
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[Listing [write-color-nan]: <kbd>[vec3.h]</kbd> NaN-tolerant write_color function]
[Listing [write-color-nan]: <kbd>[color.h]</kbd> NaN-tolerant write_color function]
</div>

<div class='together'>
Expand Down
3 changes: 2 additions & 1 deletion src/InOneWeekend/main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "rtweekend.h"

#include "camera.h"
#include "color.h"
#include "hittable_list.h"
#include "material.h"
#include "sphere.h"
Expand Down Expand Up @@ -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);
}
}

Expand Down
3 changes: 2 additions & 1 deletion src/TheNextWeek/main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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);
}
}

Expand Down
3 changes: 2 additions & 1 deletion src/TheRestOfYourLife/main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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);
}
}

Expand Down
42 changes: 42 additions & 0 deletions src/common/color.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#ifndef COLOR_H
#define COLOR_H
//==============================================================================================
// Originally written in 2020 by Peter Shirley <[email protected]>
//
// 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 <http://creativecommons.org/publicdomain/zero/1.0/>.
//==============================================================================================

#include "vec3.h"

#include <iostream>


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<int>(256 * clamp(r, 0.0, 0.999)) << ' '
<< static_cast<int>(256 * clamp(g, 0.0, 0.999)) << ' '
<< static_cast<int>(256 * clamp(b, 0.0, 0.999)) << '\n';
}


#endif

0 comments on commit 5f76429

Please sign in to comment.