├── .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 --------------------------------------------------------------------------------