├── .gitignore
├── CMakeLists.txt
├── CMakeSettings.json
├── LICENSE
├── README.md
├── earthmap.jpg
├── external
└── stb_image.h
├── result.png
└── src
├── aabb.hpp
├── box.hpp
├── bvh.hpp
├── camera.hpp
├── checker_texture.hpp
├── common.hpp
├── constant_medium.hpp
├── cosine_pdf.hpp
├── dielectric.hpp
├── diffuse_light.hpp
├── flip_face.hpp
├── hittable.hpp
├── hittable_list.hpp
├── hittable_pdf.hpp
├── image_texture.hpp
├── isotropic.hpp
├── lambertian.hpp
├── main.cpp
├── material.hpp
├── metal.hpp
├── mixture_pdf.hpp
├── moving_sphere.hpp
├── noise_texture.hpp
├── onb.hpp
├── pdf.hpp
├── perlin.hpp
├── ray.hpp
├── rotate_y.hpp
├── solid_color.hpp
├── sphere.hpp
├── texture.hpp
├── translate.hpp
├── vec3.hpp
├── xy_rect.hpp
├── xz_rect.hpp
└── yz_rect.hpp
/.gitignore:
--------------------------------------------------------------------------------
1 | # CMake temp/cache files
2 | CMakeCache.txt
3 | CMakeFiles
4 | CMakeScripts
5 | Testing
6 | Makefile
7 | cmake_install.cmake
8 | install_manifest.txt
9 | compile_commands.json
10 | CTestTestfile.cmake
11 | build
12 |
13 | # Visual Studio files
14 | /.vs
15 |
16 | # Visual Studio Code files
17 | /.vscode
18 | *.code-workspace
19 |
20 | # OSX files
21 | .DS_Store
22 |
23 | # CLion files
24 | /.idea
25 | cmake-build-debug
26 |
27 | # vi files
28 | .clang_complete
29 | .syntastic_cpp_config
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | # CMake version
2 | cmake_minimum_required(VERSION 3.8.2 FATAL_ERROR)
3 |
4 | # Declare project
5 | project(ray-tracing-the-rest-of-your-life)
6 |
7 | # Set output directories
8 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
9 |
10 | # Set enable output of compile commands during generation
11 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
12 |
13 | # Build type - Release by default
14 | if(NOT CMAKE_BUILD_TYPE)
15 | set(CMAKE_BUILD_TYPE Release)
16 | endif()
17 |
18 | # Set C++ standard to C++17
19 | set(CMAKE_CXX_STANDARD 17)
20 | set(CMAKE_CXX_STANDARD_REQUIRED ON)
21 | set(CMAKE_CXX_EXTENSIONS OFF)
22 |
23 | # Overrides
24 | set(CMAKE_MACOSX_RPATH ON)
25 |
26 | # Executables
27 | add_executable(ray-tracing-the-rest-of-your-life src/main.cpp)
--------------------------------------------------------------------------------
/CMakeSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "configurations": [
3 | {
4 | "name": "x86-Debug",
5 | "generator": "Ninja",
6 | "configurationType": "Debug",
7 | "buildRoot": "${workspaceRoot}\\build\\${name}",
8 | "installRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\install\\${name}",
9 | "cmakeCommandArgs": "",
10 | "buildCommandArgs": "-v",
11 | "ctestCommandArgs": "",
12 | "inheritEnvironments": [ "msvc_x86" ],
13 | "variables": []
14 | },
15 | {
16 | "name": "x86-Release",
17 | "generator": "Ninja",
18 | "configurationType": "RelWithDebInfo",
19 | "buildRoot": "${workspaceRoot}\\build\\${name}",
20 | "installRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\install\\${name}",
21 | "cmakeCommandArgs": "",
22 | "buildCommandArgs": "-v",
23 | "ctestCommandArgs": "",
24 | "inheritEnvironments": [ "msvc_x86" ],
25 | "variables": []
26 | },
27 | {
28 | "name": "x64-Debug",
29 | "generator": "Ninja",
30 | "configurationType": "Debug",
31 | "buildRoot": "${workspaceRoot}\\build\\${name}",
32 | "installRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\install\\${name}",
33 | "cmakeCommandArgs": "",
34 | "buildCommandArgs": "-v",
35 | "ctestCommandArgs": "",
36 | "inheritEnvironments": [ "msvc_x64_x64" ],
37 | "variables": []
38 | },
39 | {
40 | "name": "x64-Release",
41 | "generator": "Ninja",
42 | "configurationType": "RelWithDebInfo",
43 | "buildRoot": "${workspaceRoot}\\build\\${name}",
44 | "installRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\install\\${name}",
45 | "cmakeCommandArgs": "",
46 | "buildCommandArgs": "-v",
47 | "ctestCommandArgs": "",
48 | "inheritEnvironments": [ "msvc_x64_x64" ],
49 | "variables": []
50 | }
51 | ]
52 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Chris Ohk
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ray-tracing-the-rest-of-your-life-cpp
2 |
3 |
4 |
5 | This repository is C++ implementation of Peter Shirley's [Ray Tracing: The Rest of Your Life](https://raytracing.github.io/books/RayTracingTheRestOfYourLife.html). The code is built on C++17 and can be compiled with commonly available compilers such as g++, clang++, or Microsoft Visual Studio. It currently supports macOS (10.14 or later), Ubuntu (18.04 or later), Windows (Visual Studio 2017 or later), and Windows Subsystem for Linux (WSL). Other untested platforms that support C++17 also should be able to build it.
6 |
7 | ## Related Repositories
8 |
9 | * [ray-tracing-in-one-weekend-cpp](https://github.com/utilForever/ray-tracing-in-one-weekend-cpp)
10 | * [ray-tracing-the-next-week-cpp](https://github.com/utilForever/ray-tracing-the-next-week-cpp)
11 |
12 | ## Acknowledgement
13 |
14 | I would like to thank [Peter Shirley](https://twitter.com/peter_shirley) for providing the excellent original book.
15 |
16 | ## Contact
17 |
18 | You can contact me via e-mail (utilForever at gmail.com). I am always happy to answer questions or help with any issues you might have, and please be sure to share any additional work or your creations with me, I love seeing what other people are making.
19 |
20 | ## License
21 |
22 |
23 |
24 | The class is licensed under the [MIT License](http://opensource.org/licenses/MIT):
25 |
26 | Copyright © 2020 [Chris Ohk](http://www.github.com/utilForever).
27 |
28 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
29 |
30 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
31 |
32 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/earthmap.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/utilForever/ray-tracing-the-rest-of-your-life-cpp/cb756e2e81c4605e2ee9db78d0eb7660c567c228/earthmap.jpg
--------------------------------------------------------------------------------
/result.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/utilForever/ray-tracing-the-rest-of-your-life-cpp/cb756e2e81c4605e2ee9db78d0eb7660c567c228/result.png
--------------------------------------------------------------------------------
/src/aabb.hpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Chris Ohk
2 |
3 | // I am making my contributions/submissions to this project solely in my
4 | // personal capacity and are not conveying any rights to any intellectual
5 | // property of any third parties.
6 |
7 | // It is based on Ray Tracing: The Rest of Your Life book.
8 | // References: https://raytracing.github.io
9 |
10 | #ifndef RAY_TRACING_AABB_HPP
11 | #define RAY_TRACING_AABB_HPP
12 |
13 | #include "common.hpp"
14 |
15 | class aabb
16 | {
17 | public:
18 | aabb() = default;
19 | aabb(const point3& a, const point3& b) : _min(a), _max(b)
20 | {
21 | // Do nothing
22 | }
23 |
24 | point3 min() const
25 | {
26 | return _min;
27 | }
28 |
29 | point3 max() const
30 | {
31 | return _max;
32 | }
33 |
34 | bool hit(const ray& r, double tmin, double tmax) const;
35 |
36 | point3 _min;
37 | point3 _max;
38 | };
39 |
40 | inline bool aabb::hit(const ray& r, double tmin, double tmax) const
41 | {
42 | for (int a = 0; a < 3; ++a)
43 | {
44 | const auto invD = 1.0f / r.direction()[a];
45 | auto t0 = (min()[a] - r.origin()[a]) * invD;
46 | auto t1 = (max()[a] - r.origin()[a]) * invD;
47 |
48 | if (invD < 0.0f)
49 | {
50 | std::swap(t0, t1);
51 | }
52 |
53 | tmin = t0 > tmin ? t0 : tmin;
54 | tmax = t1 < tmax ? t1 : tmax;
55 |
56 | if (tmax <= tmin)
57 | {
58 | return false;
59 | }
60 | }
61 |
62 | return true;
63 | }
64 |
65 | inline aabb surrounding_box(aabb box0, aabb box1)
66 | {
67 | const point3 small(fmin(box0.min().x(), box1.min().x()),
68 | fmin(box0.min().y(), box1.min().y()),
69 | fmin(box0.min().z(), box1.min().z()));
70 | const point3 big(fmax(box0.max().x(), box1.max().x()),
71 | fmax(box0.max().y(), box1.max().y()),
72 | fmax(box0.max().z(), box1.max().z()));
73 |
74 | return aabb(small, big);
75 | }
76 |
77 | #endif
--------------------------------------------------------------------------------
/src/box.hpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Chris Ohk
2 |
3 | // I am making my contributions/submissions to this project solely in my
4 | // personal capacity and are not conveying any rights to any intellectual
5 | // property of any third parties.
6 |
7 | // It is based on Ray Tracing: The Rest of Your Life book.
8 | // References: https://raytracing.github.io
9 |
10 | #ifndef RAY_TRACING_BOX_HPP
11 | #define RAY_TRACING_BOX_HPP
12 |
13 | #include "flip_face.hpp"
14 | #include "hittable.hpp"
15 | #include "hittable_list.hpp"
16 | #include "xy_rect.hpp"
17 | #include "xz_rect.hpp"
18 | #include "yz_rect.hpp"
19 |
20 | class box final : public hittable
21 | {
22 | public:
23 | box() = default;
24 | box(const point3& p0, const point3& p1,
25 | const std::shared_ptr& ptr);
26 |
27 | bool hit(const ray& r, double t0, double t1,
28 | hit_record& rec) const override;
29 | bool bounding_box([[maybe_unused]] double t0, [[maybe_unused]] double t1,
30 | aabb& output_box) const override
31 | {
32 | output_box = aabb(box_min, box_max);
33 | return true;
34 | }
35 |
36 | point3 box_min;
37 | point3 box_max;
38 | hittable_list sides;
39 | };
40 |
41 | inline box::box(const point3& p0, const point3& p1,
42 | const std::shared_ptr& ptr)
43 | : box_min(p0), box_max(p1)
44 | {
45 | sides.add(
46 | std::make_shared(p0.x(), p1.x(), p0.y(), p1.y(), p1.z(), ptr));
47 | sides.add(std::make_shared(std::make_shared(
48 | p0.x(), p1.x(), p0.y(), p1.y(), p0.z(), ptr)));
49 |
50 | sides.add(
51 | std::make_shared(p0.x(), p1.x(), p0.z(), p1.z(), p1.y(), ptr));
52 | sides.add(std::make_shared(std::make_shared(
53 | p0.x(), p1.x(), p0.z(), p1.z(), p0.y(), ptr)));
54 |
55 | sides.add(
56 | std::make_shared(p0.y(), p1.y(), p0.z(), p1.z(), p1.x(), ptr));
57 | sides.add(std::make_shared(std::make_shared(
58 | p0.y(), p1.y(), p0.z(), p1.z(), p0.x(), ptr)));
59 | }
60 |
61 | inline bool box::hit(const ray& r, double t0, double t1, hit_record& rec) const
62 | {
63 | return sides.hit(r, t0, t1, rec);
64 | }
65 |
66 | #endif
--------------------------------------------------------------------------------
/src/bvh.hpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Chris Ohk
2 |
3 | // I am making my contributions/submissions to this project solely in my
4 | // personal capacity and are not conveying any rights to any intellectual
5 | // property of any third parties.
6 |
7 | // It is based on Ray Tracing: The Rest of Your Life book.
8 | // References: https://raytracing.github.io
9 |
10 | #ifndef RAY_TRACING_BVH_HPP
11 | #define RAY_TRACING_BVH_HPP
12 |
13 | #include "aabb.hpp"
14 | #include "hittable.hpp"
15 | #include "hittable_list.hpp"
16 |
17 | #include
18 |
19 | class bvh_node final : public hittable
20 | {
21 | public:
22 | bvh_node() = default;
23 | bvh_node(hittable_list& list, double time0, double time1)
24 | : bvh_node(list.objects, 0, list.objects.size(), time0, time1)
25 | {
26 | // Do nothing
27 | }
28 | bvh_node(std::vector>& objects, std::size_t start,
29 | std::size_t end, double time0, double time1);
30 |
31 | bool hit(const ray& r, double t_min, double t_max,
32 | hit_record& rec) const override;
33 | bool bounding_box(double t0, double t1, aabb& output_box) const override;
34 |
35 | std::shared_ptr left;
36 | std::shared_ptr right;
37 | aabb box;
38 | };
39 |
40 | inline bool box_compare(const std::shared_ptr a,
41 | const std::shared_ptr b, int axis)
42 | {
43 | aabb box_a;
44 | aabb box_b;
45 |
46 | if (!a->bounding_box(0, 0, box_a) || !b->bounding_box(0, 0, box_b))
47 | {
48 | std::cerr << "No bounding box in bvh_node constructor.\n";
49 | }
50 |
51 | return box_a.min().e[axis] < box_b.min().e[axis];
52 | }
53 |
54 | inline bool box_x_compare(const std::shared_ptr a,
55 | const std::shared_ptr b)
56 | {
57 | return box_compare(a, b, 0);
58 | }
59 |
60 | inline bool box_y_compare(const std::shared_ptr a,
61 | const std::shared_ptr b)
62 | {
63 | return box_compare(a, b, 1);
64 | }
65 |
66 | inline bool box_z_compare(const std::shared_ptr a,
67 | const std::shared_ptr b)
68 | {
69 | return box_compare(a, b, 2);
70 | }
71 |
72 | inline bvh_node::bvh_node(std::vector>& objects,
73 | std::size_t start, std::size_t end, double time0,
74 | double time1)
75 | {
76 | const int axis = random_int(0, 2);
77 | const auto comparator = (axis == 0)
78 | ? box_x_compare
79 | : (axis == 1) ? box_y_compare : box_z_compare;
80 | const size_t object_span = end - start;
81 |
82 | if (object_span == 1)
83 | {
84 | left = right = objects[start];
85 | }
86 | else if (object_span == 2)
87 | {
88 | if (comparator(objects[start], objects[start + 1]))
89 | {
90 | left = objects[start];
91 | right = objects[start + 1];
92 | }
93 | else
94 | {
95 | left = objects[start + 1];
96 | right = objects[start];
97 | }
98 | }
99 | else
100 | {
101 | std::sort(objects.begin() + start, objects.begin() + end, comparator);
102 |
103 | auto mid = start + object_span / 2;
104 | left = std::make_shared(objects, start, mid, time0, time1);
105 | right = std::make_shared(objects, mid, end, time0, time1);
106 | }
107 |
108 | aabb box_left, box_right;
109 |
110 | if (!left->bounding_box(time0, time1, box_left) ||
111 | !right->bounding_box(time0, time1, box_right))
112 | {
113 | std::cerr << "No bounding box in bvh_node constructor.\n";
114 | }
115 |
116 | box = surrounding_box(box_left, box_right);
117 | }
118 |
119 | inline bool bvh_node::hit(const ray& r, double t_min, double t_max,
120 | hit_record& rec) const
121 | {
122 | if (!box.hit(r, t_min, t_max))
123 | {
124 | return false;
125 | }
126 |
127 | const bool hit_left = left->hit(r, t_min, t_max, rec);
128 | const bool hit_right = right->hit(r, t_min, hit_left ? rec.t : t_max, rec);
129 |
130 | return hit_left || hit_right;
131 | }
132 |
133 | inline bool bvh_node::bounding_box([[maybe_unused]] double t0,
134 | [[maybe_unused]] double t1,
135 | aabb& output_box) const
136 | {
137 | output_box = box;
138 |
139 | return true;
140 | }
141 |
142 | #endif
--------------------------------------------------------------------------------
/src/camera.hpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Chris Ohk
2 |
3 | // I am making my contributions/submissions to this project solely in my
4 | // personal capacity and are not conveying any rights to any intellectual
5 | // property of any third parties.
6 |
7 | // It is based on Ray Tracing: The Rest of Your Life book.
8 | // References: https://raytracing.github.io
9 |
10 | #ifndef RAY_TRACING_CAMERA_HPP
11 | #define RAY_TRACING_CAMERA_HPP
12 |
13 | #include "common.hpp"
14 |
15 | class camera
16 | {
17 | public:
18 | camera() = default;
19 | // vfov: top to bottom, in degrees
20 | camera(vec3 lookfrom, vec3 lookat, vec3 vup, double vfov, double aspect,
21 | double aperture, double focus_dist, double t0 = 0, double t1 = 0)
22 | {
23 | origin = lookfrom;
24 | lens_radius = aperture / 2;
25 | time0 = t0;
26 | time1 = t1;
27 |
28 | const auto theta = degrees_to_radians(vfov);
29 | const auto half_height = tan(theta / 2);
30 | const auto half_width = aspect * half_height;
31 |
32 | w = unit_vector(lookfrom - lookat);
33 | u = unit_vector(cross(vup, w));
34 | v = cross(w, u);
35 | lower_left_corner = origin - half_width * focus_dist * u -
36 | half_height * focus_dist * v - focus_dist * w;
37 |
38 | horizontal = 2 * half_width * focus_dist * u;
39 | vertical = 2 * half_height * focus_dist * v;
40 | }
41 |
42 | ray get_ray(double s, double t) const
43 | {
44 | const vec3 rd = lens_radius * random_in_unit_disk();
45 | const vec3 offset = u * rd.x() + v * rd.y();
46 |
47 | return ray(
48 | origin + offset,
49 | lower_left_corner + s * horizontal + t * vertical - origin - offset,
50 | random_double(time0, time1));
51 | }
52 |
53 | vec3 origin;
54 | vec3 lower_left_corner;
55 | vec3 horizontal;
56 | vec3 vertical;
57 | vec3 u, v, w;
58 | double lens_radius;
59 | // shutter open/close times
60 | double time0, time1;
61 | };
62 |
63 | #endif
--------------------------------------------------------------------------------
/src/checker_texture.hpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Chris Ohk
2 |
3 | // I am making my contributions/submissions to this project solely in my
4 | // personal capacity and are not conveying any rights to any intellectual
5 | // property of any third parties.
6 |
7 | // It is based on Ray Tracing: The Rest of Your Life book.
8 | // References: https://raytracing.github.io
9 |
10 | #ifndef RAY_TRACING_CHECKER_TEXTURE_HPP
11 | #define RAY_TRACING_CHECKER_TEXTURE_HPP
12 |
13 | #include "texture.hpp"
14 |
15 | #include
16 |
17 | class checker_texture final : public texture
18 | {
19 | public:
20 | checker_texture() = default;
21 | checker_texture(std::shared_ptr t0, std::shared_ptr t1)
22 | : odd(std::move(t1)), even(std::move(t0))
23 | {
24 | // Do nothing
25 | }
26 |
27 | color value(double u, double v, const point3& p) const override
28 | {
29 | const auto sines = sin(10 * p.x()) * sin(10 * p.y()) * sin(10 * p.z());
30 |
31 | if (sines < 0)
32 | {
33 | return odd->value(u, v, p);
34 | }
35 | else
36 | {
37 | return even->value(u, v, p);
38 | }
39 | }
40 |
41 | std::shared_ptr odd;
42 | std::shared_ptr even;
43 | };
44 |
45 | #endif
--------------------------------------------------------------------------------
/src/common.hpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Chris Ohk
2 |
3 | // I am making my contributions/submissions to this project solely in my
4 | // personal capacity and are not conveying any rights to any intellectual
5 | // property of any third parties.
6 |
7 | // It is based on Ray Tracing: The Rest of Your Life book.
8 | // References: https://raytracing.github.io
9 |
10 | #ifndef RAY_TRACING_COMMON_HPP
11 | #define RAY_TRACING_COMMON_HPP
12 |
13 | #include
14 | #include
15 | #include
16 |
17 | // Constants
18 | const double infinity = std::numeric_limits::infinity();
19 | const double pi = 3.1415926535897932385;
20 |
21 | // Utility functions
22 | inline double degrees_to_radians(double degrees)
23 | {
24 | return degrees * pi / 180;
25 | }
26 |
27 | inline double ffmin(double a, double b)
28 | {
29 | return a <= b ? a : b;
30 | }
31 |
32 | inline double ffmax(double a, double b)
33 | {
34 | return a >= b ? a : b;
35 | }
36 |
37 | inline double random_double()
38 | {
39 | // Returns a random real in [0, 1).
40 | static std::uniform_real_distribution distribution(0.0, 1.0);
41 | static std::mt19937 generator;
42 | static std::function rand_generator =
43 | std::bind(distribution, generator);
44 |
45 | return rand_generator();
46 | }
47 |
48 | inline double random_double(double min, double max)
49 | {
50 | // Returns a random real in [min, max).
51 | return min + (max - min) * random_double();
52 | }
53 |
54 | inline int random_int(int min, int max)
55 | {
56 | // Returns a random integer in [min,max].
57 | return static_cast(random_double(min, max + 1));
58 | }
59 |
60 | // Common headers
61 | #include "ray.hpp"
62 | #include "vec3.hpp"
63 |
64 | #endif
--------------------------------------------------------------------------------
/src/constant_medium.hpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Chris Ohk
2 |
3 | // I am making my contributions/submissions to this project solely in my
4 | // personal capacity and are not conveying any rights to any intellectual
5 | // property of any third parties.
6 |
7 | // It is based on Ray Tracing: The Rest of Your Life book.
8 | // References: https://raytracing.github.io
9 |
10 | #ifndef RAY_TRACING_CONSTANT_MEDIUM_HPP
11 | #define RAY_TRACING_CONSTANT_MEDIUM_HPP
12 |
13 | #include "hittable.hpp"
14 | #include "isotropic.hpp"
15 | #include "texture.hpp"
16 |
17 | #include
18 |
19 | class constant_medium final : public hittable
20 | {
21 | public:
22 | constant_medium(std::shared_ptr b, double d,
23 | const std::shared_ptr& a)
24 | : boundary(std::move(b)), neg_inv_density(-1 / d)
25 | {
26 | phase_function = std::make_shared(a);
27 | }
28 |
29 | bool hit(const ray& r, double t_min, double t_max,
30 | hit_record& rec) const override;
31 | bool bounding_box(double t0, double t1, aabb& output_box) const override
32 | {
33 | return boundary->bounding_box(t0, t1, output_box);
34 | }
35 |
36 | std::shared_ptr boundary;
37 | std::shared_ptr phase_function;
38 | double neg_inv_density;
39 | };
40 |
41 | inline bool constant_medium::hit(const ray& r, double t_min, double t_max,
42 | hit_record& rec) const
43 | {
44 | // Print occasional samples when debugging. To enable, set enableDebug true.
45 | const bool enableDebug = false;
46 | const bool debugging = enableDebug && random_double() < 0.00001;
47 |
48 | hit_record rec1, rec2;
49 |
50 | if (!boundary->hit(r, -infinity, infinity, rec1))
51 | {
52 | return false;
53 | }
54 |
55 | if (!boundary->hit(r, rec1.t + 0.0001, infinity, rec2))
56 | {
57 | return false;
58 | }
59 |
60 | if (debugging)
61 | {
62 | std::cerr << "\nt0 = " << rec1.t << ", t1 = " << rec2.t << '\n';
63 | }
64 |
65 | if (rec1.t < t_min)
66 | {
67 | rec1.t = t_min;
68 | }
69 | if (rec2.t > t_max)
70 | {
71 | rec2.t = t_max;
72 | }
73 |
74 | if (rec1.t >= rec2.t)
75 | {
76 | return false;
77 | }
78 |
79 | if (rec1.t < 0)
80 | {
81 | rec1.t = 0;
82 | }
83 |
84 | const auto ray_length = r.direction().length();
85 | const auto distance_inside_boundary = (rec2.t - rec1.t) * ray_length;
86 | const auto hit_distance = neg_inv_density * log(random_double());
87 |
88 | if (hit_distance > distance_inside_boundary)
89 | {
90 | return false;
91 | }
92 |
93 | rec.t = rec1.t + hit_distance / ray_length;
94 | rec.p = r.at(rec.t);
95 |
96 | if (debugging)
97 | {
98 | std::cerr << "hit_distance = " << hit_distance << '\n'
99 | << "rec.t = " << rec.t << '\n'
100 | << "rec.p = " << rec.p << '\n';
101 | }
102 |
103 | rec.normal = vec3(1, 0, 0); // arbitrary
104 | rec.front_face = true; // also arbitrary
105 | rec.mat_ptr = phase_function;
106 |
107 | return true;
108 | }
109 |
110 | #endif
--------------------------------------------------------------------------------
/src/cosine_pdf.hpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Chris Ohk
2 |
3 | // I am making my contributions/submissions to this project solely in my
4 | // personal capacity and are not conveying any rights to any intellectual
5 | // property of any third parties.
6 |
7 | // It is based on Ray Tracing: The Rest of Your Life book.
8 | // References: https://raytracing.github.io
9 |
10 | #ifndef RAY_TRACING_COSINE_PDF_HPP
11 | #define RAY_TRACING_COSINE_PDF_HPP
12 |
13 | #include "onb.hpp"
14 | #include "pdf.hpp"
15 |
16 | class cosine_pdf final : public pdf
17 | {
18 | public:
19 | cosine_pdf(const vec3& w)
20 | {
21 | uvw.build_from_w(w);
22 | }
23 |
24 | double value(const vec3& direction) const override
25 | {
26 | const auto cosine = dot(unit_vector(direction), uvw.w());
27 | return (cosine <= 0) ? 0 : cosine / pi;
28 | }
29 |
30 | vec3 generate() const override
31 | {
32 | return uvw.local(random_cosine_direction());
33 | }
34 |
35 | onb uvw;
36 | };
37 |
38 | #endif
--------------------------------------------------------------------------------
/src/dielectric.hpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Chris Ohk
2 |
3 | // I am making my contributions/submissions to this project solely in my
4 | // personal capacity and are not conveying any rights to any intellectual
5 | // property of any third parties.
6 |
7 | // It is based on Ray Tracing: The Rest of Your Life book.
8 | // References: https://raytracing.github.io
9 |
10 | #ifndef RAY_TRACING_DIELECTRIC_HPP
11 | #define RAY_TRACING_DIELECTRIC_HPP
12 |
13 | #include "hittable.hpp"
14 | #include "material.hpp"
15 |
16 | class dielectric final : public material
17 | {
18 | public:
19 | dielectric(double ri) : ref_idx(ri)
20 | {
21 | // Do nothing
22 | }
23 |
24 | bool scatter(const ray& r_in, const hit_record& rec,
25 | scatter_record& srec) const override
26 | {
27 | srec.is_specular = true;
28 | srec.pdf_ptr = nullptr;
29 | srec.attenuation = color{1.0, 1.0, 1.0};
30 |
31 | const vec3 unit_direction = unit_vector(r_in.direction());
32 | const double cos_theta = fmin(dot(-unit_direction, rec.normal), 1.0);
33 | const double sin_theta = sqrt(1.0 - cos_theta * cos_theta);
34 |
35 | const double etai_over_etat =
36 | (rec.front_face) ? (1.0 / ref_idx) : (ref_idx);
37 | if (etai_over_etat * sin_theta > 1.0)
38 | {
39 | const vec3 reflected = reflect(unit_direction, rec.normal);
40 | srec.specular_ray = ray{rec.p, reflected, r_in.time()};
41 |
42 | return true;
43 | }
44 |
45 | const double reflect_prob = schlick(cos_theta, etai_over_etat);
46 | if (random_double() < reflect_prob)
47 | {
48 | const vec3 reflected = reflect(unit_direction, rec.normal);
49 | srec.specular_ray = ray{rec.p, reflected, r_in.time()};
50 |
51 | return true;
52 | }
53 |
54 | const vec3 refracted = refract(unit_direction, rec.normal, etai_over_etat);
55 | srec.specular_ray = ray{rec.p, refracted, r_in.time()};
56 |
57 | return true;
58 | }
59 |
60 | double ref_idx;
61 | };
62 |
63 | #endif
--------------------------------------------------------------------------------
/src/diffuse_light.hpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Chris Ohk
2 |
3 | // I am making my contributions/submissions to this project solely in my
4 | // personal capacity and are not conveying any rights to any intellectual
5 | // property of any third parties.
6 |
7 | // It is based on Ray Tracing: The Rest of Your Life book.
8 | // References: https://raytracing.github.io
9 |
10 | #ifndef RAY_TRACING_DIFFUSE_LIGHT_HPP
11 | #define RAY_TRACING_DIFFUSE_LIGHT_HPP
12 |
13 | #include "material.hpp"
14 | #include "texture.hpp"
15 |
16 | #include
17 |
18 | class diffuse_light final : public material
19 | {
20 | public:
21 | diffuse_light(std::shared_ptr a) : emit(std::move(a))
22 | {
23 | // Do nothing
24 | }
25 |
26 | color emitted([[maybe_unused]] const ray& r_in, const hit_record& rec,
27 | double u, double v, const point3& p) const override
28 | {
29 | if (rec.front_face)
30 | {
31 | return emit->value(u, v, p);
32 | }
33 | else
34 | {
35 | return color{0, 0, 0};
36 | }
37 | }
38 |
39 | std::shared_ptr emit;
40 | };
41 |
42 | #endif
--------------------------------------------------------------------------------
/src/flip_face.hpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Chris Ohk
2 |
3 | // I am making my contributions/submissions to this project solely in my
4 | // personal capacity and are not conveying any rights to any intellectual
5 | // property of any third parties.
6 |
7 | // It is based on Ray Tracing: The Rest of Your Life book.
8 | // References: https://raytracing.github.io
9 |
10 | #ifndef RAY_TRACING_FLIP_FACE_HPP
11 | #define RAY_TRACING_FLIP_FACE_HPP
12 |
13 | #include "hittable.hpp"
14 |
15 | #include
16 |
17 | class flip_face final : public hittable
18 | {
19 | public:
20 | flip_face(std::shared_ptr p) : ptr(std::move(p))
21 | {
22 | // Do nothing
23 | }
24 |
25 | bool hit(const ray& r, double t_min, double t_max,
26 | hit_record& rec) const override
27 | {
28 | if (!ptr->hit(r, t_min, t_max, rec))
29 | {
30 | return false;
31 | }
32 |
33 | rec.front_face = !rec.front_face;
34 | return true;
35 | }
36 |
37 | bool bounding_box(double t0, double t1, aabb& output_box) const override
38 | {
39 | return ptr->bounding_box(t0, t1, output_box);
40 | }
41 |
42 | std::shared_ptr ptr;
43 | };
44 |
45 | #endif
--------------------------------------------------------------------------------
/src/hittable.hpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Chris Ohk
2 |
3 | // I am making my contributions/submissions to this project solely in my
4 | // personal capacity and are not conveying any rights to any intellectual
5 | // property of any third parties.
6 |
7 | // It is based on Ray Tracing: The Rest of Your Life book.
8 | // References: https://raytracing.github.io
9 |
10 | #ifndef RAY_TRACING_HITTABLE_HPP
11 | #define RAY_TRACING_HITTABLE_HPP
12 |
13 | #include "aabb.hpp"
14 | #include "ray.hpp"
15 |
16 | class material;
17 |
18 | struct hit_record
19 | {
20 | vec3 p;
21 | vec3 normal;
22 | std::shared_ptr mat_ptr;
23 | double t{0.0};
24 | double u{0.0};
25 | double v{0.0};
26 | bool front_face{false};
27 |
28 | void set_face_normal(const ray& r, const vec3& outward_normal)
29 | {
30 | // dot(r.direction(), outward_normal) > 0.0 => ray is inside the sphere
31 | // dot(r.direction(), outward_normal) <= 0.0 => ray is outside the sphere
32 | front_face = dot(r.direction(), outward_normal) < 0;
33 | normal = front_face ? outward_normal : -outward_normal;
34 | }
35 | };
36 |
37 | class hittable
38 | {
39 | public:
40 | virtual ~hittable() = default;
41 |
42 | virtual bool hit(const ray& r, double t_min, double t_max,
43 | hit_record& rec) const = 0;
44 | virtual bool bounding_box(double t0, double t1, aabb& output_box) const = 0;
45 |
46 | virtual double pdf_value([[maybe_unused]] const point3& o,
47 | [[maybe_unused]] const vec3& v) const
48 | {
49 | return 0.0;
50 | }
51 |
52 | virtual vec3 random([[maybe_unused]] const vec3& p) const
53 | {
54 | return vec3{1, 0, 0};
55 | }
56 | };
57 |
58 | #endif
--------------------------------------------------------------------------------
/src/hittable_list.hpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Chris Ohk
2 |
3 | // I am making my contributions/submissions to this project solely in my
4 | // personal capacity and are not conveying any rights to any intellectual
5 | // property of any third parties.
6 |
7 | // It is based on Ray Tracing: The Rest of Your Life book.
8 | // References: https://raytracing.github.io
9 |
10 | #ifndef RAY_TRACING_HITTABLE_LIST_HPP
11 | #define RAY_TRACING_HITTABLE_LIST_HPP
12 |
13 | #include "hittable.hpp"
14 |
15 | #include
16 | #include
17 | #include
18 |
19 | class hittable_list final : public hittable
20 | {
21 | public:
22 | hittable_list() = default;
23 | hittable_list(std::shared_ptr object)
24 | {
25 | add(std::move(object));
26 | }
27 |
28 | void clear()
29 | {
30 | objects.clear();
31 | }
32 |
33 | void add(std::shared_ptr&& object)
34 | {
35 | objects.emplace_back(object);
36 | }
37 |
38 | bool hit(const ray& r, double t_min, double t_max,
39 | hit_record& rec) const override;
40 | bool bounding_box(double t0, double t1, aabb& output_box) const override;
41 |
42 | double pdf_value(const point3& o, const vec3& v) const override;
43 |
44 | vec3 random(const vec3& p) const override;
45 |
46 | std::vector> objects;
47 | };
48 |
49 | inline bool hittable_list::hit(const ray& r, double t_min, double t_max,
50 | hit_record& rec) const
51 | {
52 | hit_record temp_rec;
53 | bool hit_anything = false;
54 | auto closest_so_far = t_max;
55 |
56 | for (const auto& object : objects)
57 | {
58 | if (object->hit(r, t_min, closest_so_far, temp_rec))
59 | {
60 | hit_anything = true;
61 | closest_so_far = temp_rec.t;
62 | rec = temp_rec;
63 | }
64 | }
65 |
66 | return hit_anything;
67 | }
68 |
69 | inline bool hittable_list::bounding_box(double t0, double t1,
70 | aabb& output_box) const
71 | {
72 | if (objects.empty())
73 | {
74 | return false;
75 | }
76 |
77 | const aabb temp_box;
78 | bool first_box = true;
79 |
80 | for (const auto& object : objects)
81 | {
82 | if (!object->bounding_box(t0, t1, output_box))
83 | {
84 | return false;
85 | }
86 |
87 | output_box =
88 | first_box ? temp_box : surrounding_box(output_box, temp_box);
89 | first_box = false;
90 | }
91 |
92 | return true;
93 | }
94 |
95 | inline double hittable_list::pdf_value(const point3& o, const vec3& v) const
96 | {
97 | const auto weight = 1.0 / objects.size();
98 | auto sum = 0.0;
99 |
100 | for (const auto& object : objects)
101 | {
102 | sum += weight * object->pdf_value(o, v);
103 | }
104 |
105 | return sum;
106 | }
107 |
108 | inline vec3 hittable_list::random(const vec3& o) const
109 | {
110 | const auto int_size = static_cast(objects.size());
111 | return objects[random_int(0, int_size - 1)]->random(o);
112 | }
113 |
114 | #endif
--------------------------------------------------------------------------------
/src/hittable_pdf.hpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Chris Ohk
2 |
3 | // I am making my contributions/submissions to this project solely in my
4 | // personal capacity and are not conveying any rights to any intellectual
5 | // property of any third parties.
6 |
7 | // It is based on Ray Tracing: The Rest of Your Life book.
8 | // References: https://raytracing.github.io
9 |
10 | #ifndef RAY_TRACING_HITTABLE_PDF_HPP
11 | #define RAY_TRACING_HITTABLE_PDF_HPP
12 |
13 | #include "hittable.hpp"
14 | #include "pdf.hpp"
15 |
16 | class hittable_pdf final : public pdf
17 | {
18 | public:
19 | hittable_pdf(std::shared_ptr p, const point3& origin)
20 | : o(origin), ptr(std::move(p))
21 | {
22 | // Do nothing
23 | }
24 |
25 | double value(const vec3& direction) const override
26 | {
27 | return ptr->pdf_value(o, direction);
28 | }
29 |
30 | vec3 generate() const override
31 | {
32 | return ptr->random(o);
33 | }
34 |
35 | point3 o;
36 | std::shared_ptr ptr;
37 | };
38 |
39 | #endif
--------------------------------------------------------------------------------
/src/image_texture.hpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Chris Ohk
2 |
3 | // I am making my contributions/submissions to this project solely in my
4 | // personal capacity and are not conveying any rights to any intellectual
5 | // property of any third parties.
6 |
7 | // It is based on Ray Tracing: The Rest of Your Life book.
8 | // References: https://raytracing.github.io
9 |
10 | #ifndef RAY_TRACING_IMAGE_TEXTURE_HPP
11 | #define RAY_TRACING_IMAGE_TEXTURE_HPP
12 |
13 | #include "texture.hpp"
14 |
15 | // Disable pedantic warnings for this external library.
16 | #ifdef _MSC_VER
17 | // Microsoft Visual C++ Compiler
18 | #pragma warning(push, 0)
19 | #endif
20 |
21 | #define STB_IMAGE_IMPLEMENTATION
22 | #include "../external/stb_image.h"
23 |
24 | // Restore warning levels.
25 | #ifdef _MSC_VER
26 | // Microsoft Visual C++ Compiler
27 | #pragma warning(pop)
28 | #endif
29 |
30 | #include
31 |
32 | class image_texture final : public texture
33 | {
34 | public:
35 | const static int bytes_per_pixel = 3;
36 |
37 | image_texture() = default;
38 | image_texture(const char* filename)
39 | {
40 | auto components_per_pixel = bytes_per_pixel;
41 |
42 | data = stbi_load(filename, &width, &height, &components_per_pixel,
43 | components_per_pixel);
44 |
45 | if (!data)
46 | {
47 | std::cerr << "ERROR: Could not load texture image file '"
48 | << filename << "'.\n";
49 | width = height = 0;
50 | }
51 |
52 | bytes_per_scanline = bytes_per_pixel * width;
53 | }
54 |
55 | ~image_texture()
56 | {
57 | delete data;
58 | }
59 |
60 | color value(double u, double v,
61 | [[maybe_unused]] const point3& p) const override
62 | {
63 | // If we have no texture data,
64 | // then return solid cyan as a debugging aid.
65 | if (data == nullptr)
66 | {
67 | return color(0, 1, 1);
68 | }
69 |
70 | // Clamp input texture coordinates to [0,1] x [1,0]
71 | u = std::clamp(u, 0.0, 1.0);
72 | // Flip V to image coordinates
73 | v = 1.0 - std::clamp(v, 0.0, 1.0);
74 |
75 | auto i = static_cast(u * width);
76 | auto j = static_cast(v * height);
77 |
78 | // Clamp integer mapping, since actual coordinates
79 | // should be less than 1.0
80 | if (i >= width)
81 | {
82 | i = width - 1;
83 | }
84 | if (j >= height)
85 | {
86 | j = height - 1;
87 | }
88 |
89 | const auto color_scale = 1.0 / 255.0;
90 | const auto pixel = data + j * bytes_per_scanline + i * bytes_per_pixel;
91 |
92 | return color(color_scale * pixel[0], color_scale * pixel[1],
93 | color_scale * pixel[2]);
94 | }
95 |
96 | private:
97 | unsigned char* data = nullptr;
98 | int width = 0, height = 0;
99 | int bytes_per_scanline = 0;
100 | };
101 |
102 | #endif
--------------------------------------------------------------------------------
/src/isotropic.hpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Chris Ohk
2 |
3 | // I am making my contributions/submissions to this project solely in my
4 | // personal capacity and are not conveying any rights to any intellectual
5 | // property of any third parties.
6 |
7 | // It is based on Ray Tracing: The Rest of Your Life book.
8 | // References: https://raytracing.github.io
9 |
10 | #ifndef RAY_TRACING_ISOTROPIC_HPP
11 | #define RAY_TRACING_ISOTROPIC_HPP
12 |
13 | #include "hittable.hpp"
14 | #include "material.hpp"
15 | #include "texture.hpp"
16 |
17 | #include
18 |
19 | class isotropic final : public material
20 | {
21 | public:
22 | isotropic(std::shared_ptr a) : albedo(std::move(a))
23 | {
24 | // Do nothing
25 | }
26 |
27 | bool scatter(const ray& r_in, const hit_record& rec,
28 | scatter_record& srec) const override
29 | {
30 | srec.is_specular = true;
31 | srec.specular_ray = ray{rec.p, random_in_unit_sphere(), r_in.time()};
32 | srec.attenuation = albedo->value(rec.u, rec.v, rec.p);
33 |
34 | return true;
35 | }
36 |
37 | std::shared_ptr albedo;
38 | };
39 |
40 | #endif
--------------------------------------------------------------------------------
/src/lambertian.hpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Chris Ohk
2 |
3 | // I am making my contributions/submissions to this project solely in my
4 | // personal capacity and are not conveying any rights to any intellectual
5 | // property of any third parties.
6 |
7 | // It is based on Ray Tracing: The Rest of Your Life book.
8 | // References: https://raytracing.github.io
9 |
10 | #ifndef RAY_TRACING_LAMBERTIAN_HPP
11 | #define RAY_TRACING_LAMBERTIAN_HPP
12 |
13 | #include "hittable.hpp"
14 | #include "material.hpp"
15 | #include "onb.hpp"
16 | #include "pdf.hpp"
17 | #include "texture.hpp"
18 |
19 | #include
20 |
21 | class lambertian final : public material
22 | {
23 | public:
24 | lambertian(std::shared_ptr a) : albedo(std::move(a))
25 | {
26 | // Do nothing
27 | }
28 |
29 | bool scatter([[maybe_unused]] const ray& r_in, const hit_record& rec,
30 | scatter_record& srec) const override
31 | {
32 | srec.is_specular = false;
33 | srec.attenuation = albedo->value(rec.u, rec.v, rec.p);
34 | srec.pdf_ptr = std::make_shared(rec.normal);
35 |
36 | return true;
37 | }
38 |
39 | double scattering_pdf([[maybe_unused]] const ray& r_in,
40 | const hit_record& rec,
41 | const ray& scattered) const override
42 | {
43 | const auto cosine = dot(rec.normal, unit_vector(scattered.direction()));
44 | return cosine < 0 ? 0 : cosine / pi;
45 | }
46 |
47 | std::shared_ptr albedo;
48 | };
49 |
50 | #endif
--------------------------------------------------------------------------------
/src/main.cpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Chris Ohk
2 |
3 | // I am making my contributions/submissions to this project solely in my
4 | // personal capacity and are not conveying any rights to any intellectual
5 | // property of any third parties.
6 |
7 | // It is based on Ray Tracing: The Rest of Your Life book.
8 | // References: https://raytracing.github.io
9 |
10 | #include "box.hpp"
11 | #include "bvh.hpp"
12 | #include "camera.hpp"
13 | #include "checker_texture.hpp"
14 | #include "common.hpp"
15 | #include "constant_medium.hpp"
16 | #include "cosine_pdf.hpp"
17 | #include "dielectric.hpp"
18 | #include "diffuse_light.hpp"
19 | #include "flip_face.hpp"
20 | #include "hittable_list.hpp"
21 | #include "hittable_pdf.hpp"
22 | #include "image_texture.hpp"
23 | #include "lambertian.hpp"
24 | #include "metal.hpp"
25 | #include "mixture_pdf.hpp"
26 | #include "moving_sphere.hpp"
27 | #include "noise_texture.hpp"
28 | #include "rotate_y.hpp"
29 | #include "solid_color.hpp"
30 | #include "sphere.hpp"
31 | #include "translate.hpp"
32 | #include "xy_rect.hpp"
33 | #include "xz_rect.hpp"
34 | #include "yz_rect.hpp"
35 |
36 | #include
37 |
38 | vec3 ray_color(const ray& r, const color& background, const hittable& world,
39 | const std::shared_ptr& lights, int depth)
40 | {
41 | hit_record rec;
42 |
43 | // If we've exceeded the ray bounce limit, no more light is gathered.
44 | if (depth <= 0)
45 | {
46 | return color{0, 0, 0};
47 | }
48 |
49 | // If the ray hits nothing, return the background color.
50 | if (!world.hit(r, 0.001, infinity, rec))
51 | {
52 | return background;
53 | }
54 |
55 | scatter_record srec;
56 | const color emitted = rec.mat_ptr->emitted(r, rec, rec.u, rec.v, rec.p);
57 | if (!rec.mat_ptr->scatter(r, rec, srec))
58 | {
59 | return emitted;
60 | }
61 |
62 | if (srec.is_specular)
63 | {
64 | return srec.attenuation * ray_color(srec.specular_ray, background,
65 | world, lights, depth - 1);
66 | }
67 |
68 | const auto light_ptr = std::make_shared(lights, rec.p);
69 | const mixture_pdf p{light_ptr, srec.pdf_ptr};
70 |
71 | const ray scattered = ray{rec.p, p.generate(), r.time()};
72 | const auto pdf_val = p.value(scattered.direction());
73 |
74 | return emitted +
75 | srec.attenuation * rec.mat_ptr->scattering_pdf(r, rec, scattered) *
76 | ray_color(scattered, background, world, lights, depth - 1) /
77 | pdf_val;
78 | }
79 |
80 | hittable_list random_scene()
81 | {
82 | hittable_list world;
83 |
84 | auto checker = std::make_shared(
85 | std::make_shared(0.2, 0.3, 0.1),
86 | std::make_shared(0.9, 0.9, 0.9));
87 | world.add(std::make_shared(point3{0, -1000, 0}, 1000,
88 | std::make_shared(checker)));
89 |
90 | for (int a = -10; a < 10; ++a)
91 | {
92 | for (int b = -10; b < 10; ++b)
93 | {
94 | const auto choose_mat = random_double();
95 | vec3 center(a + 0.9 * random_double(), 0.2,
96 | b + 0.9 * random_double());
97 |
98 | if ((center - vec3{4, .2, 0}).length() > 0.9)
99 | {
100 | if (choose_mat < 0.8)
101 | {
102 | // diffuse
103 | auto albedo = vec3::random() * vec3::random();
104 | world.add(std::make_shared(
105 | center, center + vec3{0, random_double(0, .5), 0}, 0.0,
106 | 1.0, 0.2,
107 | std::make_shared(
108 | std::make_shared(albedo))));
109 | }
110 | else if (choose_mat < 0.95)
111 | {
112 | // metal
113 | auto albedo = vec3::random(.5, 1);
114 | auto fuzz = random_double(0, .5);
115 | world.add(std::make_shared(
116 | center, 0.2, std::make_shared(albedo, fuzz)));
117 | }
118 | else
119 | {
120 | // glass
121 | world.add(std::make_shared(
122 | center, 0.2, std::make_shared(1.5)));
123 | }
124 | }
125 | }
126 | }
127 |
128 | world.add(std::make_shared(vec3{0, 1, 0}, 1.0,
129 | std::make_shared(1.5)));
130 | world.add(std::make_shared(
131 | vec3{-4, 1, 0}, 1.0,
132 | std::make_shared(
133 | std::make_shared(0.4, 0.2, 0.1))));
134 | world.add(std::make_shared(
135 | vec3{4, 1, 0}, 1.0, std::make_shared(vec3{0.7, 0.6, 0.5}, 0.0)));
136 |
137 | return world;
138 | }
139 |
140 | hittable_list two_spheres()
141 | {
142 | hittable_list objects;
143 |
144 | auto checker = std::make_shared(
145 | std::make_shared(0.2, 0.3, 0.1),
146 | std::make_shared(0.9, 0.9, 0.9));
147 |
148 | objects.add(std::make_shared(
149 | point3(0, -10, 0), 10, std::make_shared(checker)));
150 | objects.add(std::make_shared(
151 | point3(0, 10, 0), 10, std::make_shared(checker)));
152 |
153 | return objects;
154 | }
155 |
156 | hittable_list two_perlin_spheres()
157 | {
158 | hittable_list objects;
159 |
160 | const auto pertext = std::make_shared(4.0);
161 |
162 | objects.add(std::make_shared(
163 | point3(0, -1000, 0), 1000, std::make_shared(pertext)));
164 | objects.add(std::make_shared(
165 | point3(0, 2, 0), 2, std::make_shared(pertext)));
166 |
167 | return objects;
168 | }
169 |
170 | hittable_list earth()
171 | {
172 | auto earth_texture = std::make_shared("earthmap.jpg");
173 | auto earth_surface = std::make_shared(earth_texture);
174 | const auto globe =
175 | std::make_shared(point3(0, 0, 0), 2, earth_surface);
176 |
177 | return hittable_list(globe);
178 | }
179 |
180 | hittable_list simple_light()
181 | {
182 | hittable_list objects;
183 |
184 | auto pertext = std::make_shared(4);
185 | objects.add(std::make_shared(
186 | point3(0, -1000, 0), 1000, std::make_shared(pertext)));
187 | objects.add(std::make_shared(
188 | point3(0, 2, 0), 2, std::make_shared(pertext)));
189 |
190 | auto difflight =
191 | std::make_shared(std::make_shared(4, 4, 4));
192 | objects.add(std::make_shared(point3(0, 7, 0), 2, difflight));
193 | objects.add(std::make_shared(3, 5, 1, 3, -2, difflight));
194 |
195 | return objects;
196 | }
197 |
198 | hittable_list cornell_box(camera& cam, double aspect)
199 | {
200 | hittable_list world;
201 |
202 | auto red = std::make_shared(
203 | std::make_shared(.65, .05, .05));
204 | auto white = std::make_shared(
205 | std::make_shared(.73, .73, .73));
206 | auto green = std::make_shared(
207 | std::make_shared(.12, .45, .15));
208 | auto light = std::make_shared(
209 | std::make_shared(15, 15, 15));
210 |
211 | world.add(std::make_shared(
212 | std::make_shared(0, 555, 0, 555, 555, green)));
213 | world.add(std::make_shared(0, 555, 0, 555, 0, red));
214 | world.add(std::make_shared(
215 | std::make_shared(213, 343, 227, 332, 554, light)));
216 | world.add(std::make_shared(
217 | std::make_shared(0, 555, 0, 555, 555, white)));
218 | world.add(std::make_shared(0, 555, 0, 555, 0, white));
219 | world.add(std::make_shared(
220 | std::make_shared(0, 555, 0, 555, 555, white)));
221 |
222 | std::shared_ptr aluminum =
223 | std::make_shared(color(0.8, 0.85, 0.88), 0.0);
224 | std::shared_ptr box1 =
225 | std::make_shared(point3(0, 0, 0), point3(165, 330, 165), aluminum);
226 | box1 = std::make_shared(box1, 15);
227 | box1 = std::make_shared(box1, vec3(265, 0, 295));
228 | world.add(std::move(box1));
229 |
230 | std::shared_ptr glass = std::make_shared(1.5);
231 | world.add(std::make_shared(point3(190, 90, 190), 90, glass));
232 |
233 | const point3 lookfrom(278, 278, -800);
234 | const point3 lookat(278, 278, 0);
235 | const vec3 vup(0, 1, 0);
236 | const auto dist_to_focus = 10.0;
237 | const auto aperture = 0.0;
238 | const auto vfov = 40.0;
239 | const auto t0 = 0.0;
240 | const auto t1 = 1.0;
241 |
242 | cam = camera(lookfrom, lookat, vup, vfov, aspect, aperture, dist_to_focus,
243 | t0, t1);
244 |
245 | return world;
246 | }
247 |
248 | hittable_list cornell_smoke()
249 | {
250 | hittable_list objects;
251 |
252 | auto red = std::make_shared(
253 | std::make_shared(.65, .05, .05));
254 | auto white = std::make_shared(
255 | std::make_shared(.73, .73, .73));
256 | auto green = std::make_shared(
257 | std::make_shared(.12, .45, .15));
258 | auto light =
259 | std::make_shared(std::make_shared(7, 7, 7));
260 |
261 | objects.add(std::make_shared(
262 | std::make_shared(0, 555, 0, 555, 555, green)));
263 | objects.add(std::make_shared(0, 555, 0, 555, 0, red));
264 | objects.add(std::make_shared(113, 443, 127, 432, 554, light));
265 | objects.add(std::make_shared(
266 | std::make_shared(0, 555, 0, 555, 555, white)));
267 | objects.add(std::make_shared(0, 555, 0, 555, 0, white));
268 | objects.add(std::make_shared(
269 | std::make_shared(0, 555, 0, 555, 555, white)));
270 |
271 | std::shared_ptr box1 =
272 | std::make_shared(point3(0, 0, 0), point3(165, 330, 165), white);
273 | box1 = std::make_shared(box1, 15);
274 | box1 = std::make_shared(box1, vec3(265, 0, 295));
275 |
276 | std::shared_ptr box2 =
277 | std::make_shared(point3(0, 0, 0), point3(165, 165, 165), white);
278 | box2 = std::make_shared(box2, -18);
279 | box2 = std::make_shared(box2, vec3(130, 0, 65));
280 |
281 | objects.add(std::make_shared(
282 | box1, 0.01, std::make_shared(0, 0, 0)));
283 | objects.add(std::make_shared(
284 | box2, 0.01, std::make_shared(1, 1, 1)));
285 |
286 | return objects;
287 | }
288 |
289 | hittable_list final_scene()
290 | {
291 | hittable_list boxes1;
292 | auto ground = std::make_shared(
293 | std::make_shared(0.48, 0.83, 0.53));
294 |
295 | const int boxes_per_side = 20;
296 | for (int i = 0; i < boxes_per_side; i++)
297 | {
298 | for (int j = 0; j < boxes_per_side; j++)
299 | {
300 | const auto w = 100.0;
301 | const auto x0 = -1000.0 + i * w;
302 | const auto z0 = -1000.0 + j * w;
303 | const auto y0 = 0.0;
304 | const auto x1 = x0 + w;
305 | const auto y1 = random_double(1, 101);
306 | const auto z1 = z0 + w;
307 |
308 | boxes1.add(std::make_shared(point3(x0, y0, z0),
309 | point3(x1, y1, z1), ground));
310 | }
311 | }
312 |
313 | hittable_list objects;
314 |
315 | objects.add(std::make_shared(boxes1, 0, 1));
316 |
317 | auto light =
318 | std::make_shared(std::make_shared(7, 7, 7));
319 | objects.add(std::make_shared(123, 423, 147, 412, 554, light));
320 |
321 | auto center1 = point3(400, 400, 200);
322 | auto center2 = center1 + vec3(30, 0, 0);
323 | auto moving_sphere_material = std::make_shared(
324 | std::make_shared(0.7, 0.3, 0.1));
325 | objects.add(std::make_shared(center1, center2, 0, 1, 50,
326 | moving_sphere_material));
327 |
328 | objects.add(std::make_shared(point3(260, 150, 45), 50,
329 | std::make_shared(1.5)));
330 | objects.add(std::make_shared(
331 | point3(0, 150, 145), 50,
332 | std::make_shared(color(0.8, 0.8, 0.9), 10.0)));
333 |
334 | auto boundary = std::make_shared(point3(360, 150, 145), 70,
335 | std::make_shared(1.5));
336 | objects.add(boundary);
337 | objects.add(std::make_shared(
338 | boundary, 0.2, std::make_shared(0.2, 0.4, 0.9)));
339 | boundary = std::make_shared(point3(0, 0, 0), 5000,
340 | std::make_shared(1.5));
341 | objects.add(std::make_shared(
342 | boundary, .0001, std::make_shared(1, 1, 1)));
343 |
344 | auto emat = std::make_shared(
345 | std::make_shared("earthmap.jpg"));
346 | objects.add(std::make_shared(point3(400, 200, 400), 100, emat));
347 | auto pertext = std::make_shared(0.1);
348 | objects.add(std::make_shared(
349 | point3(220, 280, 300), 80, std::make_shared(pertext)));
350 |
351 | hittable_list boxes2;
352 | auto white = std::make_shared(
353 | std::make_shared(.73, .73, .73));
354 | const int ns = 1000;
355 | for (int j = 0; j < ns; j++)
356 | {
357 | boxes2.add(std::make_shared(point3::random(0, 165), 10, white));
358 | }
359 |
360 | objects.add(std::make_shared(
361 | std::make_shared(std::make_shared(boxes2, 0.0, 1.0),
362 | 15),
363 | vec3(-100, 270, 395)));
364 |
365 | return objects;
366 | }
367 |
368 | int main()
369 | {
370 | const int image_width = 600;
371 | const int image_height = 600;
372 | const int samples_per_pixel = 1000;
373 | const int max_depth = 50;
374 | const auto aspect_ratio = static_cast(image_width) / image_height;
375 |
376 | std::cout << "P3\n" << image_width << ' ' << image_height << "\n255\n";
377 |
378 | const color background{0, 0, 0};
379 |
380 | camera cam;
381 | const auto world = cornell_box(cam, aspect_ratio);
382 |
383 | const auto lights = std::make_shared();
384 | lights->add(std::make_shared(213, 343, 227, 332, 554,
385 | std::make_shared()));
386 | lights->add(std::make_shared(point3{190, 90, 190}, 90,
387 | std::make_shared()));
388 |
389 | for (int j = image_height - 1; j >= 0; --j)
390 | {
391 | std::cerr << "\rScanlines remaining: " << j << ' ' << std::flush;
392 |
393 | for (int i = 0; i < image_width; ++i)
394 | {
395 | color pixel_color;
396 |
397 | for (int s = 0; s < samples_per_pixel; ++s)
398 | {
399 | const auto u = (i + random_double()) / (image_width - 1);
400 | const auto v = (j + random_double()) / (image_height - 1);
401 | ray r = cam.get_ray(u, v);
402 | pixel_color +=
403 | ray_color(r, background, world, lights, max_depth);
404 | }
405 |
406 | pixel_color.write_color(std::cout, samples_per_pixel);
407 | }
408 | }
409 |
410 | std::cerr << "\nDone.\n";
411 |
412 | return 0;
413 | }
--------------------------------------------------------------------------------
/src/material.hpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Chris Ohk
2 |
3 | // I am making my contributions/submissions to this project solely in my
4 | // personal capacity and are not conveying any rights to any intellectual
5 | // property of any third parties.
6 |
7 | // It is based on Ray Tracing: The Rest of Your Life book.
8 | // References: https://raytracing.github.io
9 |
10 | #ifndef RAY_TRACING_MATERIAL_HPP
11 | #define RAY_TRACING_MATERIAL_HPP
12 |
13 | #include "common.hpp"
14 | #include "pdf.hpp"
15 |
16 | struct hit_record;
17 |
18 | struct scatter_record
19 | {
20 | ray specular_ray;
21 | bool is_specular;
22 | color attenuation;
23 | std::shared_ptr pdf_ptr;
24 | };
25 |
26 | class material
27 | {
28 | public:
29 | virtual ~material() = default;
30 |
31 | virtual bool scatter([[maybe_unused]] const ray& r_in,
32 | [[maybe_unused]] const hit_record& rec,
33 | [[maybe_unused]] scatter_record& srec) const
34 | {
35 | return false;
36 | }
37 |
38 | virtual double scattering_pdf([[maybe_unused]] const ray& r_in,
39 | [[maybe_unused]] const hit_record& rec,
40 | [[maybe_unused]] const ray& scattered) const
41 | {
42 | return 0.0;
43 | }
44 |
45 | virtual color emitted([[maybe_unused]] const ray& r_in,
46 | [[maybe_unused]] const hit_record& rec,
47 | [[maybe_unused]] double u, [[maybe_unused]] double v,
48 | [[maybe_unused]] const point3& p) const
49 | {
50 | return color{0, 0, 0};
51 | }
52 | };
53 |
54 | inline double schlick(double cosine, double ref_idx)
55 | {
56 | auto r0 = (1 - ref_idx) / (1 + ref_idx);
57 | r0 = r0 * r0;
58 |
59 | return r0 + (1 - r0) * pow((1 - cosine), 5);
60 | }
61 |
62 | #endif
--------------------------------------------------------------------------------
/src/metal.hpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Chris Ohk
2 |
3 | // I am making my contributions/submissions to this project solely in my
4 | // personal capacity and are not conveying any rights to any intellectual
5 | // property of any third parties.
6 |
7 | // It is based on Ray Tracing: The Rest of Your Life book.
8 | // References: https://raytracing.github.io
9 |
10 | #ifndef RAY_TRACING_METAL_HPP
11 | #define RAY_TRACING_METAL_HPP
12 |
13 | #include "hittable.hpp"
14 | #include "material.hpp"
15 |
16 | class metal final : public material
17 | {
18 | public:
19 | metal(const vec3& a, double f) : albedo(a), fuzz(f < 1 ? f : 1)
20 | {
21 | // Do nothing
22 | }
23 |
24 | bool scatter(const ray& r_in, const hit_record& rec,
25 | scatter_record& srec) const override
26 | {
27 | const vec3 reflected =
28 | reflect(unit_vector(r_in.direction()), rec.normal);
29 | srec.specular_ray =
30 | ray{rec.p, reflected + fuzz * random_in_unit_sphere()};
31 | srec.attenuation = albedo;
32 | srec.is_specular = true;
33 | srec.pdf_ptr = nullptr;
34 |
35 | return true;
36 | }
37 |
38 | vec3 albedo;
39 | double fuzz;
40 | };
41 |
42 | #endif
--------------------------------------------------------------------------------
/src/mixture_pdf.hpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Chris Ohk
2 |
3 | // I am making my contributions/submissions to this project solely in my
4 | // personal capacity and are not conveying any rights to any intellectual
5 | // property of any third parties.
6 |
7 | // It is based on Ray Tracing: The Rest of Your Life book.
8 | // References: https://raytracing.github.io
9 |
10 | #ifndef RAY_TRACING_MIXTURE_PDF_HPP
11 | #define RAY_TRACING_MIXTURE_PDF_HPP
12 |
13 | #include "pdf.hpp"
14 |
15 | class mixture_pdf final : public pdf
16 | {
17 | public:
18 | mixture_pdf(std::shared_ptr p0, std::shared_ptr p1)
19 | {
20 | p[0] = std::move(p0);
21 | p[1] = std::move(p1);
22 | }
23 |
24 | double value(const vec3& direction) const override
25 | {
26 | return 0.5 * p[0]->value(direction) + 0.5 * p[1]->value(direction);
27 | }
28 |
29 | vec3 generate() const override
30 | {
31 | if (random_double() < 0.5)
32 | {
33 | return p[0]->generate();
34 | }
35 | else
36 | {
37 | return p[1]->generate();
38 | }
39 | }
40 |
41 | std::shared_ptr p[2];
42 | };
43 |
44 | #endif
--------------------------------------------------------------------------------
/src/moving_sphere.hpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Chris Ohk
2 |
3 | // I am making my contributions/submissions to this project solely in my
4 | // personal capacity and are not conveying any rights to any intellectual
5 | // property of any third parties.
6 |
7 | // It is based on Ray Tracing: The Rest of Your Life book.
8 | // References: https://raytracing.github.io
9 |
10 | #ifndef RAY_TRACING_MOVING_SPHERE_HPP
11 | #define RAY_TRACING_MOVING_SPHERE_HPP
12 |
13 | #include "hittable.hpp"
14 | #include "vec3.hpp"
15 |
16 | #include
17 |
18 | class moving_sphere final : public hittable
19 | {
20 | public:
21 | moving_sphere() = default;
22 | moving_sphere(vec3 cen0, vec3 cen1, double t0, double t1, double r,
23 | std::shared_ptr m)
24 | : center0(cen0),
25 | center1(cen1),
26 | time0(t0),
27 | time1(t1),
28 | radius(r),
29 | mat_ptr(std::move(m))
30 | {
31 | // Do nothing
32 | }
33 |
34 | bool hit(const ray& r, double t_min, double t_max,
35 | hit_record& rec) const override;
36 | bool bounding_box(double t0, double t1, aabb& output_box) const override;
37 |
38 | vec3 center(double time) const;
39 |
40 | vec3 center0, center1;
41 | double time0{0.0}, time1{0.0};
42 | double radius{0.0};
43 | std::shared_ptr mat_ptr;
44 | };
45 |
46 | inline bool moving_sphere::hit(const ray& r, double t_min, double t_max,
47 | hit_record& rec) const
48 | {
49 | const vec3 oc = r.origin() - center(r.time());
50 | const auto a = r.direction().length_squared();
51 | const auto half_b = dot(oc, r.direction());
52 | const auto c = oc.length_squared() - radius * radius;
53 | const auto discriminant = half_b * half_b - a * c;
54 |
55 | if (discriminant > 0)
56 | {
57 | const auto root = sqrt(discriminant);
58 | auto temp = (-half_b - root) / a;
59 |
60 | if (temp < t_max && temp > t_min)
61 | {
62 | rec.t = temp;
63 | rec.p = r.at(rec.t);
64 |
65 | const vec3 outward_normal = (rec.p - center(r.time())) / radius;
66 | rec.set_face_normal(r, outward_normal);
67 | rec.mat_ptr = mat_ptr;
68 |
69 | return true;
70 | }
71 |
72 | temp = (-half_b + root) / a;
73 |
74 | if (temp < t_max && temp > t_min)
75 | {
76 | rec.t = temp;
77 | rec.p = r.at(rec.t);
78 |
79 | const vec3 outward_normal = (rec.p - center(r.time())) / radius;
80 | rec.set_face_normal(r, outward_normal);
81 | rec.mat_ptr = mat_ptr;
82 |
83 | return true;
84 | }
85 | }
86 |
87 | return false;
88 | }
89 |
90 | inline bool moving_sphere::bounding_box(double t0, double t1,
91 | aabb& output_box) const
92 | {
93 | const aabb box0(center(t0) - vec3(radius, radius, radius),
94 | center(t0) + vec3(radius, radius, radius));
95 | const aabb box1(center(t1) - vec3(radius, radius, radius),
96 | center(t1) + vec3(radius, radius, radius));
97 |
98 | output_box = surrounding_box(box0, box1);
99 |
100 | return true;
101 | }
102 |
103 | inline vec3 moving_sphere::center(double time) const
104 | {
105 | return center0 + ((time - time0) / (time1 - time0)) * (center1 - center0);
106 | }
107 |
108 | #endif
--------------------------------------------------------------------------------
/src/noise_texture.hpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Chris Ohk
2 |
3 | // I am making my contributions/submissions to this project solely in my
4 | // personal capacity and are not conveying any rights to any intellectual
5 | // property of any third parties.
6 |
7 | // It is based on Ray Tracing: The Rest of Your Life book.
8 | // References: https://raytracing.github.io
9 |
10 | #ifndef RAY_TRACING_NOISE_TEXTURE_HPP
11 | #define RAY_TRACING_NOISE_TEXTURE_HPP
12 |
13 | #include "perlin.hpp"
14 | #include "texture.hpp"
15 |
16 | class noise_texture final : public texture
17 | {
18 | public:
19 | noise_texture() = default;
20 | noise_texture(double sc) : scale(sc)
21 | {
22 | // Do nothing
23 | }
24 |
25 | color value([[maybe_unused]] double u, [[maybe_unused]] double v,
26 | const point3& p) const override
27 | {
28 | return color(1, 1, 1) * 0.5 *
29 | (1 + sin(scale * p.z() + 10 * noise.turb(p)));
30 | }
31 |
32 | perlin noise;
33 | double scale = 0.0;
34 | };
35 |
36 | #endif
--------------------------------------------------------------------------------
/src/onb.hpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Chris Ohk
2 |
3 | // I am making my contributions/submissions to this project solely in my
4 | // personal capacity and are not conveying any rights to any intellectual
5 | // property of any third parties.
6 |
7 | // It is based on Ray Tracing: The Rest of Your Life book.
8 | // References: https://raytracing.github.io
9 |
10 | #ifndef RAY_TRACING_ONB_HPP
11 | #define RAY_TRACING_ONB_HPP
12 |
13 | #include "vec3.hpp"
14 |
15 | class onb
16 | {
17 | public:
18 | vec3 operator[](int i) const
19 | {
20 | return axis[i];
21 | }
22 |
23 | vec3 u() const
24 | {
25 | return axis[0];
26 | }
27 | vec3 v() const
28 | {
29 | return axis[1];
30 | }
31 | vec3 w() const
32 | {
33 | return axis[2];
34 | }
35 |
36 | vec3 local(double a, double b, double c) const
37 | {
38 | return a * u() + b * v() + c * w();
39 | }
40 |
41 | vec3 local(const vec3& a) const
42 | {
43 | return a.x() * u() + a.y() * v() + a.z() * w();
44 | }
45 |
46 | void build_from_w(const vec3& n);
47 |
48 | vec3 axis[3];
49 | };
50 |
51 | inline void onb::build_from_w(const vec3& n)
52 | {
53 | axis[2] = unit_vector(n);
54 | const vec3 a = (fabs(w().x()) > 0.9) ? vec3{0, 1, 0} : vec3{1, 0, 0};
55 | axis[1] = unit_vector(cross(w(), a));
56 | axis[0] = cross(w(), v());
57 | }
58 |
59 | #endif
--------------------------------------------------------------------------------
/src/pdf.hpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Chris Ohk
2 |
3 | // I am making my contributions/submissions to this project solely in my
4 | // personal capacity and are not conveying any rights to any intellectual
5 | // property of any third parties.
6 |
7 | // It is based on Ray Tracing: The Rest of Your Life book.
8 | // References: https://raytracing.github.io
9 |
10 | #ifndef RAY_TRACING_PDF_HPP
11 | #define RAY_TRACING_PDF_HPP
12 |
13 | #include "common.hpp"
14 |
15 | inline vec3 random_cosine_direction()
16 | {
17 | const auto r1 = random_double();
18 | const auto r2 = random_double();
19 | const auto z = sqrt(1 - r2);
20 |
21 | const auto phi = 2 * pi * r1;
22 | const auto x = cos(phi) * sqrt(r2);
23 | const auto y = sin(phi) * sqrt(r2);
24 |
25 | return vec3{x, y, z};
26 | }
27 |
28 | class pdf
29 | {
30 | public:
31 | virtual ~pdf() = default;
32 |
33 | virtual double value(const vec3& direction) const = 0;
34 | virtual vec3 generate() const = 0;
35 | };
36 |
37 | #endif
--------------------------------------------------------------------------------
/src/perlin.hpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Chris Ohk
2 |
3 | // I am making my contributions/submissions to this project solely in my
4 | // personal capacity and are not conveying any rights to any intellectual
5 | // property of any third parties.
6 |
7 | // It is based on Ray Tracing: The Rest of Your Life book.
8 | // References: https://raytracing.github.io
9 |
10 | #ifndef RAY_TRACING_PERLIN_HPP
11 | #define RAY_TRACING_PERLIN_HPP
12 |
13 | #include "common.hpp"
14 |
15 | inline double trilinear_interp(double c[2][2][2], double u, double v, double w)
16 | {
17 | auto accum = 0.0;
18 |
19 | for (int i = 0; i < 2; ++i)
20 | {
21 | for (int j = 0; j < 2; ++j)
22 | {
23 | for (int k = 0; k < 2; ++k)
24 | {
25 | accum += (i * u + (1 - i) * (1 - u)) *
26 | (j * v + (1 - j) * (1 - v)) *
27 | (k * w + (1 - k) * (1 - w)) * c[i][j][k];
28 | }
29 | }
30 | }
31 |
32 | return accum;
33 | }
34 |
35 | class perlin
36 | {
37 | public:
38 | perlin()
39 | {
40 | ranvec = new vec3[point_count];
41 | for (int i = 0; i < point_count; ++i)
42 | {
43 | ranvec[i] = unit_vector(vec3::random(-1, 1));
44 | }
45 |
46 | perm_x = perlin_generate_perm();
47 | perm_y = perlin_generate_perm();
48 | perm_z = perlin_generate_perm();
49 | }
50 |
51 | ~perlin()
52 | {
53 | delete[] ranvec;
54 | delete[] perm_x;
55 | delete[] perm_y;
56 | delete[] perm_z;
57 | }
58 |
59 | double noise(const point3& p) const
60 | {
61 | auto u = p.x() - floor(p.x());
62 | auto v = p.y() - floor(p.y());
63 | auto w = p.z() - floor(p.z());
64 | const int i = static_cast(floor(p.x()));
65 | const int j = static_cast(floor(p.y()));
66 | const int k = static_cast(floor(p.z()));
67 | vec3 c[2][2][2];
68 |
69 | for (int di = 0; di < 2; ++di)
70 | {
71 | for (int dj = 0; dj < 2; ++dj)
72 | {
73 | for (int dk = 0; dk < 2; ++dk)
74 | {
75 | c[di][dj][dk] =
76 | ranvec[perm_x[(i + di) & 255] ^ perm_y[(j + dj) & 255] ^
77 | perm_z[(k + dk) & 255]];
78 | }
79 | }
80 | }
81 |
82 | return perlin_interp(c, u, v, w);
83 | }
84 |
85 | double turb(const point3& p, int depth = 7) const
86 | {
87 | auto accum = 0.0;
88 | auto temp_p = p;
89 | auto weight = 1.0;
90 |
91 | for (int i = 0; i < depth; ++i)
92 | {
93 | accum += weight * noise(temp_p);
94 | weight *= 0.5;
95 | temp_p *= 2;
96 | }
97 |
98 | return fabs(accum);
99 | }
100 |
101 | private:
102 | static int* perlin_generate_perm()
103 | {
104 | const auto p = new int[point_count];
105 |
106 | for (int i = 0; i < point_count; ++i)
107 | {
108 | p[i] = i;
109 | }
110 |
111 | permute(p, point_count);
112 |
113 | return p;
114 | }
115 |
116 | static void permute(int* p, int n)
117 | {
118 | for (int i = n - 1; i > 0; --i)
119 | {
120 | const int target = random_int(0, i);
121 |
122 | const int tmp = p[i];
123 | p[i] = p[target];
124 | p[target] = tmp;
125 | }
126 | }
127 |
128 | static double perlin_interp(vec3 c[2][2][2], double u, double v, double w)
129 | {
130 | const auto uu = u * u * (3 - 2 * u);
131 | const auto vv = v * v * (3 - 2 * v);
132 | const auto ww = w * w * (3 - 2 * w);
133 | auto accum = 0.0;
134 |
135 | for (int i = 0; i < 2; ++i)
136 | {
137 | for (int j = 0; j < 2; ++j)
138 | {
139 | for (int k = 0; k < 2; ++k)
140 | {
141 | vec3 weight_v(u - i, v - j, w - k);
142 | accum += (i * uu + (1 - i) * (1 - uu)) *
143 | (j * vv + (1 - j) * (1 - vv)) *
144 | (k * ww + (1 - k) * (1 - ww)) *
145 | dot(c[i][j][k], weight_v);
146 | }
147 | }
148 | }
149 |
150 | return accum;
151 | }
152 |
153 | static const int point_count = 256;
154 | vec3* ranvec;
155 | int* perm_x;
156 | int* perm_y;
157 | int* perm_z;
158 | };
159 |
160 | #endif
--------------------------------------------------------------------------------
/src/ray.hpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Chris Ohk
2 |
3 | // I am making my contributions/submissions to this project solely in my
4 | // personal capacity and are not conveying any rights to any intellectual
5 | // property of any third parties.
6 |
7 | // It is based on Ray Tracing: The Rest of Your Life book.
8 | // References: https://raytracing.github.io
9 |
10 | #ifndef RAY_TRACING_RAY_HPP
11 | #define RAY_TRACING_RAY_HPP
12 |
13 | #include "vec3.hpp"
14 |
15 | class ray
16 | {
17 | public:
18 | ray() = default;
19 |
20 | ray(const vec3& origin, const vec3& direction, double time = 0.0)
21 | : orig(origin), dir(direction), tm(time)
22 | {
23 | // Do nothing
24 | }
25 |
26 | vec3 origin() const
27 | {
28 | return orig;
29 | }
30 |
31 | vec3 direction() const
32 | {
33 | return dir;
34 | }
35 |
36 | double time() const
37 | {
38 | return tm;
39 | }
40 |
41 | vec3 at(double t) const
42 | {
43 | return orig + t * dir;
44 | }
45 |
46 | vec3 orig;
47 | vec3 dir;
48 | double tm;
49 | };
50 |
51 | #endif
--------------------------------------------------------------------------------
/src/rotate_y.hpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Chris Ohk
2 |
3 | // I am making my contributions/submissions to this project solely in my
4 | // personal capacity and are not conveying any rights to any intellectual
5 | // property of any third parties.
6 |
7 | // It is based on Ray Tracing: The Rest of Your Life book.
8 | // References: https://raytracing.github.io
9 |
10 | #ifndef RAY_TRACING_ROTATE_Y_HPP
11 | #define RAY_TRACING_ROTATE_Y_HPP
12 |
13 | #include "hittable.hpp"
14 |
15 | #include
16 |
17 | class rotate_y final : public hittable
18 | {
19 | public:
20 | rotate_y(std::shared_ptr p, double angle);
21 |
22 | bool hit(const ray& r, double t_min, double t_max,
23 | hit_record& rec) const override;
24 | bool bounding_box(double t0, double t1, aabb& output_box) const override
25 | {
26 | output_box = bbox;
27 | return hasbox;
28 | }
29 |
30 | std::shared_ptr ptr;
31 | double sin_theta;
32 | double cos_theta;
33 | bool hasbox;
34 | aabb bbox;
35 | };
36 |
37 | inline rotate_y::rotate_y(std::shared_ptr p, double angle)
38 | : ptr(std::move(p))
39 | {
40 | const auto radians = degrees_to_radians(angle);
41 | sin_theta = sin(radians);
42 | cos_theta = cos(radians);
43 | hasbox = ptr->bounding_box(0, 1, bbox);
44 |
45 | point3 min{infinity, infinity, infinity};
46 | point3 max{-infinity, -infinity, -infinity};
47 |
48 | for (int i = 0; i < 2; ++i)
49 | {
50 | for (int j = 0; j < 2; ++j)
51 | {
52 | for (int k = 0; k < 2; ++k)
53 | {
54 | const auto x = i * bbox.max().x() + (1 - i) * bbox.min().x();
55 | const auto y = j * bbox.max().y() + (1 - j) * bbox.min().y();
56 | const auto z = k * bbox.max().z() + (1 - k) * bbox.min().z();
57 |
58 | const auto newx = cos_theta * x + sin_theta * z;
59 | const auto newz = -sin_theta * x + cos_theta * z;
60 |
61 | vec3 tester{newx, y, newz};
62 |
63 | for (int c = 0; c < 3; ++c)
64 | {
65 | min[c] = fmin(min[c], tester[c]);
66 | max[c] = fmax(max[c], tester[c]);
67 | }
68 | }
69 | }
70 | }
71 |
72 | bbox = aabb(min, max);
73 | }
74 |
75 | inline bool rotate_y::hit(const ray& r, double t_min, double t_max,
76 | hit_record& rec) const
77 | {
78 | auto origin = r.origin();
79 | auto direction = r.direction();
80 |
81 | origin[0] = cos_theta * r.origin()[0] - sin_theta * r.origin()[2];
82 | origin[2] = sin_theta * r.origin()[0] + cos_theta * r.origin()[2];
83 |
84 | direction[0] = cos_theta * r.direction()[0] - sin_theta * r.direction()[2];
85 | direction[2] = sin_theta * r.direction()[0] + cos_theta * r.direction()[2];
86 |
87 | const ray rotated_r{origin, direction, r.time()};
88 |
89 | if (!ptr->hit(rotated_r, t_min, t_max, rec))
90 | {
91 | return false;
92 | }
93 |
94 | auto p = rec.p;
95 | auto normal = rec.normal;
96 |
97 | p[0] = cos_theta * rec.p[0] + sin_theta * rec.p[2];
98 | p[2] = -sin_theta * rec.p[0] + cos_theta * rec.p[2];
99 |
100 | normal[0] = cos_theta * rec.normal[0] + sin_theta * rec.normal[2];
101 | normal[2] = -sin_theta * rec.normal[0] + cos_theta * rec.normal[2];
102 |
103 | rec.p = p;
104 | rec.set_face_normal(rotated_r, normal);
105 |
106 | return true;
107 | }
108 |
109 | #endif
--------------------------------------------------------------------------------
/src/solid_color.hpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Chris Ohk
2 |
3 | // I am making my contributions/submissions to this project solely in my
4 | // personal capacity and are not conveying any rights to any intellectual
5 | // property of any third parties.
6 |
7 | // It is based on Ray Tracing: The Rest of Your Life book.
8 | // References: https://raytracing.github.io
9 |
10 | #ifndef RAY_TRACING_SOLID_COLOR_HPP
11 | #define RAY_TRACING_SOLID_COLOR_HPP
12 |
13 | #include "texture.hpp"
14 |
15 | class solid_color final : public texture
16 | {
17 | public:
18 | solid_color() = default;
19 | solid_color(color c) : color_value(c)
20 | {
21 | // Do nothing
22 | }
23 | solid_color(double red, double green, double blue)
24 | : solid_color(color(red, green, blue))
25 | {
26 | // Do nothing
27 | }
28 |
29 | color value([[maybe_unused]] double u, [[maybe_unused]] double v,
30 | [[maybe_unused]] const point3& p) const override
31 | {
32 | return color_value;
33 | }
34 |
35 | private:
36 | color color_value;
37 | };
38 |
39 | #endif
--------------------------------------------------------------------------------
/src/sphere.hpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Chris Ohk
2 |
3 | // I am making my contributions/submissions to this project solely in my
4 | // personal capacity and are not conveying any rights to any intellectual
5 | // property of any third parties.
6 |
7 | // It is based on Ray Tracing: The Rest of Your Life book.
8 | // References: https://raytracing.github.io
9 |
10 | #ifndef RAY_TRACING_SPHERE_HPP
11 | #define RAY_TRACING_SPHERE_HPP
12 |
13 | #include "hittable.hpp"
14 | #include "vec3.hpp"
15 |
16 | #include
17 |
18 | void get_sphere_uv(const vec3& p, double& u, double& v);
19 | vec3 random_to_sphere(double radius, double distance_squared);
20 |
21 | class sphere final : public hittable
22 | {
23 | public:
24 | sphere() = default;
25 | sphere(vec3 cen, double r, std::shared_ptr m)
26 | : center(cen), radius(r), mat_ptr(std::move(m))
27 | {
28 | // Do nothing
29 | }
30 |
31 | bool hit(const ray& r, double t_min, double t_max,
32 | hit_record& rec) const override;
33 | bool bounding_box(double t0, double t1, aabb& output_box) const override;
34 |
35 | double pdf_value(const point3& o, const vec3& v) const override;
36 |
37 | vec3 random(const vec3& p) const override;
38 |
39 | vec3 center;
40 | double radius{ 0.0 };
41 | std::shared_ptr mat_ptr;
42 | };
43 |
44 | inline bool sphere::hit(const ray& r, double t_min, double t_max,
45 | hit_record& rec) const
46 | {
47 | const vec3 oc = r.origin() - center;
48 | const auto a = r.direction().length_squared();
49 | const auto half_b = dot(oc, r.direction());
50 | const auto c = oc.length_squared() - radius * radius;
51 | const auto discriminant = half_b * half_b - a * c;
52 |
53 | if (discriminant > 0)
54 | {
55 | const auto root = sqrt(discriminant);
56 | auto temp = (-half_b - root) / a;
57 |
58 | if (temp < t_max && temp > t_min)
59 | {
60 | rec.t = temp;
61 | rec.p = r.at(rec.t);
62 |
63 | const vec3 outward_normal = (rec.p - center) / radius;
64 | rec.set_face_normal(r, outward_normal);
65 | get_sphere_uv((rec.p - center) / radius, rec.u, rec.v);
66 | rec.mat_ptr = mat_ptr;
67 |
68 | return true;
69 | }
70 |
71 | temp = (-half_b + root) / a;
72 |
73 | if (temp < t_max && temp > t_min)
74 | {
75 | rec.t = temp;
76 | rec.p = r.at(rec.t);
77 |
78 | const vec3 outward_normal = (rec.p - center) / radius;
79 | rec.set_face_normal(r, outward_normal);
80 | get_sphere_uv((rec.p - center) / radius, rec.u, rec.v);
81 | rec.mat_ptr = mat_ptr;
82 |
83 | return true;
84 | }
85 | }
86 |
87 | return false;
88 | }
89 |
90 | inline bool sphere::bounding_box([[maybe_unused]] double t0,
91 | [[maybe_unused]] double t1,
92 | aabb& output_box) const
93 | {
94 | output_box = aabb(center - vec3(radius, radius, radius),
95 | center + vec3(radius, radius, radius));
96 |
97 | return true;
98 | }
99 |
100 | inline double sphere::pdf_value(const point3& o, const vec3& v) const
101 | {
102 | hit_record rec;
103 | if (!this->hit(ray(o, v), 0.001, infinity, rec))
104 | {
105 | return 0.0;
106 | }
107 |
108 | const auto cos_theta_max =
109 | sqrt(1 - radius * radius / (center - o).length_squared());
110 | const auto solid_angle = 2 * pi * (1 - cos_theta_max);
111 |
112 | return 1 / solid_angle;
113 | }
114 |
115 | inline vec3 sphere::random(const vec3& p) const
116 | {
117 | const vec3 direction = center - p;
118 | const auto distance_squared = direction.length_squared();
119 |
120 | onb uvw;
121 | uvw.build_from_w(direction);
122 |
123 | return uvw.local(random_to_sphere(radius, distance_squared));
124 | }
125 |
126 | inline void get_sphere_uv(const vec3& p, double& u, double& v)
127 | {
128 | const auto phi = atan2(p.z(), p.x());
129 | const auto theta = asin(p.y());
130 | u = 1 - (phi + pi) / (2 * pi);
131 | v = (theta + pi / 2) / pi;
132 | }
133 |
134 | inline vec3 random_to_sphere(double radius, double distance_squared)
135 | {
136 | const auto r1 = random_double();
137 | const auto r2 = random_double();
138 | const auto z = 1 + r2 * (sqrt(1 - radius * radius / distance_squared) - 1);
139 |
140 | const auto phi = 2 * pi * r1;
141 | const auto x = cos(phi) * sqrt(1 - z * z);
142 | const auto y = sin(phi) * sqrt(1 - z * z);
143 |
144 | return vec3{x, y, z};
145 | }
146 |
147 | #endif
--------------------------------------------------------------------------------
/src/texture.hpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Chris Ohk
2 |
3 | // I am making my contributions/submissions to this project solely in my
4 | // personal capacity and are not conveying any rights to any intellectual
5 | // property of any third parties.
6 |
7 | // It is based on Ray Tracing: The Rest of Your Life book.
8 | // References: https://raytracing.github.io
9 |
10 | #ifndef RAY_TRACING_TEXTURE_HPP
11 | #define RAY_TRACING_TEXTURE_HPP
12 |
13 | #include "common.hpp"
14 |
15 | class texture
16 | {
17 | public:
18 | virtual ~texture() = default;
19 |
20 | virtual color value(double u, double v, const point3& p) const = 0;
21 | };
22 |
23 | #endif
--------------------------------------------------------------------------------
/src/translate.hpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Chris Ohk
2 |
3 | // I am making my contributions/submissions to this project solely in my
4 | // personal capacity and are not conveying any rights to any intellectual
5 | // property of any third parties.
6 |
7 | // It is based on Ray Tracing: The Rest of Your Life book.
8 | // References: https://raytracing.github.io
9 |
10 | #ifndef RAY_TRACING_TRANSLATE_HPP
11 | #define RAY_TRACING_TRANSLATE_HPP
12 |
13 | #include "hittable.hpp"
14 |
15 | #include
16 |
17 | class translate final : public hittable
18 | {
19 | public:
20 | translate(std::shared_ptr p, const vec3& displacement)
21 | : ptr(std::move(p)), offset(displacement)
22 | {
23 | // Do nothing
24 | }
25 |
26 | bool hit(const ray& r, double t_min, double t_max,
27 | hit_record& rec) const override;
28 | bool bounding_box(double t0, double t1, aabb& output_box) const override;
29 |
30 | std::shared_ptr ptr;
31 | vec3 offset;
32 | };
33 |
34 | inline bool translate::hit(const ray& r, double t_min, double t_max,
35 | hit_record& rec) const
36 | {
37 | const ray moved_r{r.origin() - offset, r.direction(), r.time()};
38 | if (!ptr->hit(moved_r, t_min, t_max, rec))
39 | {
40 | return false;
41 | }
42 |
43 | rec.p += offset;
44 | rec.set_face_normal(moved_r, rec.normal);
45 |
46 | return true;
47 | }
48 |
49 | inline bool translate::bounding_box(double t0, double t1,
50 | aabb& output_box) const
51 | {
52 | if (!ptr->bounding_box(t0, t1, output_box))
53 | {
54 | return false;
55 | }
56 |
57 | output_box = aabb{output_box.min() + offset, output_box.max() + offset};
58 |
59 | return true;
60 | }
61 |
62 | #endif
--------------------------------------------------------------------------------
/src/vec3.hpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Chris Ohk
2 |
3 | // I am making my contributions/submissions to this project solely in my
4 | // personal capacity and are not conveying any rights to any intellectual
5 | // property of any third parties.
6 |
7 | // It is based on Ray Tracing: The Rest of Your Life book.
8 | // References: https://raytracing.github.io
9 |
10 | #ifndef RAY_TRACING_VEC3_HPP
11 | #define RAY_TRACING_VEC3_HPP
12 |
13 | #include
14 | #include
15 |
16 | class vec3
17 | {
18 | public:
19 | vec3() : e{0, 0, 0}
20 | {
21 | // Do nothing
22 | }
23 |
24 | vec3(double e0, double e1, double e2) : e{e0, e1, e2}
25 | {
26 | // Do nothing
27 | }
28 |
29 | vec3 operator-() const
30 | {
31 | return vec3{-e[0], -e[1], -e[2]};
32 | }
33 |
34 | double operator[](int i) const
35 | {
36 | return e[i];
37 | }
38 |
39 | double& operator[](int i)
40 | {
41 | return e[i];
42 | }
43 |
44 | vec3& operator+=(const vec3& v)
45 | {
46 | e[0] += v.e[0];
47 | e[1] += v.e[1];
48 | e[2] += v.e[2];
49 |
50 | return *this;
51 | }
52 |
53 | vec3& operator*=(const double t)
54 | {
55 | e[0] += t;
56 | e[1] += t;
57 | e[2] += t;
58 |
59 | return *this;
60 | }
61 |
62 | vec3& operator/=(const double t)
63 | {
64 | return *this *= (1 / t);
65 | }
66 |
67 | double x() const
68 | {
69 | return e[0];
70 | }
71 |
72 | double y() const
73 | {
74 | return e[1];
75 | }
76 |
77 | double z() const
78 | {
79 | return e[2];
80 | }
81 |
82 | double length() const
83 | {
84 | return std::sqrt(length_squared());
85 | }
86 |
87 | double length_squared() const
88 | {
89 | return e[0] * e[0] + e[1] * e[1] + e[2] * e[2];
90 | }
91 |
92 | void write_color(std::ostream& out, int samples_per_pixel)
93 | {
94 | // Replace NaN component values with zero.
95 | // See explanation in Ray Tracing: The Rest of Your Life.
96 | if (e[0] != e[0])
97 | {
98 | e[0] = 0.0;
99 | }
100 | if (e[1] != e[1])
101 | {
102 | e[1] = 0.0;
103 | }
104 | if (e[2] != e[2])
105 | {
106 | e[2] = 0.0;
107 | }
108 |
109 | // Divide the color total by the number of samples and gamma-correct
110 | // for a gamma value of 2.0.
111 | const auto scale = 1.0 / samples_per_pixel;
112 | const auto r = sqrt(scale * e[0]);
113 | const auto g = sqrt(scale * e[1]);
114 | const auto b = sqrt(scale * e[2]);
115 |
116 | // Write the translated [0,255] value of each color component.
117 | out << static_cast(256 * std::clamp(r, 0.0, 0.999)) << ' '
118 | << static_cast(256 * std::clamp(g, 0.0, 0.999)) << ' '
119 | << static_cast(256 * std::clamp(b, 0.0, 0.999)) << '\n';
120 | }
121 |
122 | static vec3 random()
123 | {
124 | return vec3(random_double(), random_double(), random_double());
125 | }
126 |
127 | static vec3 random(double min, double max)
128 | {
129 | return vec3(random_double(min, max), random_double(min, max),
130 | random_double(min, max));
131 | }
132 |
133 | double e[3];
134 | };
135 |
136 | // Type aliases for vec3
137 | using point3 = vec3; // 3D point
138 | using color = vec3; // RGB color
139 |
140 | inline std::ostream& operator<<(std::ostream& out, const vec3& v)
141 | {
142 | return out << v.e[0] << ' ' << v.e[1] << ' ' << v.e[2];
143 | }
144 |
145 | inline vec3 operator+(const vec3& u, const vec3& v)
146 | {
147 | return {u.e[0] + v.e[0], u.e[1] + v.e[1], u.e[2] + v.e[2]};
148 | }
149 |
150 | inline vec3 operator-(const vec3& u, const vec3& v)
151 | {
152 | return {u.e[0] - v.e[0], u.e[1] - v.e[1], u.e[2] - v.e[2]};
153 | }
154 |
155 | inline vec3 operator*(const vec3& u, const vec3& v)
156 | {
157 | return {u.e[0] * v.e[0], u.e[1] * v.e[1], u.e[2] * v.e[2]};
158 | }
159 |
160 | inline vec3 operator*(double t, const vec3& v)
161 | {
162 | return {t * v.e[0], t * v.e[1], t * v.e[2]};
163 | }
164 |
165 | inline vec3 operator*(const vec3& v, double t)
166 | {
167 | return t * v;
168 | }
169 |
170 | inline vec3 operator/(vec3 v, double t)
171 | {
172 | return (1 / t) * v;
173 | }
174 |
175 | inline double dot(const vec3& u, const vec3& v)
176 | {
177 | return u.e[0] * v.e[0] + u.e[1] * v.e[1] + u.e[2] * v.e[2];
178 | }
179 |
180 | inline vec3 cross(const vec3& u, const vec3& v)
181 | {
182 | return {u.e[1] * v.e[2] - u.e[2] * v.e[1],
183 | u.e[2] * v.e[0] - u.e[0] * v.e[2],
184 | u.e[0] * v.e[1] - u.e[1] * v.e[0]};
185 | }
186 |
187 | inline vec3 unit_vector(vec3 v)
188 | {
189 | return v / v.length();
190 | }
191 |
192 | inline vec3 random_in_unit_sphere()
193 | {
194 | while (true)
195 | {
196 | auto p = vec3::random(-1, 1);
197 | if (p.length_squared() >= 1)
198 | {
199 | continue;
200 | }
201 |
202 | return p;
203 | }
204 | }
205 |
206 | inline vec3 random_unit_vector()
207 | {
208 | const auto a = random_double(0, 2 * pi);
209 | const auto z = random_double(-1, 1);
210 | const auto r = sqrt(1 - z * z);
211 |
212 | return vec3{r * std::cos(a), r * std::sin(a), z};
213 | }
214 |
215 | inline vec3 random_in_hemisphere(const vec3& normal)
216 | {
217 | const vec3 in_unit_sphere = random_in_unit_sphere();
218 |
219 | // In the same hemisphere as the normal
220 | if (dot(in_unit_sphere, normal) > 0.0)
221 | {
222 | return in_unit_sphere;
223 | }
224 | else
225 | {
226 | return -in_unit_sphere;
227 | }
228 | }
229 |
230 | inline vec3 reflect(const vec3& v, const vec3& n)
231 | {
232 | return v - 2 * dot(v, n) * n;
233 | }
234 |
235 | inline vec3 refract(const vec3& uv, const vec3& n, double etai_over_etat)
236 | {
237 | const auto cos_theta = dot(-uv, n);
238 | const vec3 r_out_parallel = etai_over_etat * (uv + cos_theta * n);
239 | const vec3 r_out_perp = -sqrt(1.0 - r_out_parallel.length_squared()) * n;
240 |
241 | return r_out_parallel + r_out_perp;
242 | }
243 |
244 | inline vec3 random_in_unit_disk()
245 | {
246 | while (true)
247 | {
248 | auto p = vec3{random_double(-1, 1), random_double(-1, 1), 0};
249 |
250 | if (p.length_squared() >= 1)
251 | {
252 | continue;
253 | }
254 |
255 | return p;
256 | }
257 | }
258 |
259 | #endif
--------------------------------------------------------------------------------
/src/xy_rect.hpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Chris Ohk
2 |
3 | // I am making my contributions/submissions to this project solely in my
4 | // personal capacity and are not conveying any rights to any intellectual
5 | // property of any third parties.
6 |
7 | // It is based on Ray Tracing: The Rest of Your Life book.
8 | // References: https://raytracing.github.io
9 |
10 | #ifndef RAY_TRACING_XY_RECT_HPP
11 | #define RAY_TRACING_XY_RECT_HPP
12 |
13 | #include "hittable.hpp"
14 |
15 | #include
16 |
17 | class xy_rect final : public hittable
18 | {
19 | public:
20 | xy_rect() = default;
21 | xy_rect(double _x0, double _x1, double _y0, double _y1, double _k,
22 | std::shared_ptr mat)
23 | : mp(std::move(mat)), x0(_x0), x1(_x1), y0(_y0), y1(_y1), k(_k)
24 | {
25 | // Do nothing
26 | }
27 |
28 | bool hit(const ray& r, double t0, double t1,
29 | hit_record& rec) const override;
30 | bool bounding_box([[maybe_unused]] double t0, [[maybe_unused]] double t1,
31 | aabb& output_box) const override
32 | {
33 | // The bounding box must have non-zero width in each dimension,
34 | // so pad the Z dimension a small amount.
35 | output_box =
36 | aabb(point3(x0, y0, k - 0.0001), point3(x1, y1, k + 0.0001));
37 | return true;
38 | }
39 |
40 | std::shared_ptr mp;
41 | double x0 = 0.0, x1 = 0.0, y0 = 0.0, y1 = 0.0, k = 0.0;
42 | };
43 |
44 | inline bool xy_rect::hit(const ray& r, double t0, double t1,
45 | hit_record& rec) const
46 | {
47 | const auto t = (k - r.origin().z()) / r.direction().z();
48 | if (t < t0 || t > t1)
49 | {
50 | return false;
51 | }
52 |
53 | const auto x = r.origin().x() + t * r.direction().x();
54 | const auto y = r.origin().y() + t * r.direction().y();
55 |
56 | if (x < x0 || x > x1 || y < y0 || y > y1)
57 | {
58 | return false;
59 | }
60 |
61 | rec.u = (x - x0) / (x1 - x0);
62 | rec.v = (y - y0) / (y1 - y0);
63 | rec.t = t;
64 |
65 | const auto outward_normal = vec3(0, 0, 1);
66 | rec.set_face_normal(r, outward_normal);
67 | rec.mat_ptr = mp;
68 | rec.p = r.at(t);
69 |
70 | return true;
71 | }
72 |
73 | #endif
--------------------------------------------------------------------------------
/src/xz_rect.hpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Chris Ohk
2 |
3 | // I am making my contributions/submissions to this project solely in my
4 | // personal capacity and are not conveying any rights to any intellectual
5 | // property of any third parties.
6 |
7 | // It is based on Ray Tracing: The Rest of Your Life book.
8 | // References: https://raytracing.github.io
9 |
10 | #ifndef RAY_TRACING_XZ_RECT_HPP
11 | #define RAY_TRACING_XZ_RECT_HPP
12 |
13 | #include "hittable.hpp"
14 |
15 | #include
16 |
17 | class xz_rect final : public hittable
18 | {
19 | public:
20 | xz_rect() = default;
21 | xz_rect(double _x0, double _x1, double _z0, double _z1, double _k,
22 | std::shared_ptr mat)
23 | : mp(std::move(mat)), x0(_x0), x1(_x1), z0(_z0), z1(_z1), k(_k)
24 | {
25 | // Do nothing
26 | }
27 |
28 | bool hit(const ray& r, double t0, double t1,
29 | hit_record& rec) const override;
30 | bool bounding_box([[maybe_unused]] double t0, [[maybe_unused]] double t1,
31 | aabb& output_box) const override
32 | {
33 | // The bounding box must have non-zero width in each dimension,
34 | // so pad the Y dimension a small amount.
35 | output_box =
36 | aabb(point3(x0, k - 0.0001, z0), point3(x1, k + 0.0001, z1));
37 | return true;
38 | }
39 |
40 | double pdf_value(const point3& origin, const vec3& v) const override
41 | {
42 | hit_record rec;
43 | if (!this->hit(ray{origin, v}, 0.001, infinity, rec))
44 | {
45 | return 0.0;
46 | }
47 |
48 | const auto area = (x1 - x0) * (z1 - z0);
49 | const auto distance_squared = rec.t * rec.t * v.length_squared();
50 | const auto cosine = fabs(dot(v, rec.normal) / v.length());
51 |
52 | return distance_squared / (cosine * area);
53 | }
54 |
55 | vec3 random(const vec3& origin) const override
56 | {
57 | const auto random_point =
58 | point3{random_double(x0, x1), k, random_double(z0, z1)};
59 | return random_point - origin;
60 | }
61 |
62 | std::shared_ptr mp;
63 | double x0 = 0.0, x1 = 0.0, z0 = 0.0, z1 = 0.0, k = 0.0;
64 | };
65 |
66 | inline bool xz_rect::hit(const ray& r, double t0, double t1,
67 | hit_record& rec) const
68 | {
69 | const auto t = (k - r.origin().y()) / r.direction().y();
70 | if (t < t0 || t > t1)
71 | {
72 | return false;
73 | }
74 |
75 | const auto x = r.origin().x() + t * r.direction().x();
76 | const auto z = r.origin().z() + t * r.direction().z();
77 |
78 | if (x < x0 || x > x1 || z < z0 || z > z1)
79 | {
80 | return false;
81 | }
82 |
83 | rec.u = (x - x0) / (x1 - x0);
84 | rec.v = (z - z0) / (z1 - z0);
85 | rec.t = t;
86 |
87 | const auto outward_normal = vec3(0, 1, 0);
88 | rec.set_face_normal(r, outward_normal);
89 | rec.mat_ptr = mp;
90 | rec.p = r.at(t);
91 |
92 | return true;
93 | }
94 |
95 | #endif
--------------------------------------------------------------------------------
/src/yz_rect.hpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Chris Ohk
2 |
3 | // I am making my contributions/submissions to this project solely in my
4 | // personal capacity and are not conveying any rights to any intellectual
5 | // property of any third parties.
6 |
7 | // It is based on Ray Tracing: The Rest of Your Life book.
8 | // References: https://raytracing.github.io
9 |
10 | #ifndef RAY_TRACING_YZ_RECT_HPP
11 | #define RAY_TRACING_YZ_RECT_HPP
12 |
13 | #include "hittable.hpp"
14 |
15 | #include
16 |
17 | class yz_rect final : public hittable
18 | {
19 | public:
20 | yz_rect() = default;
21 | yz_rect(double _y0, double _y1, double _z0, double _z1, double _k,
22 | std::shared_ptr mat)
23 | : mp(std::move(mat)), y0(_y0), y1(_y1), z0(_z0), z1(_z1), k(_k)
24 | {
25 | // Do nothing
26 | }
27 |
28 | bool hit(const ray& r, double t0, double t1,
29 | hit_record& rec) const override;
30 | bool bounding_box([[maybe_unused]] double t0, [[maybe_unused]] double t1,
31 | aabb& output_box) const override
32 | {
33 | // The bounding box must have non-zero width in each dimension,
34 | // so pad the X dimension a small amount.
35 | output_box =
36 | aabb(point3(k - 0.0001, y0, z0), point3(k + 0.0001, y1, z1));
37 | return true;
38 | }
39 |
40 | std::shared_ptr mp;
41 | double y0 = 0.0, y1 = 0.0, z0 = 0.0, z1 = 0.0, k = 0.0;
42 | };
43 |
44 | inline bool yz_rect::hit(const ray& r, double t0, double t1,
45 | hit_record& rec) const
46 | {
47 | const auto t = (k - r.origin().x()) / r.direction().x();
48 | if (t < t0 || t > t1)
49 | {
50 | return false;
51 | }
52 |
53 | const auto y = r.origin().y() + t * r.direction().y();
54 | const auto z = r.origin().z() + t * r.direction().z();
55 |
56 | if (y < y0 || y > y1 || z < z0 || z > z1)
57 | {
58 | return false;
59 | }
60 |
61 | rec.u = (y - y0) / (y1 - y0);
62 | rec.v = (z - z0) / (z1 - z0);
63 | rec.t = t;
64 |
65 | const auto outward_normal = vec3(1, 0, 0);
66 | rec.set_face_normal(r, outward_normal);
67 | rec.mat_ptr = mp;
68 | rec.p = r.at(t);
69 |
70 | return true;
71 | }
72 |
73 | #endif
--------------------------------------------------------------------------------