diff --git a/perf.data b/perf.data deleted file mode 100644 index 2796874..0000000 Binary files a/perf.data and /dev/null differ diff --git a/perf.data.old b/perf.data.old deleted file mode 100644 index 3090232..0000000 Binary files a/perf.data.old and /dev/null differ diff --git a/src/colour.h b/src/colour.h index 77ecfe7..6e07b9f 100644 --- a/src/colour.h +++ b/src/colour.h @@ -75,9 +75,14 @@ class Colour { }; constexpr Colour Colour::kBlack{0.0, 0.0, 0.0}; +constexpr Colour Colour::kWhite{1.0, 1.0, 1.0}; -constexpr Colour operator*(f64 t, Colour col) { return {t * col.r(), t * col.g(), t * col.b()}; } -constexpr Colour operator/(Colour col, f64 t) { return (1.0 / t) * col; } +constexpr Colour operator*(f64 t, Colour col) { + return {t * col.r(), t * col.g(), t * col.b()}; +} +constexpr Colour operator/(Colour col, f64 t) { + return (1.0 / t) * col; +} constexpr Colour operator+(Colour c1, Colour c2) { Colour out{c1}; diff --git a/src/main.cc b/src/main.cc index 3fac189..c96e20f 100644 --- a/src/main.cc +++ b/src/main.cc @@ -1,6 +1,5 @@ #include #include -#include #include "camera.h" #include "colour.h" @@ -26,21 +25,26 @@ int main(int /* argc */, char* /* argv */[]) { world.Add(std::move(ground)); world.Add(std::move(sphere)); */ - auto material_ground = std::make_shared(Colour(0.8, 0.8, 0.0)); - auto material_center = std::make_shared(Colour(0.7, 0.3, 0.3)); - auto material_left = std::make_shared(Colour(0.8, 0.8, 0.8), 0.05); - auto material_right = std::make_shared(Colour(0.8, 0.5, 0.4), 0.5); + auto mat_ground = std::make_shared(Colour(0.8, 0.8, 0.0)); + auto mat_lamb = std::make_shared(Colour(0.1, 0.2, 0.5)); + auto mat_metal = std::make_shared(Colour(0.8, 0.6, 0.2), 0.0); + auto mat_dielec = std::make_shared(1.5, 0.0); + auto mat_dielec2 = std::make_shared(0.66, 0.0); - world.Add(std::make_unique(Point3(0.0, -100.5, -1.0), 100.0, material_ground)); - world.Add(std::make_unique(Point3(0.0, 0.0, -1.0), 0.5, material_center)); - world.Add(std::make_unique(Point3(-1.0, 0.0, -1.0), 0.5, material_left)); - world.Add(std::make_unique(Point3(1.0, 0.0, -1.0), 0.5, material_right)); + world.Add(std::make_unique(Point3(0.0, -100.5, -1.0), 100.0, mat_ground)); + + world.Add(std::make_unique(Point3(0.0, 0.0, -1.0), 0.5, mat_lamb)); + + world.Add(std::make_unique(Point3(-1.0, 0.0, -1.0), 0.5, mat_dielec)); + world.Add(std::make_unique(Point3(-1.0, 0.0, -1.0), 0.4, mat_dielec2)); + + world.Add(std::make_unique(Point3(1.0, 0.0, -1.0), 0.5, mat_metal)); // camera constexpr f64 aspect_ratio = 16.0 / 9.0; - constexpr u32 image_width = 600; + constexpr u32 image_width = 800; 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 index 4fd22f3..c8fe960 100644 --- a/src/material.h +++ b/src/material.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -9,6 +10,7 @@ #include "ray.h" #include "raytracer.h" #include "renderobject.h" +#include "vec3.h" class Material { public: @@ -22,13 +24,10 @@ class Material { 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 { @@ -37,7 +36,7 @@ class Lambertian : public Material { std::optional> Scatter(const Ray& /* in */, const HitRecord& hit_record) const override { - auto scatter_dir = hit_record.normal + rand().GenOnUnitSphere(); + auto scatter_dir = hit_record.normal + RandomGen::GenInstance().GenOnUnitSphere(); if (scatter_dir.almost_zero()) { scatter_dir = hit_record.normal; @@ -60,7 +59,7 @@ class Metal : public Material { const HitRecord& hit_record) const override { auto reflect_dir = in.direction().reflect(hit_record.normal).normed(); - reflect_dir += fuzz_ * rand().GenOnUnitSphere(); + reflect_dir += fuzz_ * RandomGen::GenInstance().GenOnUnitSphere(); Ray scattered{hit_record.p, reflect_dir}; @@ -71,3 +70,44 @@ class Metal : public Material { Colour albedo_; f64 fuzz_; }; + +class Dielectric : public Material { + public: + constexpr explicit Dielectric(f64 eta, f64 fuzz) : eta_{eta}, fuzz_{fuzz} {} + + std::optional> Scatter(const Ray& in, + const HitRecord& hit_record) const override { + auto& rand = RandomGen::GenInstance(); + + f64 cos_theta = dot(-in.direction(), hit_record.normal); + f64 sin_theta = std::sqrt(1.0 - cos_theta * cos_theta); + + f64 eta = hit_record.front_face ? 1 / eta_ : eta_; + + Vec3 out_dir; + + constexpr auto reflectance = [](f64 cos, f64 eta) { + auto r0 = (1 - eta) / (1 + eta); + r0 = r0 * r0; + return r0 * (1 - r0) * std::pow(1 - cos, 5); + }; + + if (eta * sin_theta > 1.0 || reflectance(cos_theta, eta) > rand.GenUniform()) { + // can't refract, must reflect or Schlick approximation + out_dir = in.direction().reflect(hit_record.normal); + } else { + // reflect + out_dir = in.direction().refract(hit_record.normal, eta); + } + + out_dir += fuzz_ * rand.GenOnUnitSphere(); + + Ray out{hit_record.p, out_dir}; + + return std::make_tuple(Colour::kWhite, out); + } + + private: + f64 eta_; // index of refraction inside over outside + f64 fuzz_; +}; diff --git a/src/rand.h b/src/rand.h index 13b68da..4b1cdd9 100644 --- a/src/rand.h +++ b/src/rand.h @@ -7,6 +7,20 @@ class RandomGen { public: + constexpr RandomGen(const RandomGen&) = delete; + constexpr RandomGen& operator=(const RandomGen&) = delete; + + constexpr RandomGen(RandomGen&&) = delete; + constexpr RandomGen& operator=(RandomGen&&) = delete; + + constexpr ~RandomGen() = default; + + static RandomGen& GenInstance() { + static thread_local RandomGen rand{}; + + return rand; + } + constexpr u64 GenU64() { return philox_.Next(); } // constexpr u32 GenU32() { return static_cast(GenU64()); } @@ -43,5 +57,7 @@ class RandomGen { } private: + RandomGen() = default; + Philox philox_; }; diff --git a/src/ray.h b/src/ray.h index 67f7eb7..6f260d5 100644 --- a/src/ray.h +++ b/src/ray.h @@ -7,7 +7,8 @@ class Ray { public: constexpr Ray() = default; - constexpr Ray(const Point3& origin, const Vec3& direction) : orig_{origin}, dir_{direction} {} + constexpr Ray(const Point3& origin, const Vec3& direction) + : orig_{origin}, dir_{direction.normed()} {} Point3 origin() const { return orig_; } Vec3 direction() const { return dir_; } diff --git a/src/renderer.h b/src/renderer.h index 0843811..2fad7b7 100644 --- a/src/renderer.h +++ b/src/renderer.h @@ -1,7 +1,11 @@ #pragma once +#include + #include +#include #include +#include #include #include @@ -30,12 +34,12 @@ class Renderer { Image Render(const RenderObject& world) { Image img{camera_.image_width(), camera_.image_height()}; - constexpr u32 kNumThreads = 4; + constexpr u32 kNumThreads = 16; - auto render_thread = [this, &world, &img](u32 thread) { + std::atomic_uint32_t lines_done = 0; + + auto render_thread = [this, &world, &img, &lines_done](u32 thread) { for (u32 j = thread; j < img.height(); j += kNumThreads) { - std::clog << "\rWriting line " << j << " of " << img.height() << std::flush; - for (u32 i = 0; i < img.width(); i++) { Colour colour_sum{0.0, 0.0, 0.0}; @@ -47,6 +51,8 @@ class Renderer { img[i, j] = colour_sum / samples_per_pixel_; } + + lines_done.fetch_add(1); } }; @@ -58,21 +64,39 @@ class Renderer { render_threads[i] = std::thread{render_thread, i}; } + std::atomic_bool done = false; + + auto writer_thread = std::thread([&done, &lines_done, total_lines = img.height()] { + using std::literals::chrono_literals::operator""ms; + + while (!done.load()) { + std::cerr << "\rWriting line " << lines_done.load() << " of " << total_lines + << std::flush; + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + + std::clog << "\rDone " << newline; + }); + for (auto& t : render_threads) { t.join(); } - std::clog << "\rDone " << newline; + done.store(true); + + writer_thread.join(); return img; } private: - constexpr Ray SampleRay(u32 i, u32 j) { + Ray SampleRay(u32 i, u32 j) { auto pixel_centre = camera_.PixelToWorld(i, j); - Vec3 random_shift = rand_.GenUniform(-0.5, 0.5) * camera_.d_u_pixel() + - rand_.GenUniform(-0.5, 0.5) * camera_.d_v_pixel(); + auto& rand = RandomGen::GenInstance(); + + Vec3 random_shift = rand.GenUniform(-0.5, 0.5) * camera_.d_u_pixel() + + rand.GenUniform(-0.5, 0.5) * camera_.d_v_pixel(); pixel_centre += random_shift; @@ -86,7 +110,7 @@ class Renderer { // background if (!hit_record.has_value()) { - auto unit_dir = ray.direction().normed(); + auto unit_dir = ray.direction(); f64 a = 0.5 * (unit_dir.y() + 1.0); @@ -95,7 +119,7 @@ class Renderer { // hit - if (!hit_record->front_face || bounces == 0) { + if (bounces == 0) { return Colour::kBlack; } @@ -114,8 +138,6 @@ class Renderer { Camera camera_; - RandomGen rand_{}; - u32 samples_per_pixel_; u32 max_bounces_; }; diff --git a/src/vec3.h b/src/vec3.h index 35b304a..dc64ed1 100644 --- a/src/vec3.h +++ b/src/vec3.h @@ -73,6 +73,8 @@ class Vec3 { constexpr Vec3 reflect(Vec3 normal) const; + constexpr Vec3 refract(Vec3 normal, f64 eta) const; + private: std::array xyz_; }; @@ -93,7 +95,9 @@ constexpr Vec3 operator+(const Vec3& u, const Vec3& v) { return out; } -constexpr Vec3 operator-(const Vec3& u, const Vec3& v) { return u + (-v); } +constexpr Vec3 operator-(const Vec3& u, const Vec3& v) { + return u + (-v); +} constexpr Vec3 operator*(const Vec3& u, const Vec3& v) { return {u.x() * v.x(), u.y() * v.y(), u.z() * v.z()}; @@ -105,7 +109,9 @@ constexpr Vec3 operator*(f64 t, const Vec3& v) { return out; } -constexpr Vec3 operator/(const Vec3& v, f64 t) { return (1 / t) * v; } +constexpr Vec3 operator/(const Vec3& v, f64 t) { + return (1 / t) * v; +} constexpr f64 dot(const Vec3& u, const Vec3& v) { Vec3 tmp = u * v; @@ -117,10 +123,27 @@ constexpr Vec3 cross(const Vec3& u, const Vec3& v) { u.x() * v.y() - u.y() - v.x()}; } -constexpr f64 Vec3::squared() const { return dot(*this, *this); } +constexpr f64 Vec3::squared() const { + return dot(*this, *this); +} -constexpr f64 Vec3::norm() const { return std::sqrt(squared()); } +constexpr f64 Vec3::norm() const { + return std::sqrt(squared()); +} -constexpr Vec3 Vec3::normed() const { return *this / norm(); } +constexpr Vec3 Vec3::normed() const { + return *this / norm(); +} -constexpr Vec3 Vec3::reflect(Vec3 normal) const { return *this - 2 * dot(*this, normal) * normal; } +constexpr Vec3 Vec3::reflect(Vec3 normal) const { + return *this - 2 * dot(*this, normal) * normal; +} + +constexpr Vec3 Vec3::refract(Vec3 normal, f64 eta) const { + f64 cos_theta = dot(-*this, normal); + + Vec3 out_orth = eta * (*this + cos_theta * normal); + Vec3 out_par = -std::sqrt(std::fabs(1.0 - out_orth.squared())) * normal; + + return out_orth + out_par; +} diff --git a/subprojects/rand b/subprojects/rand index 3b8796e..8cba484 160000 --- a/subprojects/rand +++ b/subprojects/rand @@ -1 +1 @@ -Subproject commit 3b8796ee2775f3f1fe735a19c0f041ea144ae7b4 +Subproject commit 8cba4843cff103ae458b814d3ae0a4244a041710