mirror of
https://github.com/MorizzG/ray-tracer.git
synced 2025-12-06 04:22:42 +00:00
added Dielectric (glass) material
This commit is contained in:
parent
79869930cf
commit
3b92603141
10 changed files with 148 additions and 37 deletions
BIN
perf.data
BIN
perf.data
Binary file not shown.
BIN
perf.data.old
BIN
perf.data.old
Binary file not shown.
|
|
@ -75,9 +75,14 @@ class Colour {
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr Colour Colour::kBlack{0.0, 0.0, 0.0};
|
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*(f64 t, Colour col) {
|
||||||
constexpr Colour operator/(Colour col, f64 t) { return (1.0 / t) * 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) {
|
constexpr Colour operator+(Colour c1, Colour c2) {
|
||||||
Colour out{c1};
|
Colour out{c1};
|
||||||
|
|
|
||||||
24
src/main.cc
24
src/main.cc
|
|
@ -1,6 +1,5 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
#include "camera.h"
|
#include "camera.h"
|
||||||
#include "colour.h"
|
#include "colour.h"
|
||||||
|
|
@ -26,21 +25,26 @@ int main(int /* argc */, char* /* argv */[]) {
|
||||||
world.Add(std::move(ground));
|
world.Add(std::move(ground));
|
||||||
world.Add(std::move(sphere)); */
|
world.Add(std::move(sphere)); */
|
||||||
|
|
||||||
auto material_ground = std::make_shared<Lambertian>(Colour(0.8, 0.8, 0.0));
|
auto mat_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 mat_lamb = std::make_shared<Lambertian>(Colour(0.1, 0.2, 0.5));
|
||||||
auto material_left = std::make_shared<Metal>(Colour(0.8, 0.8, 0.8), 0.05);
|
auto mat_metal = std::make_shared<Metal>(Colour(0.8, 0.6, 0.2), 0.0);
|
||||||
auto material_right = std::make_shared<Metal>(Colour(0.8, 0.5, 0.4), 0.5);
|
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, -100.5, -1.0), 100.0, mat_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(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, material_right));
|
|
||||||
|
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
|
// camera
|
||||||
|
|
||||||
constexpr f64 aspect_ratio = 16.0 / 9.0;
|
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 auto image_height = static_cast<u32>(image_width / aspect_ratio);
|
||||||
|
|
||||||
// constexpr u32 image_width = 1920;
|
// constexpr u32 image_width = 1920;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#include <cmath>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
|
||||||
|
|
@ -9,6 +10,7 @@
|
||||||
#include "ray.h"
|
#include "ray.h"
|
||||||
#include "raytracer.h"
|
#include "raytracer.h"
|
||||||
#include "renderobject.h"
|
#include "renderobject.h"
|
||||||
|
#include "vec3.h"
|
||||||
|
|
||||||
class Material {
|
class Material {
|
||||||
public:
|
public:
|
||||||
|
|
@ -22,13 +24,10 @@ class Material {
|
||||||
|
|
||||||
virtual ~Material() = default;
|
virtual ~Material() = default;
|
||||||
|
|
||||||
RandomGen& rand() const { return rand_; }
|
|
||||||
|
|
||||||
virtual std::optional<std::tuple<Colour, Ray>> Scatter(const Ray& in,
|
virtual std::optional<std::tuple<Colour, Ray>> Scatter(const Ray& in,
|
||||||
const HitRecord& hit_record) const = 0;
|
const HitRecord& hit_record) const = 0;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
mutable RandomGen rand_{};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class Lambertian : public Material {
|
class Lambertian : public Material {
|
||||||
|
|
@ -37,7 +36,7 @@ class Lambertian : public Material {
|
||||||
|
|
||||||
std::optional<std::tuple<Colour, Ray>> Scatter(const Ray& /* in */,
|
std::optional<std::tuple<Colour, Ray>> Scatter(const Ray& /* in */,
|
||||||
const HitRecord& hit_record) const override {
|
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()) {
|
if (scatter_dir.almost_zero()) {
|
||||||
scatter_dir = hit_record.normal;
|
scatter_dir = hit_record.normal;
|
||||||
|
|
@ -60,7 +59,7 @@ class Metal : public Material {
|
||||||
const HitRecord& hit_record) const override {
|
const HitRecord& hit_record) const override {
|
||||||
auto reflect_dir = in.direction().reflect(hit_record.normal).normed();
|
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};
|
Ray scattered{hit_record.p, reflect_dir};
|
||||||
|
|
||||||
|
|
@ -71,3 +70,44 @@ class Metal : public Material {
|
||||||
Colour albedo_;
|
Colour albedo_;
|
||||||
f64 fuzz_;
|
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_;
|
||||||
|
};
|
||||||
|
|
|
||||||
16
src/rand.h
16
src/rand.h
|
|
@ -7,6 +7,20 @@
|
||||||
|
|
||||||
class RandomGen {
|
class RandomGen {
|
||||||
public:
|
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 u64 GenU64() { return philox_.Next(); }
|
||||||
// constexpr u32 GenU32() { return static_cast<u32>(GenU64()); }
|
// constexpr u32 GenU32() { return static_cast<u32>(GenU64()); }
|
||||||
|
|
||||||
|
|
@ -43,5 +57,7 @@ class RandomGen {
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
RandomGen() = default;
|
||||||
|
|
||||||
Philox philox_;
|
Philox philox_;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,8 @@ class Ray {
|
||||||
public:
|
public:
|
||||||
constexpr Ray() = default;
|
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_; }
|
Point3 origin() const { return orig_; }
|
||||||
Vec3 direction() const { return dir_; }
|
Vec3 direction() const { return dir_; }
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,11 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <sys/sysinfo.h>
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <atomic>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#include <chrono>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
|
|
@ -30,12 +34,12 @@ class Renderer {
|
||||||
Image Render(const RenderObject& world) {
|
Image Render(const RenderObject& world) {
|
||||||
Image img{camera_.image_width(), camera_.image_height()};
|
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) {
|
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++) {
|
for (u32 i = 0; i < img.width(); i++) {
|
||||||
Colour colour_sum{0.0, 0.0, 0.0};
|
Colour colour_sum{0.0, 0.0, 0.0};
|
||||||
|
|
||||||
|
|
@ -47,6 +51,8 @@ class Renderer {
|
||||||
|
|
||||||
img[i, j] = colour_sum / samples_per_pixel_;
|
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};
|
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) {
|
for (auto& t : render_threads) {
|
||||||
t.join();
|
t.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::clog << "\rDone " << newline;
|
done.store(true);
|
||||||
|
|
||||||
|
writer_thread.join();
|
||||||
|
|
||||||
return img;
|
return img;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
constexpr Ray SampleRay(u32 i, u32 j) {
|
Ray SampleRay(u32 i, u32 j) {
|
||||||
auto pixel_centre = camera_.PixelToWorld(i, j);
|
auto pixel_centre = camera_.PixelToWorld(i, j);
|
||||||
|
|
||||||
Vec3 random_shift = rand_.GenUniform(-0.5, 0.5) * camera_.d_u_pixel() +
|
auto& rand = RandomGen::GenInstance();
|
||||||
rand_.GenUniform(-0.5, 0.5) * camera_.d_v_pixel();
|
|
||||||
|
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;
|
pixel_centre += random_shift;
|
||||||
|
|
||||||
|
|
@ -86,7 +110,7 @@ class Renderer {
|
||||||
|
|
||||||
// background
|
// background
|
||||||
if (!hit_record.has_value()) {
|
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);
|
f64 a = 0.5 * (unit_dir.y() + 1.0);
|
||||||
|
|
||||||
|
|
@ -95,7 +119,7 @@ class Renderer {
|
||||||
|
|
||||||
// hit
|
// hit
|
||||||
|
|
||||||
if (!hit_record->front_face || bounces == 0) {
|
if (bounces == 0) {
|
||||||
return Colour::kBlack;
|
return Colour::kBlack;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -114,8 +138,6 @@ class Renderer {
|
||||||
|
|
||||||
Camera camera_;
|
Camera camera_;
|
||||||
|
|
||||||
RandomGen rand_{};
|
|
||||||
|
|
||||||
u32 samples_per_pixel_;
|
u32 samples_per_pixel_;
|
||||||
u32 max_bounces_;
|
u32 max_bounces_;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
35
src/vec3.h
35
src/vec3.h
|
|
@ -73,6 +73,8 @@ class Vec3 {
|
||||||
|
|
||||||
constexpr Vec3 reflect(Vec3 normal) const;
|
constexpr Vec3 reflect(Vec3 normal) const;
|
||||||
|
|
||||||
|
constexpr Vec3 refract(Vec3 normal, f64 eta) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::array<f64, 3> xyz_;
|
std::array<f64, 3> xyz_;
|
||||||
};
|
};
|
||||||
|
|
@ -93,7 +95,9 @@ constexpr Vec3 operator+(const Vec3& u, const Vec3& v) {
|
||||||
return out;
|
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) {
|
constexpr Vec3 operator*(const Vec3& u, const Vec3& v) {
|
||||||
return {u.x() * v.x(), u.y() * v.y(), u.z() * v.z()};
|
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;
|
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) {
|
constexpr f64 dot(const Vec3& u, const Vec3& v) {
|
||||||
Vec3 tmp = u * 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()};
|
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
|
||||||
Loading…
Add table
Add a link
Reference in a new issue