added Dielectric (glass) material

This commit is contained in:
Moritz Gmeiner 2023-12-31 00:28:45 +01:00
commit 3b92603141
10 changed files with 148 additions and 37 deletions

BIN
perf.data

Binary file not shown.

Binary file not shown.

View file

@ -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};

View file

@ -1,6 +1,5 @@
#include <iostream>
#include <memory>
#include <utility>
#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<Lambertian>(Colour(0.8, 0.8, 0.0));
auto material_center = std::make_shared<Lambertian>(Colour(0.7, 0.3, 0.3));
auto material_left = std::make_shared<Metal>(Colour(0.8, 0.8, 0.8), 0.05);
auto material_right = std::make_shared<Metal>(Colour(0.8, 0.5, 0.4), 0.5);
auto mat_ground = std::make_shared<Lambertian>(Colour(0.8, 0.8, 0.0));
auto mat_lamb = std::make_shared<Lambertian>(Colour(0.1, 0.2, 0.5));
auto mat_metal = std::make_shared<Metal>(Colour(0.8, 0.6, 0.2), 0.0);
auto mat_dielec = std::make_shared<Dielectric>(1.5, 0.0);
auto mat_dielec2 = std::make_shared<Dielectric>(0.66, 0.0);
world.Add(std::make_unique<Sphere>(Point3(0.0, -100.5, -1.0), 100.0, material_ground));
world.Add(std::make_unique<Sphere>(Point3(0.0, 0.0, -1.0), 0.5, material_center));
world.Add(std::make_unique<Sphere>(Point3(-1.0, 0.0, -1.0), 0.5, material_left));
world.Add(std::make_unique<Sphere>(Point3(1.0, 0.0, -1.0), 0.5, material_right));
world.Add(std::make_unique<Sphere>(Point3(0.0, -100.5, -1.0), 100.0, mat_ground));
world.Add(std::make_unique<Sphere>(Point3(0.0, 0.0, -1.0), 0.5, mat_lamb));
world.Add(std::make_unique<Sphere>(Point3(-1.0, 0.0, -1.0), 0.5, mat_dielec));
world.Add(std::make_unique<Sphere>(Point3(-1.0, 0.0, -1.0), 0.4, mat_dielec2));
world.Add(std::make_unique<Sphere>(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<u32>(image_width / aspect_ratio);
// constexpr u32 image_width = 1920;

View file

@ -1,6 +1,7 @@
#pragma once
#include <cassert>
#include <cmath>
#include <optional>
#include <tuple>
@ -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<std::tuple<Colour, Ray>> 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<std::tuple<Colour, Ray>> 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<std::tuple<Colour, Ray>> 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_;
};

View file

@ -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<u32>(GenU64()); }
@ -43,5 +57,7 @@ class RandomGen {
}
private:
RandomGen() = default;
Philox philox_;
};

View file

@ -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_; }

View file

@ -1,7 +1,11 @@
#pragma once
#include <sys/sysinfo.h>
#include <array>
#include <atomic>
#include <cassert>
#include <chrono>
#include <iostream>
#include <thread>
@ -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_;
};

View file

@ -73,6 +73,8 @@ class Vec3 {
constexpr Vec3 reflect(Vec3 normal) const;
constexpr Vec3 refract(Vec3 normal, f64 eta) const;
private:
std::array<f64, 3> 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;
}

@ -1 +1 @@
Subproject commit 3b8796ee2775f3f1fe735a19c0f041ea144ae7b4
Subproject commit 8cba4843cff103ae458b814d3ae0a4244a041710