diff --git a/.gitmodules b/.gitmodules index 2baec20..d781ec1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ -[submodule "subprojects/philox"] - path = subprojects/philox - url = git@github.com:MorizzG/philox [submodule "subprojects/rand"] path = subprojects/rand url = git@github.com:MorizzG/rand diff --git a/meson.build b/meson.build index dbf7d53..350c2b6 100644 --- a/meson.build +++ b/meson.build @@ -12,9 +12,24 @@ add_project_arguments( language : 'cpp' ) +if get_option('native') + add_global_arguments( + '-march=native', -philox_proj = subproject('philox', default_options : ['warning_level=0', 'werror=false']) -philox_dep = philox_proj.get_variable('philox_dep') + language : 'c' + ) + + add_global_arguments( + '-march=native', + + language : 'cpp' + ) +endif + + + +rand_proj = subproject('rand', default_options : ['warning_level=0', 'werror=false']) +rand_dep = rand_proj.get_variable('rand_dep') sources = [] @@ -22,4 +37,4 @@ subdir('src') inc = include_directories('src') -executable('ray-tracer', sources, include_directories : inc, dependencies : [philox_dep]) +executable('ray-tracer', sources, include_directories : inc, dependencies : [rand_dep]) diff --git a/meson.options b/meson.options new file mode 100644 index 0000000..a909bad --- /dev/null +++ b/meson.options @@ -0,0 +1 @@ +option('native', type : 'boolean', value : true) diff --git a/perf.data b/perf.data new file mode 100644 index 0000000..2796874 Binary files /dev/null and b/perf.data differ diff --git a/perf.data.old b/perf.data.old new file mode 100644 index 0000000..3090232 Binary files /dev/null and b/perf.data.old differ diff --git a/src/colour.h b/src/colour.h index 47d57ee..c34d3f8 100644 --- a/src/colour.h +++ b/src/colour.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include @@ -43,6 +44,10 @@ class Colour { return *this; } + constexpr Colour to_gamma2() const { + return Colour{std::sqrt(r()), std::sqrt(g()), std::sqrt(b())}; + } + std::string to_string() const { std::stringstream ss; @@ -74,8 +79,12 @@ constexpr Colour operator+(Colour c1, Colour c2) { return out; } -inline std::ostream& operator<<(std::ostream& os, Colour colour) { - os << FToU8(colour.r()) << ' ' << FToU8(colour.g()) << ' ' << FToU8(colour.b()); +inline std::ostream& operator<<(std::ostream& os, Colour c) { + double r = c.r(); + double g = c.g(); + double b = c.b(); + + os << FToU8(r) << ' ' << FToU8(g) << ' ' << FToU8(b); return os; } diff --git a/src/image.h b/src/image.h index 39a4e92..79a3d86 100644 --- a/src/image.h +++ b/src/image.h @@ -52,18 +52,15 @@ constexpr std::ostream& operator<<(std::ostream& os, const Image& img) { os << "255" << newline; for (u32 j = 0; j < img.height(); j++) { - std::clog << "\rWriting line " << j << " of " << img.height() << std::flush; - for (u32 i = 0; i < img.width(); i++) { const Colour col = img[i, j]; col.CheckValid(); - os << col << newline; + // apply gamma 2 transform + os << col.to_gamma2() << newline; } } - std::clog << "\rDone " << newline; - return os; } diff --git a/src/main.cc b/src/main.cc index fc81e8c..3ed7bcf 100644 --- a/src/main.cc +++ b/src/main.cc @@ -2,10 +2,11 @@ #include #include "camera.h" +#include "colour.h" #include "image.h" +#include "material.h" #include "raytracer.h" #include "renderer.h" -#include "renderobject.h" #include "renderobjectlist.h" #include "sphere.h" #include "vec3.h" @@ -15,14 +16,16 @@ int main(int /* argc */, char* /* argv */[]) { RenderObjectList world; - world.Add(std::make_shared(Vec3{0, -100.5, -1}, 100)); - world.Add(std::make_shared(-Vec3::e_z, 0.5)); + auto lamb = std::make_shared(Colour{0.1, 0.4, 0.8}); + + world.Add(std::make_unique(Vec3{0, -100.5, -1}, 100, lamb)); + world.Add(std::make_unique(-Vec3::e_z, 0.5, lamb)); // camera constexpr f64 aspect_ratio = 16.0 / 9.0; - constexpr u32 image_width = 800; + constexpr u32 image_width = 600; constexpr auto image_height = static_cast(image_width / aspect_ratio); // constexpr u32 image_width = 1920; diff --git a/src/material.h b/src/material.h new file mode 100644 index 0000000..bbedc27 --- /dev/null +++ b/src/material.h @@ -0,0 +1,52 @@ +#pragma once + +#include +#include + +#include "colour.h" +#include "rand.h" +#include "ray.h" +#include "renderobject.h" + +class Material { + public: + constexpr Material() = default; + + constexpr Material(const Material&) = default; + Material& operator=(const Material&) = default; + + constexpr Material(Material&&) = default; + Material& operator=(Material&&) = default; + + virtual ~Material() = default; + + RandomGen& rand() const { return rand_; } + + virtual std::optional> Scatter(const Ray& in, + const HitRecord& hit_record) const = 0; + + private: + mutable RandomGen rand_{}; +}; + +class Lambertian : public Material { + public: + constexpr explicit Lambertian(Colour albedo) : albedo_{albedo} {} + + std::optional> Scatter(const Ray& /* in */, + const HitRecord& hit_record) const override { + auto scatter_dir = hit_record.normal + rand().GenOnUnitSphere(); + + // TODO: enable + /* if (scatter_dir.almost_zero()) { + scatter_dir = hit_record.normal; + } */ + + Ray scattered{hit_record.p, scatter_dir}; + + return std::make_tuple(albedo_, scattered); + } + + private: + Colour albedo_; +}; diff --git a/src/raytracer.h b/src/raytracer.h index 40fccf2..32f9d67 100644 --- a/src/raytracer.h +++ b/src/raytracer.h @@ -10,6 +10,8 @@ using u64 = uint64_t; using f64 = double; constexpr f64 kInf = std::numeric_limits::infinity(); +constexpr f64 kNan = std::numeric_limits::signaling_NaN(); + constexpr f64 kPi = std::numbers::pi_v; constexpr char newline = '\n'; diff --git a/src/renderer.h b/src/renderer.h index c534af5..4aab1ba 100644 --- a/src/renderer.h +++ b/src/renderer.h @@ -1,11 +1,15 @@ #pragma once +#include +#include #include +#include #include "camera.h" #include "colour.h" #include "image.h" #include "interval.h" +#include "material.h" #include "rand.h" #include "ray.h" #include "raytracer.h" @@ -14,28 +18,52 @@ class Renderer { public: - constexpr explicit Renderer(Camera camera, u32 samples_per_pixel = 10) - : camera_{camera}, samples_per_pixel_{samples_per_pixel} {} + constexpr explicit Renderer(Camera camera, u32 samples_per_pixel = 100, u32 max_bounces = 50) + : camera_{camera}, samples_per_pixel_{samples_per_pixel}, max_bounces_{max_bounces} {} - constexpr Image Render(const RenderObject& world) { + constexpr u32& samples_per_pixel() { return samples_per_pixel_; } + constexpr u32 samples_per_pixel() const { return samples_per_pixel_; } + + constexpr u32& max_bounces() { return max_bounces_; } + constexpr u32 max_bounces() const { return max_bounces_; } + + Image Render(const RenderObject& world) { Image img{camera_.image_width(), camera_.image_height()}; - for (u32 j = 0; j < img.height(); j++) { - std::clog << "\rWriting line " << j << " of " << img.height() << std::flush; + constexpr u32 kNumThreads = 4; - for (u32 i = 0; i < img.width(); i++) { - Colour colour_sum{0.0, 0.0, 0.0}; + auto render_thread = [this, &world, &img](u32 thread) { + for (u32 j = thread; j < img.height(); j += kNumThreads) { + std::clog << "\rWriting line " << j << " of " << img.height() << std::flush; - for (u32 num_sample = 0; num_sample < samples_per_pixel_; num_sample++) { - auto ray = SampleRay(i, j); + for (u32 i = 0; i < img.width(); i++) { + Colour colour_sum{0.0, 0.0, 0.0}; - colour_sum += Cast(ray, world); + for (u32 num_sample = 0; num_sample < samples_per_pixel_; num_sample++) { + auto ray = SampleRay(i, j); + + colour_sum += Cast(ray, world, max_bounces_); + } + + img[i, j] = colour_sum / samples_per_pixel_; } - - img[i, j] = colour_sum / samples_per_pixel_; } + }; + + // render_thread(0); + + std::array render_threads; + + for (u32 i = 0; i < kNumThreads; i++) { + render_threads[i] = std::thread{render_thread, i}; } + for (auto& t : render_threads) { + t.join(); + } + + std::clog << "\rDone " << newline; + return img; } @@ -53,22 +81,29 @@ class Renderer { return Ray{camera_.centre(), ray_direction}; } - constexpr Colour Cast(const Ray& ray, const RenderObject& world) { - auto hit_record = world.hit(ray, Interval::kPositive); + constexpr Colour Cast(const Ray& ray, const RenderObject& world, u32 bounces) { + auto hit_record = world.hit(ray, Interval{0.001, kInf}); if (hit_record.has_value()) { - Vec3 normal = hit_record->normal; - - if (!hit_record->front_face) { + if (!hit_record->front_face || bounces == 0) { return Colour::kBlack; } + /* Vec3 normal = hit_record->normal; + Vec3 new_origin = hit_record->p; - Vec3 new_direction = rand_.GenOnHemisphere(normal); - Ray new_ray{new_origin, new_direction}; + // Vec3 new_direction = rand_.GenOnHemisphere(normal); + Vec3 new_direction = normal + rand_.GenOnUnitSphere(); - return 0.5 * Cast(new_ray, world); + Ray new_ray{new_origin, new_direction}; */ + + auto res = hit_record->mat->Scatter(ray, hit_record.value()); + + auto [albedo, out_ray] = res.value(); + + assert(bounces > 0); + return 0.5 * Cast(out_ray, world, bounces - 1); } auto unit_dir = ray.direction().normed(); @@ -83,4 +118,5 @@ class Renderer { RandomGen rand_{}; u32 samples_per_pixel_; + u32 max_bounces_; }; diff --git a/src/renderobject.h b/src/renderobject.h index 70ab0c1..8829e6f 100644 --- a/src/renderobject.h +++ b/src/renderobject.h @@ -5,12 +5,17 @@ #include "interval.h" #include "ray.h" +#include "raytracer.h" #include "vec3.h" +class Material; + struct HitRecord { Point3 p; Vec3 normal; - f64 t = 0.0; + const Material* mat = nullptr; + + f64 t = kNan; bool front_face = true; }; diff --git a/src/renderobjectlist.h b/src/renderobjectlist.h index e716086..3597942 100644 --- a/src/renderobjectlist.h +++ b/src/renderobjectlist.h @@ -1,18 +1,21 @@ #pragma once #include +#include #include +#include #include #include "interval.h" #include "ray.h" +#include "raytracer.h" #include "renderobject.h" class RenderObjectList : public RenderObject { public: constexpr RenderObjectList() = default; // explicit RenderObjectList(const SharedRenderObject& obj) { Append(obj); } - RenderObjectList(std::initializer_list objs) : objs_{objs} {} + // RenderObjectList(std::initializer_list objs) : objs_{objs} {} constexpr auto begin() { return objs_.begin(); } constexpr auto end() { return objs_.end(); } @@ -20,7 +23,7 @@ class RenderObjectList : public RenderObject { constexpr auto begin() const { return objs_.begin(); } constexpr auto end() const { return objs_.end(); } - void Add(const SharedRenderObject& obj) { objs_.push_back(obj); } + void Add(std::unique_ptr obj) { objs_.emplace_back(std::move(obj)); } void Clear() { objs_.clear(); } @@ -43,5 +46,5 @@ class RenderObjectList : public RenderObject { } private: - std::vector objs_; + std::vector> objs_; }; diff --git a/src/sphere.h b/src/sphere.h index 852dd5f..f968b76 100644 --- a/src/sphere.h +++ b/src/sphere.h @@ -2,9 +2,11 @@ #include #include +#include #include #include "interval.h" +#include "material.h" #include "ray.h" #include "raytracer.h" #include "renderobject.h" @@ -13,7 +15,9 @@ class Sphere : public RenderObject { public: constexpr Sphere() = default; - constexpr Sphere(Point3 centre, f64 radius) : centre_{centre}, radius_{radius} { + + Sphere(Point3 centre, f64 radius, const std::shared_ptr& mat) + : centre_{centre}, radius_{radius}, mat_{mat} { assert(radius >= 0); } @@ -21,7 +25,7 @@ class Sphere : public RenderObject { constexpr f64 radius() const { return radius_; } std::optional hit(const Ray& ray, Interval ts) const override { - HitRecord hit_record; + HitRecord hit_record{}; Vec3 oc = ray.origin() - centre_; @@ -66,6 +70,7 @@ class Sphere : public RenderObject { hit_record.t = t; hit_record.p = ray.At(t); + hit_record.mat = mat_.get(); if (hit_record.front_face) { hit_record.normal = (hit_record.p - centre_) / radius_; @@ -79,4 +84,6 @@ class Sphere : public RenderObject { private: Point3 centre_{}; f64 radius_ = 0.0; + + std::shared_ptr mat_; }; diff --git a/src/vec3.h b/src/vec3.h index 5d4794e..5f7520a 100644 --- a/src/vec3.h +++ b/src/vec3.h @@ -59,6 +59,12 @@ class Vec3 { return *this; } + constexpr bool almost_zero() const { + f64 eps = 1e-8; + + return std::fabs(x()) < eps && std::fabs(y()) < eps && std::fabs(z()) < eps; + } + constexpr f64 squared() const { return x() * x() + y() * y() + z() * z(); } constexpr f64 norm() const { return std::sqrt(squared()); } diff --git a/subprojects/philox b/subprojects/philox deleted file mode 160000 index cf69284..0000000 --- a/subprojects/philox +++ /dev/null @@ -1 +0,0 @@ -Subproject commit cf69284bd2d82b279c34fb1cf90543fd04b3a038 diff --git a/subprojects/rand b/subprojects/rand index 4d76806..8043050 160000 --- a/subprojects/rand +++ b/subprojects/rand @@ -1 +1 @@ -Subproject commit 4d7680624857241aca166f58b957712b0583fa05 +Subproject commit 8043050d3c871d241a1274be81bfaf1894f5edb2