├── .clang-format ├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── data ├── 120x120at25ssp.jpg ├── 300x300at4ssp_working_lights.png ├── 840x840at16spp_fixed_metal_reflections.png └── compile_time_glass_ball.png ├── src ├── camera.h ├── color.h ├── dynamic_array.h ├── image.h ├── light.h ├── main.cpp ├── material.h ├── ray.h ├── raytracer.h ├── rect.h ├── runtime.cpp ├── shape.h ├── sphere.h ├── threads │ ├── thread_0.cpp │ ├── thread_1.cpp │ ├── thread_10.cpp │ ├── thread_11.cpp │ ├── thread_2.cpp │ ├── thread_3.cpp │ ├── thread_4.cpp │ ├── thread_5.cpp │ ├── thread_6.cpp │ ├── thread_7.cpp │ ├── thread_8.cpp │ ├── thread_9.cpp │ └── threads.h ├── unique_ptr.h ├── util.h ├── vec3.h └── world.h └── test ├── test_main.cpp ├── test_ray.h ├── test_util.h └── test_vec3.h /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | 3 | AccessModifierOffset: 0 4 | AlignEscapedNewlinesLeft: false 5 | AlignTrailingComments: true 6 | AlignAfterOpenBracket: DontAlign 7 | AllowAllParametersOfDeclarationOnNextLine: true 8 | AllowShortFunctionsOnASingleLine: true 9 | AllowShortIfStatementsOnASingleLine: true 10 | AllowShortLoopsOnASingleLine: false 11 | AlwaysBreakBeforeMultilineStrings: false 12 | AlwaysBreakTemplateDeclarations: false 13 | BinPackArguments: false 14 | BinPackParameters: false 15 | BreakBeforeBinaryOperators: false 16 | BreakBeforeBraces: Allman 17 | BreakBeforeTernaryOperators: false 18 | BreakConstructorInitializersBeforeComma: false 19 | ColumnLimit: 100 20 | CommentPragmas: '' 21 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 22 | ConstructorInitializerIndentWidth: 0 23 | ContinuationIndentWidth: 4 24 | Cpp11BracedListStyle: false 25 | DerivePointerBinding: false 26 | IndentCaseLabels: true 27 | IndentFunctionDeclarationAfterType: false 28 | IndentWidth: 4 29 | Language: Cpp 30 | MaxEmptyLinesToKeep: 4 31 | NamespaceIndentation: None 32 | PenaltyBreakBeforeFirstCallParameter: 100 33 | PenaltyBreakComment: 100 34 | PenaltyBreakFirstLessLess: 0 35 | PenaltyBreakString: 100 36 | PenaltyExcessCharacter: 1 37 | PenaltyReturnTypeOnItsOwnLine: 20 38 | PointerBindsToType: true 39 | SpaceBeforeAssignmentOperators: true 40 | SpaceBeforeParens: Always 41 | SpaceInEmptyParentheses: false 42 | SpacesBeforeTrailingComments: 1 43 | SpacesInAngles: false 44 | SpacesInCStyleCastParentheses: false 45 | SpacesInContainerLiterals: false 46 | SpacesInParentheses: false 47 | Standard: Auto 48 | TabWidth: 4 49 | UseTab: ForIndentation 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.vscode 2 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.7 FATAL_ERROR) 2 | 3 | project(CERT VERSION 0.0.1) 4 | 5 | add_executable(cert src/main.cpp 6 | src/threads/thread_0.cpp src/threads/thread_1.cpp src/threads/thread_2.cpp src/threads/thread_3.cpp 7 | src/threads/thread_4.cpp src/threads/thread_5.cpp src/threads/thread_6.cpp src/threads/thread_7.cpp 8 | src/threads/thread_8.cpp src/threads/thread_9.cpp src/threads/thread_10.cpp src/threads/thread_11.cpp 9 | src/threads/threads.h 10 | src/color.h src/ray.h src/shape.h src/shape.h src/util.h src/vec3.h) 11 | 12 | set_property(TARGET cert PROPERTY CXX_STANDARD 20) 13 | target_compile_options(cert PUBLIC -std=c++2a -fconstexpr-steps=2147483647 -O3) 14 | 15 | add_executable(rt-runtime src/runtime.cpp 16 | src/color.h src/ray.h src/shape.h src/shape.h src/util.h src/vec3.h) 17 | set_property(TARGET rt-runtime PROPERTY CXX_STANDARD 20) 18 | 19 | add_executable(cert-test test/test_main.cpp) 20 | 21 | set_property(TARGET cert-test PROPERTY CXX_STANDARD 20) 22 | target_compile_options(cert-test PUBLIC -std=c++2a -fconstexpr-steps=2147483647 -O3) 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2021 Charles Giessen 2 | 3 | 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: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | 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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ConstExpr RayTracer (CERT) 2 | 3 | Behold, the worlds fastest (runtime) raytracer! 4 | 5 | Through the power of `constexpr`, we can trace rays in a place that was never meant to ray trace, the compiler. 6 | 7 | Currently in a proof of concept state, more features are to be added in the future. 8 | 9 | Example image output. 10 | 11 | ![](https://github.com/cdgiessen/CERT/blob/master/data/840x840at16spp_fixed_metal_reflections.png) 12 | 13 | ## Build Requirements 14 | Requires a C++20 ready compiler to build. Tested with clang-10. 15 | 16 | Requires a recent version of cmake. Tested with cmake 3.13.4 17 | 18 | ## Building 19 | Make sure you are running a C++20 capable compiler. Easiest way is to check by running `gcc -std=c++2a` or `clang -std=c++2a`. If the `std=c++2a` is unrecognized, a newer C++ compiler is needed. 20 | 21 | To clone and build 22 | ``` 23 | git clone https://github.com/cdgiessen/CERT.git 24 | mkdir build 25 | cd build 26 | cmake ../CERT 27 | ``` 28 | 29 | ## Running 30 | Once build, run the 'cert' executable located in the build directory. 31 | The `output.ppm` should of been created. 32 | 33 | ## Credits 34 | Based of the venerable [RayTracingInOneWeekend](http://www.realtimerendering.com/raytracing/Ray%20Tracing%20in%20a%20Weekend.pdf) 35 | -------------------------------------------------------------------------------- /data/120x120at25ssp.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cdgiessen/CERT/1d686bbf8468d9ad22c5d45bec0f41ed901ebfa2/data/120x120at25ssp.jpg -------------------------------------------------------------------------------- /data/300x300at4ssp_working_lights.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cdgiessen/CERT/1d686bbf8468d9ad22c5d45bec0f41ed901ebfa2/data/300x300at4ssp_working_lights.png -------------------------------------------------------------------------------- /data/840x840at16spp_fixed_metal_reflections.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cdgiessen/CERT/1d686bbf8468d9ad22c5d45bec0f41ed901ebfa2/data/840x840at16spp_fixed_metal_reflections.png -------------------------------------------------------------------------------- /data/compile_time_glass_ball.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cdgiessen/CERT/1d686bbf8468d9ad22c5d45bec0f41ed901ebfa2/data/compile_time_glass_ball.png -------------------------------------------------------------------------------- /src/camera.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ray.h" 4 | namespace cert 5 | { 6 | 7 | class Camera 8 | { 9 | public: 10 | constexpr Camera (Vec3 lookfrom, Vec3 lookat, Vec3 vup, float vfov, float aspect) 11 | { 12 | Vec3 u, v, w; 13 | float theta = vfov * 3.141 / 180; 14 | float half_height = tan (theta / 2); 15 | float half_width = aspect * half_height; 16 | origin = lookfrom; 17 | w = normalize (lookfrom - lookat); 18 | u = normalize (cross (vup, w)); 19 | v = cross (w, u); 20 | upper_left_corner = origin - half_width * u + half_height * v - w; 21 | horizontal = 2 * half_width * u; 22 | vertical = 2 * half_height * v; 23 | } 24 | 25 | constexpr Ray get_ray (float u, float v) 26 | { 27 | return Ray (origin, upper_left_corner + u * horizontal - v * vertical - origin); 28 | } 29 | 30 | Vec3 origin; 31 | Vec3 upper_left_corner; 32 | Vec3 horizontal; 33 | Vec3 vertical; 34 | }; 35 | } -------------------------------------------------------------------------------- /src/color.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #include "vec3.h" 5 | namespace cert 6 | { 7 | struct Color 8 | { 9 | uint8_t r = 0, g = 0, b = 0; 10 | }; 11 | 12 | constexpr Color vec_to_color (Vec3 const& v) 13 | { 14 | return { static_cast (clamp (v.x * 255.99, 0.0, 255.0)), 15 | static_cast (clamp (v.y * 255.99, 0.0, 255.0)), 16 | static_cast (clamp (v.z * 255.99, 0.0, 255.0)) }; 17 | } 18 | } -------------------------------------------------------------------------------- /src/dynamic_array.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace cert 4 | { 5 | template class DynamicArray 6 | { 7 | T* m_data = nullptr; 8 | std::size_t m_size = 0; 9 | std::size_t m_allocated = 0; 10 | 11 | public: 12 | constexpr DynamicArray () 13 | { 14 | m_allocated = 100; 15 | m_data = new T[m_allocated]; 16 | m_size = 0; 17 | } 18 | constexpr DynamicArray (std::size_t size) 19 | { 20 | m_data = new T[size]; 21 | m_size = size; 22 | m_allocated = size; 23 | } 24 | constexpr ~DynamicArray () 25 | { 26 | if (m_data != nullptr) delete[] m_data; 27 | } 28 | DynamicArray (DynamicArray const& obj) = delete; 29 | DynamicArray& operator= (DynamicArray const& obj) = delete; 30 | 31 | DynamicArray (DynamicArray&& obj) 32 | : m_data (obj.m_data), m_size (obj.m_size), m_allocated (obj.m_allocated) 33 | { 34 | obj.m_data = nullptr; 35 | } 36 | DynamicArray& operator= (DynamicArray&& obj) 37 | { 38 | m_data = obj.m_data; 39 | m_size = obj.m_size; 40 | m_allocated = obj.m_allocated; 41 | obj.m_data = nullptr; 42 | return *this; 43 | } 44 | 45 | constexpr void resize (std::size_t new_size) 46 | { 47 | if (new_size > m_allocated) 48 | { 49 | T* temp = new T[m_allocated * 2]; 50 | for (int i = 0; i < m_size; i++) 51 | { 52 | temp[i] = m_data[i]; 53 | } 54 | delete[] m_data; 55 | m_data = temp; 56 | m_size = new_size; 57 | m_allocated = m_allocated * 2; 58 | } 59 | else 60 | { 61 | m_size = new_size; 62 | } 63 | } 64 | 65 | constexpr void reserve (std::size_t new_reserve) 66 | { 67 | if (new_reserve > m_allocated) 68 | { 69 | T* temp = new T[new_reserve]; 70 | for (int i = 0; i < m_size; i++) 71 | { 72 | temp[i] = m_data[i]; 73 | } 74 | delete[] m_data; 75 | m_data = temp; 76 | m_allocated = new_reserve; 77 | } 78 | } 79 | 80 | constexpr T& set (std::size_t index, T value) 81 | { 82 | if (index < m_size) 83 | { 84 | m_data[index] = value; 85 | return m_data[index]; 86 | } 87 | } 88 | 89 | constexpr void push_back (T value) 90 | { 91 | int new_val_index = m_size; 92 | if (m_size >= m_allocated) 93 | { 94 | reserve (2 * m_allocated); 95 | } 96 | m_data[new_val_index] = value; 97 | m_size++; 98 | } 99 | 100 | constexpr void erase (std::size_t index) 101 | { 102 | // TODO: make shrinking vector 103 | } 104 | 105 | constexpr void reset () { m_size = 0; } 106 | 107 | constexpr T const& at (std::size_t index) const { return m_data[index]; } 108 | constexpr T& at (std::size_t index) { return m_data[index]; } 109 | constexpr std::size_t size () const { return m_size; } 110 | constexpr T* data () const { return m_data; } 111 | }; 112 | } // namespace cert -------------------------------------------------------------------------------- /src/image.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "vec3.h" 4 | #include "util.h" 5 | #include "dynamic_array.h" 6 | 7 | namespace cert 8 | { 9 | template struct OutputImage 10 | { 11 | constexpr OutputImage () {} 12 | constexpr void set (int x, int y, T const& value) { data[y * width + x] = value; } 13 | constexpr T get (int x, int y) const { return data[y * width + x]; } 14 | constexpr T at (int index) const { return data[index]; } 15 | constexpr std::size_t size () const { return data.size (); } 16 | 17 | private: 18 | std::array data; 19 | }; 20 | 21 | class Texture 22 | { 23 | public: 24 | constexpr Texture (int width, int height) { tex.resize (width * height); } 25 | constexpr void set (int x, int y, Vec3 const& value) { tex.at (x * width + y) = value; } 26 | constexpr Vec3 get (int x, int y) const { return tex.at (x * width + y); } 27 | 28 | constexpr Vec3 sample (UV uv) { return tex.at (get_index (uv.u, uv.v)); } 29 | 30 | private: 31 | constexpr size_t get_index (float x, float y) const 32 | { 33 | return static_cast (x * width) * width + static_cast (y * height); 34 | } 35 | 36 | int width; 37 | int height; 38 | DynamicArray tex; 39 | }; 40 | 41 | } // namespace cert -------------------------------------------------------------------------------- /src/light.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ray.h" 4 | #include "vec3.h" 5 | 6 | namespace cert 7 | { 8 | struct LightRayRecord 9 | { 10 | bool visible; 11 | Vec3 attenuation; 12 | }; 13 | 14 | struct Light 15 | { 16 | constexpr Light (Vec3 position, Vec3 color) : position (position), color (color) {} 17 | constexpr virtual ~Light (); 18 | constexpr Ray ray_to_light (Vec3 const& point) { return { point, position - point }; } 19 | constexpr virtual LightRayRecord visible (Vec3 const& point, Vec3 const& normal) = 0; 20 | Vec3 position; 21 | Vec3 color; 22 | }; 23 | 24 | constexpr Light::~Light () {} 25 | 26 | struct PointLight : public Light 27 | { 28 | constexpr PointLight (Vec3 position, Vec3 color, float strength) 29 | : Light (position, color), strength (strength) 30 | { 31 | } 32 | constexpr LightRayRecord visible (Vec3 const& point, Vec3 const& normal) override 33 | { 34 | Vec3 direction = position - point; 35 | float distance = direction.length (); 36 | float lambert = dot (normal, normalize (direction)); 37 | 38 | return { .visible = lambert > 0.0f, .attenuation = color * lambert * strength / (distance * distance) }; 39 | } 40 | float strength; 41 | }; 42 | 43 | struct SpotLight : public Light 44 | { 45 | constexpr SpotLight (Vec3 position, Vec3 color, Vec3 direction, float strength, float cutoff) 46 | : Light (position, color), direction (direction), strength (strength), cutoff (cutoff) 47 | { 48 | } 49 | constexpr virtual bool visible (Ray const& ray) 50 | { 51 | return dot (ray.direction, direction) > 0.0f && (ray.origin - position).length () < strength; 52 | } 53 | 54 | Vec3 direction; 55 | float strength; 56 | float cutoff; 57 | }; 58 | } // namespace cert -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "raytracer.h" 5 | 6 | #include "threads/threads.h" 7 | 8 | template void print_image (std::ofstream& ppm, T const& frame) 9 | { 10 | for (int i = 0; i < frame.size (); i++) 11 | { 12 | int r = static_cast (frame.at (i).r); 13 | int g = static_cast (frame.at (i).g); 14 | int b = static_cast (frame.at (i).b); 15 | 16 | ppm << r << " " << g << " " << b << "\n"; 17 | } 18 | } 19 | 20 | int main (int argc, char** argv) 21 | { 22 | std::ofstream ppm ("output_compiletime.ppm"); 23 | ppm << "P3\n" << width << " " << height << "\n255\n"; 24 | print_image (ppm, get_frame_0 ()); 25 | print_image (ppm, get_frame_1 ()); 26 | print_image (ppm, get_frame_2 ()); 27 | print_image (ppm, get_frame_3 ()); 28 | print_image (ppm, get_frame_4 ()); 29 | print_image (ppm, get_frame_5 ()); 30 | print_image (ppm, get_frame_6 ()); 31 | print_image (ppm, get_frame_7 ()); 32 | print_image (ppm, get_frame_8 ()); 33 | print_image (ppm, get_frame_9 ()); 34 | print_image (ppm, get_frame_10 ()); 35 | print_image (ppm, get_frame_11 ()); 36 | } -------------------------------------------------------------------------------- /src/material.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ray.h" 4 | #include "vec3.h" 5 | 6 | #include "image.h" 7 | namespace cert 8 | { 9 | struct ScatterOut 10 | { 11 | bool is_scattered; 12 | Vec3 attenuation; 13 | Ray scattered = Ray (VEC3_ZERO, VEC3_RIGHT); 14 | float index = -1.0f; 15 | Ray refracted = Ray (VEC3_ZERO, VEC3_RIGHT); 16 | }; 17 | 18 | struct Material 19 | { 20 | constexpr Material (Vec3 albedo) : albedo (albedo) {} 21 | constexpr virtual ~Material (); 22 | 23 | constexpr virtual ScatterOut scatter ( 24 | Vec3 point, Vec3 normal, UV uv, const Ray& r_in, PRNG& random) const = 0; 25 | 26 | Vec3 albedo; 27 | }; 28 | 29 | constexpr Material::~Material () {} 30 | 31 | 32 | struct Lambertian : public Material 33 | { 34 | constexpr Lambertian (const Vec3& a) : Material (a) {} 35 | constexpr virtual ScatterOut scatter (Vec3 point, Vec3 normal, UV uv, const Ray& r_in, PRNG& random) const 36 | { 37 | return { .is_scattered = false, .attenuation = albedo }; 38 | } 39 | }; 40 | 41 | struct Metal : public Material 42 | { 43 | constexpr Metal (const Vec3& a) : Material (a) {} 44 | constexpr virtual ScatterOut scatter (Vec3 point, Vec3 normal, UV uv, const Ray& r_in, PRNG& random) const 45 | { 46 | Vec3 reflected = reflect (normalize (r_in.direction), normal); 47 | return { .is_scattered = dot (reflected, normal) > 0.0, 48 | .attenuation = albedo, 49 | .scattered = Ray (point, reflected) }; 50 | } 51 | }; 52 | 53 | constexpr float schlick (float cosine, float ref_idx) 54 | { 55 | float r0 = (1 - ref_idx) / (1 + ref_idx); 56 | r0 = r0 * r0; 57 | return r0 + (1 - r0) * pow ((1 - cosine), 5); 58 | } 59 | 60 | struct RefractDataOut 61 | { 62 | bool is_refracted = false; 63 | Vec3 refracted; 64 | }; 65 | 66 | constexpr RefractDataOut refract_func (Vec3 const& v, Vec3 const& n, float ni_over_nt) 67 | { 68 | Vec3 uv = normalize (v); 69 | float dt = dot (uv, n); 70 | float discriminant = 1.0 - ni_over_nt * ni_over_nt * (1 - dt * dt); 71 | if (discriminant > 0) 72 | { 73 | return { true, ni_over_nt * (uv - n * dt) - n * sqrt (discriminant) }; 74 | } 75 | else 76 | return { false }; 77 | } 78 | 79 | struct Dielectric : public Material 80 | { 81 | 82 | constexpr Dielectric (float ri) : Material (VEC3_ZERO), ref_idx (ri) {} 83 | constexpr virtual ScatterOut scatter (Vec3 point, Vec3 normal, UV uv, const Ray& r_in, PRNG& random) const 84 | { 85 | Vec3 outward_normal; 86 | Vec3 reflected = reflect (r_in.direction, normal); 87 | float ni_over_nt = 0; 88 | float reflect_prob = 0; 89 | float cosine = 0; 90 | 91 | if (dot (r_in.direction, normal) > 0) 92 | { 93 | outward_normal = -normal; 94 | ni_over_nt = ref_idx; 95 | cosine = ref_idx * dot (r_in.direction, normal) / r_in.direction.length (); 96 | } 97 | else 98 | { 99 | outward_normal = normal; 100 | ni_over_nt = 1.0 / ref_idx; 101 | } 102 | RefractDataOut refracted = refract_func (r_in.direction, outward_normal, ni_over_nt); 103 | if (refracted.is_refracted) 104 | { 105 | reflect_prob = schlick (cosine, ref_idx); 106 | return { true, 107 | Vec3 (1.0, 1.0, 1.0), 108 | Ray (point, reflected), 109 | reflect_prob, 110 | Ray (point, refracted.refracted) }; 111 | } 112 | else 113 | { 114 | return { true, Vec3 (1.0, 1.0, 1.0), Ray (point, reflected), 0.0 }; 115 | } 116 | } 117 | 118 | float ref_idx; 119 | }; 120 | 121 | struct DiffuseTex : public Material 122 | { 123 | constexpr DiffuseTex (Texture* tex) : Material (VEC3_ZERO) {} 124 | constexpr virtual ScatterOut scatter (Vec3 point, Vec3 normal, UV uv, const Ray& r_in, PRNG& random) const 125 | { 126 | return { .is_scattered = false, .attenuation = tex->sample (uv) }; 127 | } 128 | Texture* tex; 129 | }; 130 | } // namespace cert -------------------------------------------------------------------------------- /src/ray.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "util.h" 4 | #include "vec3.h" 5 | namespace cert 6 | { 7 | class Ray 8 | { 9 | public: 10 | constexpr Ray (const Vec3& origin, const Vec3& direction) 11 | : origin (origin), direction (normalize (direction)) 12 | { 13 | } 14 | constexpr Vec3 point_at_parameter (float t) const { return origin + direction * t; } 15 | 16 | Vec3 origin = VEC3_ZERO; 17 | Vec3 direction = VEC3_ONE; 18 | }; 19 | 20 | constexpr Vec3 random_in_unit_sphere (PRNG& random) 21 | { 22 | int count = 0; 23 | Vec3 p; 24 | do 25 | { 26 | count++; 27 | p = 2.0 * Vec3 (random.get_float (), random.get_float (), random.get_float ()) - Vec3 (1, 1, 1); 28 | } while (p.squared_length () >= 1.0 && count < 100); 29 | return p; 30 | } 31 | 32 | class AABB 33 | { 34 | public: 35 | AABB (Vec3 const& mininum, Vec3 const& maximum) : mininum (mininum), maximum (maximum) {} 36 | 37 | bool hit (Ray const& r, float tmin, float tmax) const 38 | { 39 | for (int a = 0; a < 3; a++) 40 | { 41 | float t0 = min ((mininum[a] - r.origin[a]) / r.direction[a], 42 | (maximum[a] - r.origin[a]) / r.direction[a]); 43 | float t1 = max ((mininum[a] - r.origin[a]) / r.direction[a], 44 | (maximum[a] - r.origin[a]) / r.direction[a]); 45 | tmin = max (t0, tmin); 46 | tmax = min (t1, tmax); 47 | if (tmax <= tmin) return false; 48 | } 49 | return true; 50 | } 51 | 52 | Vec3 mininum; 53 | Vec3 maximum; 54 | }; 55 | } -------------------------------------------------------------------------------- /src/raytracer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "vec3.h" 6 | 7 | #include "camera.h" 8 | #include "color.h" 9 | #include "image.h" 10 | #include "ray.h" 11 | #include "shape.h" 12 | #include "sphere.h" 13 | #include "world.h" 14 | 15 | constexpr int thread_count = 12; 16 | 17 | constexpr int width = 10 * thread_count; 18 | constexpr int height = 10 * thread_count; 19 | constexpr int samples = 3; // this value is squared! 20 | constexpr int max_bounces = 5; 21 | 22 | namespace cert 23 | { 24 | constexpr void setup_scene (World& world) 25 | { 26 | auto grey = world.add_material (new Lambertian (Vec3 (0.5, 0.5, 0.5))); 27 | world.add_shape (new Sphere (Vec3 (0, -1000, 0), 1000, grey)); 28 | 29 | int x_range = 3; 30 | int z_range = 3; 31 | for (int a = -x_range; a < x_range; a++) 32 | { 33 | for (int b = -z_range; b < z_range; b++) 34 | { 35 | float choose_mat = world.random.get_float (); 36 | Vec3 center (a + 0.9 * world.random.get_float (), 0.2, b + 0.9 * world.random.get_float ()); 37 | if ((center - Vec3 (4, 0.2, 0)).length () > 0.9) 38 | { 39 | if (choose_mat < 0.8) 40 | { // diffuse 41 | auto lambert = world.add_material ( 42 | new Lambertian (Vec3 (world.random.get_float () * world.random.get_float (), 43 | world.random.get_float () * world.random.get_float (), 44 | world.random.get_float () * world.random.get_float ()))); 45 | world.add_shape (new Sphere (center, 0.2, lambert)); 46 | } 47 | else if (choose_mat < 0.95) 48 | { // metal 49 | auto metal = world.add_material (new Metal (Vec3 (0.5 * (1 + world.random.get_float ()), 50 | 0.5 * (1 + world.random.get_float ()), 51 | 0.5 * (1 + world.random.get_float ())))); 52 | world.add_shape (new Sphere (center, 0.2, metal)); 53 | } 54 | // else 55 | //{ // glass 56 | // auto glass = world.add_material (new Dielectric (1.5)); 57 | // world.add_shape (new Sphere (center, 0.2, glass)); 58 | //} 59 | } 60 | } 61 | } 62 | 63 | 64 | 65 | auto col1 = world.add_material (new Metal (Vec3 (0.4, 1.0, 0.6))); 66 | auto col2 = world.add_material (new Metal (Vec3 (0.4, 0.2, 0.1))); 67 | auto col3 = world.add_material (new Metal (Vec3 (0.7, 0.6, 0.5))); 68 | auto glass = world.add_material (new Dielectric (1.5f)); 69 | 70 | world.add_shape (new Sphere (Vec3 (-2, 1, 0), 1.0, col1)); 71 | world.add_shape (new Sphere (Vec3 (0, 1, 0), 1.0, glass)); 72 | world.add_shape (new Sphere (Vec3 (2, 1, 0), 1.0, col3)); 73 | 74 | world.add_light (new PointLight (Vec3 (2, 4, 2), VEC3_ONE, 15.0f)); 75 | world.add_light (new PointLight (Vec3 (2, 4, -2), VEC3_ONE, 15.0f)); 76 | world.add_light (new PointLight (Vec3 (-2, 4, 2), VEC3_ONE, 15.0f)); 77 | world.add_light (new PointLight (Vec3 (-2, 4, -2), VEC3_ONE, 15.0f)); 78 | } 79 | 80 | constexpr void setup_simple_scene (World& world) 81 | { 82 | auto grey = world.add_material (new Lambertian (Vec3 (0.5, 0.5, 0.5))); 83 | auto col1 = world.add_material (new Lambertian (Vec3 (0.4, 1.0, 0.6))); 84 | 85 | world.add_shape (new Sphere (Vec3 (0, -1000, 0), 1000, grey)); 86 | world.add_shape (new Sphere (Vec3 (0, 1, 0), 1.0, col1)); 87 | 88 | world.add_light (new PointLight (Vec3 (1, 4, -2), VEC3_ONE, 10.0f)); 89 | world.add_light (new PointLight (Vec3 (-1, 3, 2), VEC3_ONE, 10.0f)); 90 | } 91 | 92 | constexpr Vec3 trace (const Ray& r, World& world, int depth) 93 | { 94 | HitRecord rec = world.hit (r, 0.001, std::numeric_limits::max ()); 95 | if (rec.hit) 96 | { 97 | if (depth > max_bounces) return VEC3_ZERO; 98 | 99 | auto light_contribution = world.light_hit (rec.p, rec.normal); 100 | 101 | ScatterOut out = rec.mat->scatter (rec.p, rec.normal, rec.uv, r, world.random); 102 | if (out.index >= 0.0f) 103 | { 104 | if (out.index >= 1.0f) 105 | { 106 | return trace (out.refracted, world, depth + 1); 107 | } 108 | else 109 | { 110 | Vec3 reflect_contrib = trace (out.scattered, world, depth + 1); 111 | Vec3 refract_contrib = trace (out.refracted, world, depth + 1); 112 | return out.index * reflect_contrib + (1.0 - out.index) * refract_contrib; 113 | } 114 | } 115 | else if (out.is_scattered) 116 | { 117 | return light_contribution * out.attenuation * trace (out.scattered, world, depth + 1); 118 | } 119 | else 120 | { 121 | return light_contribution * out.attenuation; 122 | } 123 | } 124 | else 125 | { 126 | return background_color (r.direction); 127 | } 128 | } 129 | 130 | template 131 | constexpr auto raytrace () 132 | { 133 | World world{}; 134 | // setup_simple_scene (world); 135 | setup_scene (world); 136 | Vec3 lookfrom{ 0, 1, 3 }; 137 | Vec3 lookat{ 0, 0, 0 }; 138 | Camera cam{ lookfrom, lookat, VEC3_UP, 90, static_cast (width) / static_cast (height) }; 139 | 140 | OutputImage framebuffer{}; 141 | for (int i = 0; i < x_size; i++) 142 | { 143 | for (int j = 0; j < y_size; j++) 144 | { 145 | Vec3 color (0, 0, 0); 146 | for (float sX = 0; sX < 1; sX += 1.0 / sample_count) 147 | { 148 | for (float sY = 0; sY < 1; sY += 1.0 / sample_count) 149 | { 150 | float u = float (i + x_offset + sX) / float (width); 151 | float v = float (j + y_offset + sY) / float (height); 152 | 153 | Ray r = cam.get_ray (u, v); 154 | color += trace (r, world, 0); 155 | } 156 | } 157 | color /= static_cast (sample_count * sample_count); 158 | 159 | framebuffer.set (i, j, vec_to_color (color)); 160 | } 161 | } 162 | return framebuffer; 163 | } 164 | } // namespace cert -------------------------------------------------------------------------------- /src/rect.h: -------------------------------------------------------------------------------- 1 | #include "shape.h" 2 | 3 | namespace cert 4 | { 5 | class Rect : public Shape 6 | { 7 | public: 8 | constexpr Rect () {} 9 | constexpr Rect (float _x0, float _x1, float _y0, float _y1, float _k, Material* mat) 10 | : x0 (_x0), x1 (_x1), y0 (_y0), y1 (_y1), k (_k), mp (mat){}; 11 | constexpr override HitRecord hit (const Ray& r, float t0, float t1) const; 12 | constexpr virtual bool bounding_box (float t0, float t1, aabb& box) const 13 | { 14 | box = aabb (Vec3 (x0, y0, k - 0.0001), Vec3 (x1, y1, k + 0.0001)); 15 | return true; 16 | } 17 | Material* mp; 18 | float x0, x1, y0, y1, k; 19 | }; 20 | 21 | HitRecord Rect::hit (const Ray& r, float t0, float t1) const 22 | { 23 | float t = (k - r.origin ().z) / r.direction ().z; 24 | if (t < t0 || t > t1) return false; 25 | float x = r.origin ().x + t * r.direction ().x; 26 | float y = r.origin ().y + t * r.direction ().y; 27 | if (x < x0 || x > x1 || y < y0 || y > y1) return false; 28 | HitRecord rec; 29 | rec.u = (x - x0) / (x1 - x0); 30 | rec.v = (y - y0) / (y1 - y0); 31 | rec.t = t; 32 | rec.mat_ptr = mp; 33 | rec.p = r.point_at_parameter (t); 34 | rec.normal = VEC3_FORWARD; 35 | return {}; 36 | } 37 | } // namespace cert -------------------------------------------------------------------------------- /src/runtime.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "raytracer.h" 5 | 6 | template void print_image (std::ofstream& ppm, T const& frame) 7 | { 8 | for (int i = 0; i < frame.size (); i++) 9 | { 10 | int r = static_cast (frame.at (i).r); 11 | int g = static_cast (frame.at (i).g); 12 | int b = static_cast (frame.at (i).b); 13 | 14 | ppm << r << " " << g << " " << b << "\n"; 15 | } 16 | } 17 | 18 | void runtime () 19 | { 20 | std::ofstream ppm ("output_runtime.ppm"); 21 | ppm << "P3\n" << width << " " << height << "\n255\n"; 22 | print_image (ppm, cert::raytrace ()); 23 | } 24 | 25 | int main (int argc, char** argv) 26 | { 27 | std::ofstream ppm ("output_runtime.ppm"); 28 | ppm << "P3\n" << width << " " << height << "\n255\n"; 29 | print_image (ppm, cert::raytrace ()); 30 | } -------------------------------------------------------------------------------- /src/shape.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "material.h" 4 | #include "ray.h" 5 | #include "vec3.h" 6 | 7 | namespace cert 8 | { 9 | struct HitRecord 10 | { 11 | bool hit; 12 | float t; 13 | Vec3 p; 14 | Vec3 normal; 15 | Material* mat; 16 | UV uv; 17 | }; // namespace certstructHitRecord 18 | 19 | 20 | struct Shape 21 | { 22 | constexpr Shape () {} 23 | constexpr Shape (Material* mat) : mat (mat) {} 24 | constexpr virtual ~Shape (); 25 | constexpr virtual bool intersects (const Ray& r, float t_min, float t_max) const = 0; 26 | constexpr virtual HitRecord hit (const Ray& r, float t_min, float t_max) const = 0; 27 | Material* mat = nullptr; 28 | }; 29 | 30 | constexpr Shape::~Shape () {} 31 | } // namespace cert -------------------------------------------------------------------------------- /src/sphere.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "shape.h" 4 | 5 | 6 | namespace cert 7 | { 8 | class Sphere : public Shape 9 | { 10 | public: 11 | constexpr Sphere () {} 12 | constexpr Sphere (Vec3 cen, float r, Material* mat) : Shape (mat), center (cen), radius (r) 13 | { 14 | this->mat = mat; 15 | }; 16 | 17 | constexpr virtual bool intersects (const Ray& r, float t_min, float t_max) const 18 | { 19 | Vec3 oc = r.origin - center; 20 | float a = dot (r.direction, r.direction); 21 | float b = dot (oc, r.direction); 22 | float c = dot (oc, oc) - radius * radius; 23 | float discriminant = b * b - a * c; 24 | if (discriminant <= 0) return false; 25 | float negative = (-b - sqrt (discriminant)) / a; 26 | if (negative < t_max && negative > t_min) 27 | { 28 | return true; 29 | } 30 | float positive = (-b + sqrt (discriminant)) / a; 31 | if (positive < t_max && positive > t_min) 32 | { 33 | return true; 34 | } 35 | return false; 36 | } 37 | 38 | constexpr virtual HitRecord hit (const Ray& r, float t_min, float t_max) const 39 | { 40 | Vec3 oc = r.origin - center; 41 | float a = dot (r.direction, r.direction); 42 | float b = dot (oc, r.direction); 43 | float c = dot (oc, oc) - radius * radius; 44 | float discriminant = b * b - a * c; 45 | if (discriminant > 0) 46 | { 47 | float negative = (-b - sqrt (discriminant)) / a; 48 | if (negative < t_max && negative > t_min) 49 | { 50 | HitRecord out{}; 51 | out.hit = true; 52 | out.t = negative; 53 | out.p = r.point_at_parameter (out.t); 54 | out.normal = normalize ((out.p - center)); 55 | out.mat = mat; 56 | return out; 57 | } 58 | float positive = (-b + sqrt (discriminant)) / a; 59 | if (positive < t_max && positive > t_min) 60 | { 61 | HitRecord out{}; 62 | out.hit = true; 63 | out.t = positive; 64 | out.p = r.point_at_parameter (out.t); 65 | out.normal = normalize ((out.p - center)); 66 | out.mat = mat; 67 | return out; 68 | } 69 | } 70 | return HitRecord{ false, 0.f, { 0.f, 0.f, 0.f }, { 0.f, 0.f, 0.f }, nullptr, { 0.f, 0.f } }; 71 | } 72 | Vec3 center = VEC3_ZERO; 73 | float radius = 1.0; 74 | }; 75 | } // namespace cert -------------------------------------------------------------------------------- /src/threads/thread_0.cpp: -------------------------------------------------------------------------------- 1 | #include "threads.h" 2 | 3 | constexpr cert::OutputImage frame = 4 | cert::raytrace (); 5 | 6 | cert::OutputImage get_frame_0 () { return frame; } 7 | -------------------------------------------------------------------------------- /src/threads/thread_1.cpp: -------------------------------------------------------------------------------- 1 | #include "threads.h" 2 | 3 | constexpr cert::OutputImage frame = 4 | cert::raytrace (); 5 | 6 | cert::OutputImage get_frame_1 () { return frame; } 7 | -------------------------------------------------------------------------------- /src/threads/thread_10.cpp: -------------------------------------------------------------------------------- 1 | #include "threads.h" 2 | 3 | constexpr cert::OutputImage frame = 4 | cert::raytrace (); 5 | 6 | cert::OutputImage get_frame_10 () { return frame; } 7 | -------------------------------------------------------------------------------- /src/threads/thread_11.cpp: -------------------------------------------------------------------------------- 1 | #include "threads.h" 2 | 3 | constexpr cert::OutputImage frame = 4 | cert::raytrace (); 5 | 6 | cert::OutputImage get_frame_11 () { return frame; } 7 | -------------------------------------------------------------------------------- /src/threads/thread_2.cpp: -------------------------------------------------------------------------------- 1 | #include "threads.h" 2 | 3 | constexpr cert::OutputImage frame = 4 | cert::raytrace (); 5 | 6 | cert::OutputImage get_frame_2 () { return frame; } 7 | -------------------------------------------------------------------------------- /src/threads/thread_3.cpp: -------------------------------------------------------------------------------- 1 | #include "threads.h" 2 | 3 | constexpr cert::OutputImage frame = 4 | cert::raytrace (); 5 | 6 | cert::OutputImage get_frame_3 () { return frame; } 7 | -------------------------------------------------------------------------------- /src/threads/thread_4.cpp: -------------------------------------------------------------------------------- 1 | #include "threads.h" 2 | 3 | constexpr cert::OutputImage frame = 4 | cert::raytrace (); 5 | 6 | cert::OutputImage get_frame_4 () { return frame; } 7 | -------------------------------------------------------------------------------- /src/threads/thread_5.cpp: -------------------------------------------------------------------------------- 1 | #include "threads.h" 2 | 3 | constexpr cert::OutputImage frame = 4 | cert::raytrace (); 5 | 6 | cert::OutputImage get_frame_5 () { return frame; } 7 | -------------------------------------------------------------------------------- /src/threads/thread_6.cpp: -------------------------------------------------------------------------------- 1 | #include "threads.h" 2 | 3 | constexpr cert::OutputImage frame = 4 | cert::raytrace (); 5 | 6 | cert::OutputImage get_frame_6 () { return frame; } 7 | -------------------------------------------------------------------------------- /src/threads/thread_7.cpp: -------------------------------------------------------------------------------- 1 | #include "threads.h" 2 | 3 | constexpr cert::OutputImage frame = 4 | cert::raytrace (); 5 | 6 | cert::OutputImage get_frame_7 () { return frame; } 7 | -------------------------------------------------------------------------------- /src/threads/thread_8.cpp: -------------------------------------------------------------------------------- 1 | #include "threads.h" 2 | 3 | constexpr cert::OutputImage frame = 4 | cert::raytrace (); 5 | 6 | cert::OutputImage get_frame_8 () { return frame; } 7 | -------------------------------------------------------------------------------- /src/threads/thread_9.cpp: -------------------------------------------------------------------------------- 1 | #include "threads.h" 2 | 3 | constexpr cert::OutputImage frame = 4 | cert::raytrace (); 5 | 6 | cert::OutputImage get_frame_9 () { return frame; } 7 | -------------------------------------------------------------------------------- /src/threads/threads.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../raytracer.h" 3 | 4 | extern cert::OutputImage get_frame_0 (); 5 | extern cert::OutputImage get_frame_1 (); 6 | extern cert::OutputImage get_frame_2 (); 7 | extern cert::OutputImage get_frame_3 (); 8 | extern cert::OutputImage get_frame_4 (); 9 | extern cert::OutputImage get_frame_5 (); 10 | extern cert::OutputImage get_frame_6 (); 11 | extern cert::OutputImage get_frame_7 (); 12 | extern cert::OutputImage get_frame_8 (); 13 | extern cert::OutputImage get_frame_9 (); 14 | extern cert::OutputImage get_frame_10 (); 15 | extern cert::OutputImage get_frame_11 (); 16 | -------------------------------------------------------------------------------- /src/unique_ptr.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace cert 4 | { 5 | 6 | template class UniquePtr 7 | { 8 | T* ptr; 9 | 10 | public: 11 | constexpr UniquePtr (T* ptr) : ptr (ptr) {} 12 | constexpr ~UniquePtr () { delete ptr; } 13 | constexpr UniquePtr (UniquePtr const& other) = delete; 14 | constexpr UniquePtr& operator= (UniquePtr const& other) = delete; 15 | constexpr UniquePtr (UniquePtr&& other) : ptr (other.ptr) { other.ptr = nullptr; }; 16 | constexpr UniquePtr& operator= (UniquePtr&& other) 17 | { 18 | ptr = other.ptr; 19 | other.ptr = nullptr; 20 | }; 21 | T* get () const { return ptr; } 22 | }; 23 | 24 | } // namespace cert -------------------------------------------------------------------------------- /src/util.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | namespace cert 10 | { 11 | 12 | constexpr float debug_float (float in) { return in / 0.0f; } 13 | 14 | template constexpr T const& min (const T& a, const T& b) { return (b < a) ? b : a; } 15 | template constexpr T const& max (const T& a, const T& b) { return (a < b) ? b : a; } 16 | template constexpr T const& clamp (const T& value, const T& min, const T& max) 17 | { 18 | return value > max ? max : (value < min ? min : value); 19 | } 20 | 21 | template constexpr T abs (T num) { return num >= 0 ? num : -num; } 22 | 23 | constexpr float pow (float x, int n); 24 | constexpr float pow_recursive (float x, int n) 25 | { 26 | if (n == 0) return 1.0; 27 | if (n == 1) return x; 28 | 29 | if (n > 1) 30 | { 31 | if (n & 1) 32 | { 33 | return x * pow (x, n - 1); 34 | } 35 | else 36 | { 37 | return pow (x, n / 2) * pow (x, n / 2); 38 | } 39 | } 40 | else 41 | { 42 | return 1.0 / pow (x, -n); 43 | } 44 | } 45 | 46 | constexpr float pow (float x, int n) 47 | { 48 | double left = x; 49 | double right = 1; 50 | 51 | if (n < 0) return 1 / (x * pow (x, -n - 1)); 52 | if (n == 0) return 1; 53 | 54 | while (n > 1) 55 | { 56 | if (n % 2 == 1) right *= left; 57 | left = left * left; 58 | n = n / 2; 59 | } 60 | 61 | return left * right; 62 | } 63 | 64 | struct PRNG 65 | { 66 | constexpr PRNG (int seed = 123456789) : seed (seed) {} 67 | 68 | constexpr int get_int () 69 | { 70 | seed = (1103515245 * seed + 12345) % std::numeric_limits::max (); 71 | return static_cast (seed); 72 | } 73 | constexpr float get_float () 74 | { 75 | seed = ((1103515245 * seed + 12345) % std::numeric_limits::max ()); 76 | return static_cast (seed) / static_cast (std::numeric_limits::max ()); 77 | } 78 | 79 | private: 80 | long seed; 81 | }; 82 | 83 | constexpr bool nearly_equal (const float a, 84 | const float b, 85 | float epsilon = 128 * std::numeric_limits::epsilon (), 86 | float relth = std::numeric_limits::min ()) 87 | // those defaults are arbitrary and could be removed 88 | { 89 | // assert (std::numeric_limits::epsilon () <= epsilon); 90 | // assert (epsilon < 1.f); 91 | 92 | if (a == b) return true; 93 | 94 | const float minus_ab = a - b; 95 | const float diff = minus_ab >= 0 ? minus_ab : -minus_ab; 96 | const float abs_a = a >= 0 ? a : -a; 97 | const float abs_b = b >= 0 ? b : -b; 98 | const float norm = (abs_a + abs_b) < std::numeric_limits::max () ? 99 | (abs_a + abs_b) : 100 | std::numeric_limits::max (); 101 | const float max_relth = relth > epsilon * norm ? relth : epsilon * norm; 102 | return diff < max_relth; 103 | } 104 | 105 | // Newton–Raphson method 106 | constexpr float sqrt (float x) 107 | { 108 | if (0 <= x && x < std::numeric_limits::infinity ()) 109 | { 110 | double curr = x; 111 | double prev = 0; 112 | while (!nearly_equal (curr, prev)) 113 | { 114 | prev = curr; 115 | curr = 0.5 * (curr + x / curr); 116 | } 117 | return curr; 118 | } 119 | else 120 | { 121 | return std::numeric_limits::quiet_NaN (); 122 | } 123 | } 124 | // old version 125 | /* 126 | constexpr float sqrt (float x) 127 | { 128 | float l = 1; 129 | float r = res; 130 | while (!nearly_equal (l, r)) 131 | { 132 | const auto mid = (r + l) / 2.f; 133 | if (mid * mid >= res) 134 | { 135 | r = mid; 136 | } 137 | else 138 | { 139 | l = mid + 1.f; 140 | } 141 | } 142 | return r; 143 | }*/ 144 | 145 | // Taken from http://brnz.org/hbr/?p=1518 146 | 147 | // Based on code from 148 | // https://graphics.stanford.edu/~seander/bithacks.html 149 | constexpr int count_leading_VEC3_VEC3_ZEROes (uint64_t v) 150 | { // clang-format off 151 | constexpr char bit_position[64] = { 152 | 0, 1, 2, 7, 3, 13, 8, 19, 4, 25, 14, 28, 9, 34, 20, 40, 153 | 5, 17, 26, 38, 15, 46, 29, 48, 10, 31, 35, 54, 21, 50, 41, 57, 154 | 63, 6, 12, 18, 24, 27, 33, 39, 16, 37, 45, 47, 30, 53, 49, 56, 155 | 62, 11, 23, 32, 36, 44, 52, 55, 61, 22, 43, 51, 60, 42, 59, 58 }; 156 | // clang-format on 157 | v |= v >> 1; // first round down to one less than a power of 2 158 | v |= v >> 2; 159 | v |= v >> 4; 160 | v |= v >> 8; 161 | v |= v >> 16; 162 | v |= v >> 32; 163 | v = (v >> 1) + 1; 164 | 165 | return 63 - bit_position[(v * 0x0218a392cd3d5dbf) >> 58]; // [3] 166 | } 167 | 168 | constexpr int32_t bits (float f) 169 | { 170 | if (f == 0.0f) 171 | return 0; // also matches -0.0f and gives wrong result 172 | else if (f == std::numeric_limits::infinity ()) 173 | return 0x7f800000; 174 | else if (f == -std::numeric_limits::infinity ()) 175 | return 0xff800000; 176 | else if (f != f) // NaN 177 | return 0x7fc00000; // This is my NaN... 178 | 179 | bool sign = f < 0.0f; 180 | float abs_f = sign ? -f : f; 181 | 182 | int exponent = 254; 183 | 184 | while (abs_f < 0x1p87f) 185 | { 186 | abs_f *= 0x1p41f; 187 | exponent -= 41; 188 | } 189 | 190 | uint64_t a = (uint64_t) (abs_f * 0x1p-64f); 191 | int lz = count_leading_VEC3_VEC3_ZEROes (a); 192 | exponent -= lz; 193 | 194 | if (exponent <= 0) 195 | { 196 | exponent = 0; 197 | lz = 8 - 1; 198 | } 199 | 200 | int32_t significand = (a << (lz + 1)) >> (64 - 23); // [3] 201 | return (sign << 31) | (exponent << 23) | significand; 202 | } 203 | 204 | constexpr float bits (int32_t i) 205 | { 206 | int sign = (i >> 31); 207 | int exponent = ((i & 0x7f800000) >> 23) - 127; 208 | int mantissa = (i & 0x007fffff); 209 | float out = sign == 1 ? 1.0 : -1.0; 210 | int negative = 0; 211 | while (exponent > 0) 212 | { 213 | out *= 2; 214 | exponent = exponent >> 1; 215 | } 216 | return out; 217 | } 218 | 219 | template 220 | typename std::enable_if<(sizeof (To) == sizeof (From)) && std::is_trivially_copyable::value && std::is_trivial::value, 221 | // this implementation requires that To is trivially default constructible 222 | To>::type 223 | // constexpr support needs compiler magic 224 | bit_cast (const From& src) noexcept 225 | { 226 | To dst; 227 | // need some UB free way to do memcpy at compile time 228 | std::memcpy (&dst, &src, sizeof (To)); 229 | return dst; 230 | } 231 | 232 | 233 | 234 | inline float fast_sqrt (float number) 235 | { 236 | int32_t i = 0; 237 | float x2, y; 238 | const float threehalfs = 1.5F; 239 | 240 | x2 = number * 0.5F; 241 | y = number; 242 | i = bit_cast (y); 243 | // i = *static_cast (&y); // evil floating point bit level hacking 244 | i = 0x5f3759df - (i >> 1); // what the fuck? 245 | // y = *static_cast (&i); 246 | y = bit_cast (i); 247 | y = y * (threehalfs - (x2 * y * y)); // 1st iteration 248 | // y = y * ( threehalfs - ( x2 * y * y ) ); // 2nd iteration, this can be removed 249 | 250 | return y; 251 | } 252 | 253 | namespace detail 254 | { 255 | 256 | constexpr bool feq (float x, float y) 257 | { 258 | return abs (x - y) <= std::numeric_limits::epsilon (); 259 | } 260 | 261 | constexpr float trig_series (float x, float sum, float n, int i, int s, float t) 262 | { 263 | return feq (sum, sum + t * s / n) ? 264 | sum : 265 | trig_series (x, sum + t * s / n, n * i * (i + 1), i + 2, -s, t * x * x); 266 | } 267 | } // namespace detail 268 | constexpr float sin (float x) 269 | { 270 | float sum = x; 271 | float n = 6.0f; 272 | int i = 4; 273 | int s = 1; 274 | float t = x * x * x; 275 | 276 | bool done = detail::feq (sum, sum + t * s / n); 277 | while (!done) 278 | { 279 | sum += t * s / n; 280 | n *= i * (i + 1); 281 | i += 2; 282 | s = -s; 283 | t *= x * x; 284 | done = detail::feq (sum, sum + t * s / n); 285 | } 286 | return sum; 287 | // return trig_series (x, x, 6.0f, 4, -1, x * x * x); 288 | } 289 | constexpr float cos (float x) 290 | { 291 | float sum = 1.0f; 292 | float n = 2.0f; 293 | int i = 3; 294 | int s = -1; 295 | float t = x * x; 296 | 297 | bool done = detail::feq (sum, sum + t * s / n); 298 | while (!done) 299 | { 300 | sum += t * s / n; 301 | n *= i * (i + 1); 302 | i += 2; 303 | s = -s; 304 | t *= x * x; 305 | done = detail::feq (sum, sum + t * s / n); 306 | } 307 | return sum; 308 | // return trig_series (x, 1.0f, 2.0f, 3, -1, x * x); 309 | } 310 | constexpr float tan (float x) { return sin (x) / cos (x); } 311 | 312 | struct UV 313 | { 314 | float u = 0.f; 315 | float v = 0.f; 316 | }; 317 | 318 | 319 | } // namespace cert -------------------------------------------------------------------------------- /src/vec3.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "util.h" 6 | 7 | namespace cert 8 | { 9 | struct Vec3 10 | { 11 | float x = 0; 12 | float y = 0; 13 | float z = 0; 14 | 15 | constexpr Vec3 () : x (0), y (0), z (0) {} 16 | constexpr Vec3 (float x, float y, float z) : x (x), y (y), z (z) {} 17 | 18 | constexpr float operator[] (const int index) const 19 | { 20 | if (index == 0) return x; 21 | if (index == 1) return y; 22 | if (index == 2) return z; 23 | return std::numeric_limits::signaling_NaN (); 24 | } 25 | 26 | const Vec3& operator+ () const { return *this; } 27 | constexpr Vec3 operator- () const { return Vec3 (-x, -y, -z); } 28 | 29 | constexpr Vec3& operator+= (const Vec3& v2); 30 | constexpr Vec3& operator-= (const Vec3& v2); 31 | constexpr Vec3& operator*= (const Vec3& v2); 32 | constexpr Vec3& operator/= (const Vec3& v2); 33 | constexpr Vec3& operator*= (const float t); 34 | constexpr Vec3& operator/= (const float t); 35 | 36 | constexpr inline float length () const { return sqrt (x * x + y * y + z * z); } 37 | constexpr inline float squared_length () const { return x * x + y * y + z * z; } 38 | constexpr inline Vec3& norm () 39 | { 40 | float k = 1.0 / sqrt (x * x + y * y + z * z); 41 | x *= k; 42 | y *= k; 43 | z *= k; 44 | return *this; 45 | } 46 | }; 47 | 48 | constexpr Vec3 operator+ (const Vec3& v1, const Vec3& v2) 49 | { 50 | return Vec3 (v1.x + v2.x, v1.y + v2.y, v1.z + v2.z); 51 | } 52 | constexpr Vec3 operator+ (const Vec3& v1, const int& t) 53 | { 54 | return Vec3 (v1.x + t, v1.y + t, v1.z + t); 55 | } 56 | constexpr Vec3 operator- (const Vec3& v1, const Vec3& v2) 57 | { 58 | return Vec3 (v1.x - v2.x, v1.y - v2.y, v1.z - v2.z); 59 | } 60 | constexpr Vec3 operator* (const Vec3& v1, const Vec3& v2) 61 | { 62 | return Vec3 (v1.x * v2.x, v1.y * v2.y, v1.z * v2.z); 63 | } 64 | constexpr Vec3 operator/ (const Vec3& v1, const Vec3& v2) 65 | { 66 | return Vec3 (v1.x / v2.x, v1.y / v2.y, v1.z / v2.z); 67 | } 68 | constexpr Vec3 operator* (const Vec3& v1, const float t) 69 | { 70 | return Vec3 (v1.x * t, v1.y * t, v1.z * t); 71 | } 72 | constexpr Vec3 operator/ (const Vec3& v1, const float t) 73 | { 74 | return Vec3 (v1.x / t, v1.y / t, v1.z / t); 75 | } 76 | constexpr Vec3 operator* (const float t, const Vec3& v1) { return operator* (v1, t); } 77 | 78 | constexpr Vec3& Vec3::operator+= (const Vec3& v) 79 | { 80 | x += v.x; 81 | y += v.y; 82 | z += v.z; 83 | return *this; 84 | } 85 | 86 | constexpr Vec3& Vec3::operator-= (const Vec3& v) 87 | { 88 | x -= v.x; 89 | y -= v.y; 90 | z -= v.z; 91 | return *this; 92 | } 93 | 94 | constexpr Vec3& Vec3::operator*= (const Vec3& v) 95 | { 96 | x *= v.x; 97 | y *= v.y; 98 | z *= v.z; 99 | return *this; 100 | } 101 | 102 | constexpr Vec3& Vec3::operator*= (const float t) 103 | { 104 | x *= t; 105 | y *= t; 106 | z *= t; 107 | return *this; 108 | } 109 | 110 | constexpr Vec3& Vec3::operator/= (const Vec3& v) 111 | { 112 | x /= v.x; 113 | y /= v.y; 114 | z /= v.z; 115 | return *this; 116 | } 117 | 118 | constexpr Vec3& Vec3::operator/= (const float t) 119 | { 120 | float k = 1.0 / t; 121 | 122 | x *= k; 123 | y *= k; 124 | z *= k; 125 | return *this; 126 | } 127 | 128 | constexpr inline float dot (const Vec3& v1, const Vec3& v2) 129 | { 130 | return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z; 131 | } 132 | 133 | constexpr inline Vec3 cross (const Vec3& v1, const Vec3& v2) 134 | { 135 | return Vec3 (v1.y * v2.z - v1.z * v2.y, v1.z * v2.x - v1.x * v2.z, v1.x * v2.y - v1.y * v2.x); 136 | } 137 | 138 | constexpr Vec3 reflect (const Vec3& v, const Vec3& n) { return v - 2 * dot (v, n) * n; } 139 | 140 | constexpr inline Vec3 normalize (const Vec3& v) { return v / v.length (); } 141 | 142 | constexpr static Vec3 VEC3_ZERO = Vec3 (0.0f, 0.0f, 0.0f); 143 | constexpr static Vec3 VEC3_ONE = Vec3 (1.0f, 1.0f, 1.0f); 144 | constexpr static Vec3 VEC3_RIGHT = Vec3 (1.0f, 0.0f, 0.0f); 145 | constexpr static Vec3 VEC3_LEFT = Vec3 (-1.0f, 0.0f, 0.0f); 146 | constexpr static Vec3 VEC3_UP = Vec3 (0.0f, 1.0f, 0.0f); 147 | constexpr static Vec3 VEC3_DOWN = Vec3 (0.0f, -1.0f, 0.0f); 148 | constexpr static Vec3 VEC3_FORWARD = Vec3 (0.0f, 0.0f, 1.0f); 149 | constexpr static Vec3 VEC3_BACKWARD = Vec3 (0.0f, 0.0f, -1.0f); 150 | 151 | } // namespace cert -------------------------------------------------------------------------------- /src/world.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "dynamic_array.h" 4 | #include "light.h" 5 | #include "material.h" 6 | #include "ray.h" 7 | #include "shape.h" 8 | #include "util.h" 9 | 10 | namespace cert 11 | { 12 | constexpr Vec3 background_color (Vec3 direction) 13 | { 14 | Vec3 unit_direction = normalize (direction); 15 | float t = 0.5 * (unit_direction.y + 1.0); 16 | return (1.0 - t) * Vec3 (1.0, 1.0, 1.0) + t * Vec3 (0.5, 0.7, 1.0); 17 | } 18 | 19 | struct World 20 | { 21 | constexpr World () {} 22 | constexpr ~World () 23 | { 24 | for (int i = 0; i < shapes.size (); i++) 25 | { 26 | delete shapes.at (i); 27 | } 28 | for (int i = 0; i < materials.size (); i++) 29 | { 30 | delete materials.at (i); 31 | } 32 | for (int i = 0; i < lights.size (); i++) 33 | { 34 | delete lights.at (i); 35 | } 36 | } 37 | 38 | constexpr Shape* add_shape (Shape* shape) 39 | { 40 | shapes.push_back (shape); 41 | return shape; 42 | } 43 | constexpr Material* add_material (Material* mat) 44 | { 45 | materials.push_back (mat); 46 | return mat; 47 | } 48 | constexpr Light* add_light (Light* light) 49 | { 50 | lights.push_back (light); 51 | return light; 52 | } 53 | constexpr Texture* add_texture (Texture* texture) 54 | { 55 | textures.push_back (texture); 56 | return texture; 57 | } 58 | constexpr HitRecord hit (const Ray& r, float t_min, float t_max) const 59 | { 60 | HitRecord return_rec{}; 61 | return_rec.hit = false; 62 | float closest_so_far = t_max; 63 | for (int i = 0; i < shapes.size (); i++) 64 | { 65 | HitRecord temp_rec = shapes.at (i)->hit (r, t_min, closest_so_far); 66 | if (temp_rec.hit && temp_rec.t < closest_so_far) 67 | { 68 | closest_so_far = temp_rec.t; 69 | return_rec = temp_rec; 70 | } 71 | } 72 | return return_rec; 73 | } 74 | 75 | constexpr bool intersects (const Ray& r, float t_min, float t_max) const 76 | { 77 | for (int i = 0; i < shapes.size (); i++) 78 | { 79 | bool hit = shapes.at (i)->intersects (r, t_min, t_max); 80 | if (hit) return true; 81 | } 82 | return false; 83 | } 84 | 85 | 86 | constexpr Vec3 light_hit (Vec3 point, Vec3 normal) const 87 | { 88 | Vec3 attenuation = VEC3_ZERO; 89 | for (int i = 0; i < lights.size (); i++) 90 | { 91 | auto rec = lights.at (i)->visible (point, normal); 92 | if (rec.visible) 93 | { 94 | float distance = (lights.at (i)->position - point).length (); 95 | if (!intersects (lights.at (i)->ray_to_light (point), 0.001, distance)) 96 | { 97 | attenuation += rec.attenuation; 98 | } 99 | } 100 | } 101 | return attenuation; 102 | } 103 | 104 | PRNG random; 105 | 106 | private: 107 | DynamicArray shapes; 108 | DynamicArray materials; 109 | DynamicArray lights; 110 | DynamicArray textures; 111 | }; 112 | } // namespace cert -------------------------------------------------------------------------------- /test/test_main.cpp: -------------------------------------------------------------------------------- 1 | #include "test_ray.h" 2 | #include "test_util.h" 3 | #include "test_vec3.h" 4 | #include 5 | constexpr int run_tests () 6 | { 7 | run_util_test (); 8 | run_vec3_test (); 9 | run_ray_test (); 10 | return 0; 11 | } 12 | int main () 13 | { 14 | constexpr int res = run_tests (); 15 | return 0; 16 | } -------------------------------------------------------------------------------- /test/test_ray.h: -------------------------------------------------------------------------------- 1 | #include "../src/ray.h" 2 | 3 | constexpr void run_ray_test () {} -------------------------------------------------------------------------------- /test/test_util.h: -------------------------------------------------------------------------------- 1 | #include "../src/util.h" 2 | constexpr void run_util_test () 3 | { 4 | constexpr float p_1 = 0.1f; 5 | constexpr float p_2 = 0.2f; 6 | 7 | static_assert (cert::nearly_equal (p_1 + p_2, 0.3f)); 8 | static_assert (p_1 + p_2 == 0.3f); 9 | 10 | static_assert (cert::nearly_equal (0.0f, 0.00000000000000000000000000000000000001f)); 11 | static_assert (cert::nearly_equal (0.1f, 0.100003f)); 12 | static_assert (cert::nearly_equal (0.1f, 0.099997f)); 13 | static_assert (cert::nearly_equal (0.5f, 0.50001f)); 14 | static_assert (cert::nearly_equal (0.5f, 0.49999f)); 15 | static_assert (cert::nearly_equal (1.0f, 1.00003f)); 16 | static_assert (cert::nearly_equal (1.0f, 0.99997f)); 17 | static_assert (cert::nearly_equal (2.0f, 2.00006f)); 18 | static_assert (cert::nearly_equal (2.0f, 1.99994f)); 19 | static_assert (cert::nearly_equal (4.0f, 4.0001f)); 20 | static_assert (cert::nearly_equal (8.0f, 8.0002f)); 21 | static_assert (cert::nearly_equal (10.0f, 10.0002f)); 22 | static_assert (cert::nearly_equal (1000.0f, 1000.01f)); 23 | static_assert (cert::nearly_equal (100000.0f, 100002.f)); 24 | 25 | static_assert (cert::nearly_equal (-0.0f, -0.00000000000000000000000000000000000001f)); 26 | static_assert (cert::nearly_equal (-0.1f, -0.100003f)); 27 | static_assert (cert::nearly_equal (-0.1f, -0.099997f)); 28 | static_assert (cert::nearly_equal (-0.5f, -0.50001f)); 29 | static_assert (cert::nearly_equal (-0.5f, -0.49999f)); 30 | static_assert (cert::nearly_equal (-1.0f, -1.00003f)); 31 | static_assert (cert::nearly_equal (-1.0f, -0.99997f)); 32 | static_assert (cert::nearly_equal (-2.0f, -2.00006f)); 33 | static_assert (cert::nearly_equal (-2.0f, -1.99994f)); 34 | static_assert (cert::nearly_equal (-4.0f, -4.0001f)); 35 | static_assert (cert::nearly_equal (-8.0f, -8.0002f)); 36 | static_assert (cert::nearly_equal (-10.0f, -10.0002f)); 37 | static_assert (cert::nearly_equal (-1000.0f, -1000.01f)); 38 | static_assert (cert::nearly_equal (-100000.0f, -100002.f)); 39 | 40 | 41 | static_assert (cert::nearly_equal (cert::sqrt (1.0f), 1.0f)); 42 | constexpr float two_s = cert::sqrt (4.0f); 43 | static_assert (cert::nearly_equal (two_s, 2.0f)); 44 | constexpr float three_s = cert::sqrt (9.0f); 45 | static_assert (cert::nearly_equal (three_s, 3.0f)); 46 | constexpr float four_s = cert::sqrt (16.0f); 47 | static_assert (cert::nearly_equal (four_s, 4.0f)); 48 | constexpr float five_s = cert::sqrt (25.0f); 49 | static_assert (cert::nearly_equal (five_s, 5.0f)); 50 | 51 | static_assert (cert::nearly_equal (cert::pow (0.0, 3), 0)); 52 | static_assert (cert::nearly_equal (cert::pow (0.2, 3), 0.008)); 53 | static_assert (cert::nearly_equal (cert::pow (0.5, 3), 0.125)); 54 | static_assert (cert::nearly_equal (cert::pow (0.8, 3), 0.512)); 55 | static_assert (cert::nearly_equal (cert::pow (1.0, 3), 1.0)); 56 | static_assert (cert::nearly_equal (cert::pow (1.2, 3), 1.728)); 57 | 58 | static_assert (cert::nearly_equal (9.0f, cert::pow (3.0f, 2)), "cert::pow(3.0f, 2)"); 59 | static_assert (cert::nearly_equal (0.111111f, cert::pow (3.0f, -2)), "cert::pow(3.0f, -2)"); 60 | static_assert (cert::nearly_equal (1024.0, cert::pow (2, 10)), "cert::pow(2, 10)"); 61 | 62 | static_assert (cert::nearly_equal (cert::pow (-2, 1), -2), "cert::pow(-2, -3)"); 63 | static_assert (cert::nearly_equal (cert::pow (-2, 2), 4), "cert::pow(-2, -3)"); 64 | static_assert (cert::nearly_equal (cert::pow (-2, 3), -8), "cert::pow(-2, -3)"); 65 | static_assert (cert::nearly_equal (cert::pow (-2, -3), -0.125), "cert::pow(-2, -3)"); 66 | } -------------------------------------------------------------------------------- /test/test_vec3.h: -------------------------------------------------------------------------------- 1 | #include "../src/vec3.h" 2 | 3 | constexpr void run_vec3_test () 4 | { 5 | constexpr auto zero = cert::VEC3_ZERO; 6 | constexpr auto v_one = cert::VEC3_ONE; 7 | 8 | static_assert (zero.x == 0.0f && zero.y == 0.0f && zero.z == 0.0f); 9 | static_assert (v_one[0] == 1.0f && v_one[1] == 1.0f && v_one[2] == 1.0f); 10 | constexpr auto neg_one = -v_one; 11 | static_assert (neg_one.x == -1.0f && neg_one.y == -1.0f && neg_one.z == -1.0f); 12 | 13 | constexpr auto len_pre_v1 = cert::Vec3 (0, 3, 4); 14 | constexpr auto len_pre_v2 = cert::Vec3 (3, 0, 4); 15 | constexpr auto len_pre_v3 = cert::Vec3 (3, 4, 0); 16 | 17 | static_assert (len_pre_v1.squared_length () == 25.0f); // && len_pre_v1.length () == 5.0f); 18 | static_assert (len_pre_v2.squared_length () == 25.0f); // && len_pre_v2.length () == 5.0f); 19 | static_assert (len_pre_v3.squared_length () == 25.0f); // && len_pre_v3.length () == 5.0f); 20 | } --------------------------------------------------------------------------------