added Materials

Lambertian: scatter ray randomly with absorption

Metal: reflect ray with absorption
This commit is contained in:
Moritz Gmeiner 2023-12-30 14:01:45 +01:00
commit 79869930cf
7 changed files with 92 additions and 38 deletions

View file

@ -27,7 +27,6 @@ if get_option('native')
endif endif
rand_proj = subproject('rand', default_options : ['warning_level=0', 'werror=false']) rand_proj = subproject('rand', default_options : ['warning_level=0', 'werror=false'])
rand_dep = rand_proj.get_variable('rand_dep') rand_dep = rand_proj.get_variable('rand_dep')

View file

@ -44,6 +44,14 @@ class Colour {
return *this; return *this;
} }
constexpr Colour& operator*=(const Colour& c) {
r_ *= c.r();
g_ *= c.g();
b_ *= c.b();
return *this;
}
constexpr Colour to_gamma2() const { constexpr Colour to_gamma2() const {
return Colour{std::sqrt(r()), std::sqrt(g()), std::sqrt(b())}; return Colour{std::sqrt(r()), std::sqrt(g()), std::sqrt(b())};
} }
@ -79,6 +87,14 @@ constexpr Colour operator+(Colour c1, Colour c2) {
return out; return out;
} }
constexpr Colour operator*(Colour c1, Colour c2) {
Colour out{c1};
out *= c2;
return out;
}
inline std::ostream& operator<<(std::ostream& os, Colour c) { inline std::ostream& operator<<(std::ostream& os, Colour c) {
double r = c.r(); double r = c.r();
double g = c.g(); double g = c.g();

View file

@ -1,5 +1,6 @@
#include <iostream> #include <iostream>
#include <memory> #include <memory>
#include <utility>
#include "camera.h" #include "camera.h"
#include "colour.h" #include "colour.h"
@ -16,10 +17,24 @@ int main(int /* argc */, char* /* argv */[]) {
RenderObjectList world; RenderObjectList world;
auto lamb = std::make_shared<Lambertian>(Colour{0.1, 0.4, 0.8}); /* auto lamb = std::make_shared<Lambertian>(Colour{0.7, 0.0, 0.5});
auto metal = std::make_shared<Metal>(Colour{0.3, 0.3, 0.3});
world.Add(std::make_unique<Sphere>(Vec3{0, -100.5, -1}, 100, lamb)); auto ground = std::make_unique<Sphere>(Point3{0, -100.5, -1}, 100, metal);
world.Add(std::make_unique<Sphere>(-Vec3::e_z, 0.5, lamb)); auto sphere = std::make_unique<Sphere>(-Vec3::e_z, 0.5, lamb);
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);
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));
// camera // camera

View file

@ -1,11 +1,13 @@
#pragma once #pragma once
#include <cassert>
#include <optional> #include <optional>
#include <tuple> #include <tuple>
#include "colour.h" #include "colour.h"
#include "rand.h" #include "rand.h"
#include "ray.h" #include "ray.h"
#include "raytracer.h"
#include "renderobject.h" #include "renderobject.h"
class Material { class Material {
@ -37,10 +39,9 @@ class Lambertian : public Material {
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 + rand().GenOnUnitSphere();
// TODO: enable if (scatter_dir.almost_zero()) {
/* if (scatter_dir.almost_zero()) {
scatter_dir = hit_record.normal; scatter_dir = hit_record.normal;
} */ }
Ray scattered{hit_record.p, scatter_dir}; Ray scattered{hit_record.p, scatter_dir};
@ -50,3 +51,23 @@ class Lambertian : public Material {
private: private:
Colour albedo_; Colour albedo_;
}; };
class Metal : public Material {
public:
constexpr Metal(Colour albedo, f64 fuzz) : albedo_{albedo}, fuzz_{fuzz} { assert(fuzz <= 1); }
std::optional<std::tuple<Colour, Ray>> Scatter(const Ray& in,
const HitRecord& hit_record) const override {
auto reflect_dir = in.direction().reflect(hit_record.normal).normed();
reflect_dir += fuzz_ * rand().GenOnUnitSphere();
Ray scattered{hit_record.p, reflect_dir};
return std::make_tuple(albedo_, scattered);
}
private:
Colour albedo_;
f64 fuzz_;
};

View file

@ -1,6 +1,6 @@
#pragma once #pragma once
#include <philox.h> #include <philox.hh>
#include "raytracer.h" #include "raytracer.h"
#include "vec3.h" #include "vec3.h"

View file

@ -76,7 +76,7 @@ class Renderer {
pixel_centre += random_shift; pixel_centre += random_shift;
auto ray_direction = pixel_centre - camera_.centre(); auto ray_direction = (pixel_centre - camera_.centre()).normed();
return Ray{camera_.centre(), ray_direction}; return Ray{camera_.centre(), ray_direction};
} }
@ -84,33 +84,32 @@ class Renderer {
constexpr Colour Cast(const Ray& ray, const RenderObject& world, u32 bounces) { constexpr Colour Cast(const Ray& ray, const RenderObject& world, u32 bounces) {
auto hit_record = world.hit(ray, Interval{0.001, kInf}); auto hit_record = world.hit(ray, Interval{0.001, kInf});
if (hit_record.has_value()) { // background
if (!hit_record->front_face || bounces == 0) { if (!hit_record.has_value()) {
return Colour::kBlack; auto unit_dir = ray.direction().normed();
}
/* Vec3 normal = hit_record->normal; f64 a = 0.5 * (unit_dir.y() + 1.0);
Vec3 new_origin = hit_record->p; return (1.0 - a) * Colour{1.0, 1.0, 1.0} + a * Colour{0.5, 0.7, 1.0};
// Vec3 new_direction = rand_.GenOnHemisphere(normal);
Vec3 new_direction = normal + rand_.GenOnUnitSphere();
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(); // hit
f64 a = 0.5 * (unit_dir.y() + 1.0); if (!hit_record->front_face || bounces == 0) {
return Colour::kBlack;
}
return (1.0 - a) * Colour{1.0, 1.0, 1.0} + a * Colour{0.5, 0.7, 1.0}; auto res = hit_record->mat->Scatter(ray, hit_record.value());
if (!res.has_value()) {
// absorped
return Colour::kBlack;
}
auto [albedo, out_ray] = res.value();
assert(bounces > 0);
return albedo * Cast(out_ray, world, bounces - 1);
} }
Camera camera_; Camera camera_;

View file

@ -65,15 +65,13 @@ class Vec3 {
return std::fabs(x()) < eps && std::fabs(y()) < eps && std::fabs(z()) < eps; 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 squared() const;
constexpr f64 norm() const { return std::sqrt(squared()); } constexpr f64 norm() const;
constexpr Vec3 normed() const { constexpr Vec3 normed() const;
auto r = *this;
r /= norm(); constexpr Vec3 reflect(Vec3 normal) const;
return r;
}
private: private:
std::array<f64, 3> xyz_; std::array<f64, 3> xyz_;
@ -119,4 +117,10 @@ 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 Vec3 normed(const Vec3& v) { return v.normed(); } constexpr f64 Vec3::squared() const { return dot(*this, *this); }
constexpr f64 Vec3::norm() const { return std::sqrt(squared()); }
constexpr Vec3 Vec3::normed() const { return *this / norm(); }
constexpr Vec3 Vec3::reflect(Vec3 normal) const { return *this - 2 * dot(*this, normal) * normal; }