Ray class now contains some of the hit code

Rectangle class added
This commit is contained in:
Moritz Gmeiner 2024-01-04 12:44:22 +01:00
commit e1360466de
11 changed files with 455 additions and 135 deletions

65
src/2dshapes.h Normal file
View file

@ -0,0 +1,65 @@
#pragma once
#include <cassert>
#include <memory>
#include <optional>
#include <utility>
#include "interval.h"
#include "ray.h"
#include "raytracer.h"
#include "renderobject.h"
#include "vec3.h"
class Rectangle : public RenderObject {
public:
Rectangle() = default;
Rectangle(Point3 origin, Vec3 a, Vec3 b, std::shared_ptr<Material> mat)
: origin_{origin}, a_{a}, b_{b}, normal_{cross(a, b).normed()}, mat_{std::move(mat)} {
assert(is_zero(dot(a_, b_)));
}
std::optional<HitRecord> hit(const Ray& ray, Interval ts) const override {
auto t_maybe = ray.HitPlane(origin_, normal_);
if (!t_maybe.has_value()) {
return std::nullopt;
}
f64 t = t_maybe.value();
if (!ts.contains(t)) {
return std::nullopt;
}
Point3 p = ray.At(t);
f64 _a = dot(p - origin_, a_);
f64 _b = dot(p - origin_, b_);
if (_a <= 0.0 || _b <= 0.0 || _a >= a_.norm() || _b >= b_.norm()) {
return std::nullopt;
}
HitRecord hit_record;
hit_record.t = t;
hit_record.front_face = dot(normal_, ray.direction()) > 0;
hit_record.p = p;
hit_record.mat = mat_.get();
hit_record.normal = normal_;
return hit_record;
}
private:
Point3 origin_;
Vec3 a_;
Vec3 b_;
Vec3 normal_;
std::shared_ptr<Material> mat_;
};

View file

@ -1,5 +1,6 @@
#pragma once
#include <cassert>
#include <cmath>
#include "raytracer.h"
@ -7,39 +8,102 @@
class Camera {
public:
constexpr Camera(Point3 centre, u32 image_width, u32 image_height, f64 focal_length)
: centre_{centre},
focal_length_{focal_length},
image_width_{image_width},
image_height_{image_height} {
Recalculate();
constexpr Camera(Point3 centre, u32 image_width, u32 image_height)
: centre_{centre}, image_width_{image_width}, image_height_{image_height} {
Update();
}
// can be changed, need to recalculate parameters
// change paramters
constexpr Point3 centre() const { return centre_; }
constexpr void centre(Point3 centre) {
centre_ = centre;
Recalculate();
focal_length_ = (look_at_ - centre_).norm();
need_update_ = true;
}
constexpr Vec3 u() const { return u_; }
constexpr Vec3 v() const { return v_; }
constexpr Vec3 w() const { return w_; }
constexpr Point3 look_at() const { return look_at_; }
constexpr void look_at(Point3 look_at) {
look_at_ = look_at;
constexpr void look_at(Point3 look_at, Vec3 up = Vec3::e_y) {
w_ = (look_at - centre_).normed();
focal_length_ = (look_at_ - centre_).norm();
v_ = (up - dot(up, w_) * w_).normed();
u_ = cross(v_, w_);
focal_length_ = (look_at - centre_).norm();
Recalculate();
need_update_ = true;
}
constexpr Vec3 up() const { return up_; }
constexpr void up(Vec3 up) {
up_ = up;
need_update_ = true;
}
constexpr f64 fov() const { return fov_; }
constexpr void fov(f64 fov) {
fov_ = fov;
need_update_ = true;
}
constexpr f64 focal_length() const { return focal_length_; }
constexpr void focal_length(f64 focal_length) {
focal_length_ = focal_length;
need_update_ = true;
}
constexpr f64 defocus_angle() const { return defocus_angle_; }
constexpr void defocus_angle(f64 defocus_angle) { defocus_angle_ = defocus_angle; }
// const-only accessors
constexpr Vec3 u() const {
Update();
return u_;
}
constexpr Vec3 v() const {
Update();
return v_;
}
constexpr Vec3 w() const {
Update();
return w_;
}
constexpr Vec3 d_u_pixel() const {
Update();
return d_u_pixel_;
}
constexpr Vec3 d_v_pixel() const {
Update();
return d_v_pixel_;
}
constexpr u32 image_width() const { return image_width_; }
constexpr u32 image_height() const { return image_height_; }
constexpr Vec3 PixelToWorld(f64 i, f64 j) const {
Update();
return pixel_00_ + i * d_u_pixel_ + j * d_v_pixel_;
}
constexpr f64 defocus_radius() const {
return focal_length() * std::tan(deg2rad(defocus_angle()) / 2);
}
//
constexpr void rotate(f64 deg) {
Update();
f64 rad = deg2rad(deg);
Vec3 u = std::cos(rad) * u_ + std::sin(rad) * v_;
@ -47,37 +111,29 @@ class Camera {
u_ = u;
v_ = v;
Recalculate();
}
constexpr f64 fov_angle_v() const { return fov_deg_v_; }
constexpr void fov_angle_v(f64 fov) {
fov_deg_v_ = fov;
Recalculate();
}
// const-only accessors
constexpr Vec3 d_u_pixel() const { return d_u_pixel_; }
constexpr Vec3 d_v_pixel() const { return d_v_pixel_; }
constexpr u32 image_width() const { return image_width_; }
constexpr u32 image_height() const { return image_height_; }
constexpr Vec3 PixelToWorld(u32 i, u32 j) const {
return pixel_00_ + i * d_u_pixel_ + j * d_v_pixel_;
}
private:
constexpr void Recalculate() {
const f64 aspect_ratio = static_cast<f64>(image_width_) / image_height_;
constexpr void Update() const {
if (!need_update_) {
return;
}
f64 fov_rad_v = deg2rad(fov_deg_v_);
f64 h = std::tan(fov_rad_v / 2);
f64 aspect_ratio = static_cast<f64>(image_width_) / image_height_;
// constexpr f64 viewport_height = 2.0;
const f64 viewport_height = 2 * h * focal_length_;
w_ = -(look_at_ - centre_).normed();
v_ = (up_ - dot(up_, w_) * w_).normed();
u_ = cross(v_, w_);
assert(is_zero(dot(u_, v_)));
assert(is_zero(dot(u_, w_)));
assert(is_zero(dot(v_, w_)));
f64 h = focal_length_ * std::tan(deg2rad(fov_) / 2);
const f64 viewport_height = 2 * h;
f64 viewport_width = viewport_height * aspect_ratio;
@ -92,28 +148,38 @@ class Camera {
auto viewport_upper_left = centre_ - u_viewport / 2 - v_viewport / 2 - focal_length_ * w_;
pixel_00_ = viewport_upper_left + 0.5 * d_u_pixel_ + 0.5 * d_v_pixel_;
need_update_ = false;
}
// settable camera parameters
// fundamental camera parameters
Point3 centre_ = Vec3::origin;
Point3 centre_ = -Point3::e_z;
Point3 look_at_ = Point3::origin;
Vec3 up_ = Vec3::e_y;
Vec3 u_ = Vec3::e_x; // points to right of camera
Vec3 v_ = Vec3::e_y; // points to up of camera
Vec3 w_ = Vec3::e_z; // points backward of camera
f64 fov_ = 80;
f64 focal_length_;
f64 defocus_angle_ = 0;
f64 fov_deg_v_ = 80;
// saved camera properties, need to be recalculated after updating parameters
// saved camera properties
mutable bool need_update_ = true;
mutable Vec3 u_ = Vec3::e_x; // points to right of camera
mutable Vec3 v_ = Vec3::e_y; // points to up of camera
mutable Vec3 w_ = Vec3::e_z; // points backward of camera
mutable f64 focal_length_ = 1.0;
// position of pixel 0, 0
Vec3 pixel_00_;
mutable Vec3 pixel_00_;
// Vec3 pointing from pixel to right/lower neighbour resp.
Vec3 d_u_pixel_;
Vec3 d_v_pixel_;
mutable Vec3 d_u_pixel_;
mutable Vec3 d_v_pixel_;
// const
u32 image_width_;
u32 image_height_;

View file

@ -1,6 +1,7 @@
#include <iostream>
#include <memory>
#include "2dshapes.h"
#include "camera.h"
#include "colour.h"
#include "image.h"
@ -16,6 +17,8 @@ int main(int /* argc */, char* /* argv */[]) {
RenderObjectList world;
constexpr f64 focal_length = 5.0;
/* 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});
@ -26,25 +29,42 @@ int main(int /* argc */, char* /* argv */[]) {
world.Add(std::move(sphere)); */
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_lamb = std::make_shared<Lambertian>(Colour(1.0, 0.0, 0.0));
auto mat_metal = std::make_shared<Metal>(Colour(0.9, 0.4, 0.6), 0.0);
auto mat_dielec = std::make_shared<Dielectric>(1.5, 0.0);
auto mat_dielec2 = std::make_shared<Dielectric>(0.66, 0.0);
auto mat_metal2 = std::make_shared<Metal>(Colour(0.3, 0.5, 0.9), 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, mat_ground));
world.Add(std::make_unique<Sphere>(Point3(0, -100, 0), 100, mat_ground));
world.Add(std::make_unique<Sphere>(Point3(0.0, 0.0, -1.0), 0.5, mat_lamb));
world.Add(std::make_unique<Rectangle>(Point3(-2.5, -2.5, 50), 5 * Vec3::e_x, 5 * Vec3::e_y,
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));
/* constexpr i32 N = 5;
world.Add(std::make_unique<Sphere>(Point3(1.0, 0.0, -1.0), 0.5, mat_metal));
for (i32 x = -N; x <= N; x++) {
for (i32 y = 0; y <= N; y++) {
world.Add(std::make_unique<Rectangle>(Point3(x, y, 50), Vec3::e_x, Vec3::e_y,
(x + y) % 2 == 0 ? mat_lamb : mat_lamb2));
}
} */
// world.Add(std::make_unique<Sphere>(Point3(-1.0, 0.0, 0.0), 0.5, mat_dielec));
// world.Add(std::make_unique<Sphere>(Point3(-1.0, 0.0, 0.0), 0.4, mat_dielec2));
// world.Add(std::make_unique<Sphere>(Point3{-1, 0, 0}, 0.5, mat_metal2));
// world.Add(std::make_unique<Sphere>(Point3(1, 0, 0), 0.5, mat_metal));
/* for (f64 z = 0.5; z < 10; z += 0.5) {
world.Add(std::make_unique<Sphere>(Point3(1, 0, z), 1 * z / focal_length, mat_metal));
} */
// camera
constexpr f64 aspect_ratio = 16.0 / 9.0;
constexpr u32 image_width = 800;
constexpr u32 image_width = 1200;
constexpr auto image_height = static_cast<u32>(image_width / aspect_ratio);
// constexpr u32 image_width = 1920;
@ -52,21 +72,18 @@ int main(int /* argc */, char* /* argv */[]) {
static_assert(image_height >= 1);
constexpr f64 focal_length = 1.0;
constexpr Point3 camera_centre{0, 1, -1};
constexpr Point3 camera_centre{0.0, 0.0, 0.0};
Camera camera{camera_centre, image_width, image_height};
Camera camera{camera_centre, image_width, image_height, focal_length};
camera.look_at(Vec3{0, 0, 1});
camera.focal_length(focal_length);
std::clog << camera.u() << newline;
std::clog << camera.v() << newline;
std::clog << camera.w() << newline << newline;
camera.defocus_angle(1);
// camera.focal_length(1);
camera.look_at(Point3{0.8, 0.0, 1.0}, Vec3::e_x);
std::clog << camera.u() << newline;
std::clog << camera.v() << newline;
std::clog << camera.w() << newline << newline;
// camera.fov(40);
// camera.centre(Point3{-2, 2, 1});
// render

View file

@ -36,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 + RandomGen::GenInstance().GenOnUnitSphere();
auto scatter_dir = hit_record.normal + RandomGen::GenInstance().UnitSphereVec3();
if (scatter_dir.almost_zero()) {
scatter_dir = hit_record.normal;
@ -59,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_ * RandomGen::GenInstance().GenOnUnitSphere();
reflect_dir += fuzz_ * RandomGen::GenInstance().UnitSphereVec3();
Ray scattered{hit_record.p, reflect_dir};
@ -93,7 +93,7 @@ class Dielectric : public Material {
return r2 + (1 - r2) * std::pow(1 - cos, 5);
};
if (eta * sin_theta > 1.0 || reflectance(cos_theta, eta) > rand.GenUniform()) {
if (eta * sin_theta > 1.0 || reflectance(cos_theta, eta) > rand.Uniform()) {
// reflect
out_dir = in.direction().reflect(hit_record.normal);
} else {
@ -101,7 +101,7 @@ class Dielectric : public Material {
out_dir = in.direction().refract(hit_record.normal, eta);
}
out_dir += fuzz_ * rand.GenOnUnitSphere();
out_dir += fuzz_ * rand.UnitSphereVec3();
Ray out{hit_record.p, out_dir};

103
src/parallelepiped.h Normal file
View file

@ -0,0 +1,103 @@
#pragma once
#include <cassert>
#include <cmath>
#include <optional>
#include <utility>
#include "interval.h"
#include "ray.h"
#include "raytracer.h"
#include "renderobject.h"
#include "vec3.h"
class Parallelepiped : public RenderObject {
public:
constexpr Parallelepiped() = default;
constexpr Parallelepiped(Point3 centre, Vec3 u, Vec3 v, Vec3 w, f64 a = 1.0, f64 b = 1.0,
f64 c = 1.0)
: centre_{centre}, u_{u.normed()}, v_{v.normed()}, w_{w.normed()}, a_{a}, b_{b}, c_{c} {
assert(a >= 0);
assert(b >= 0);
assert(c >= 0);
// make sure all axes lie in unit cube
if (dot(u_, v_) < 0) {
v_ = -v_;
}
if (dot(u_, w_) < 0) {
w_ = -w_;
}
// make u, v, w are right-handed (not sure if neccessary, but why not)
if (dot(cross(u_, v_), w_) < 0.0) {
std::swap(v_, w_);
}
}
constexpr Vec3 u() const { return u_; }
constexpr Vec3 v() const { return v_; }
constexpr Vec3 w() const { return w_; }
constexpr f64 a() const { return a_; }
constexpr f64 b() const { return b_; }
constexpr f64 c() const { return c_; }
std::optional<HitRecord> hit(const Ray& ray, Interval ts) const override {
// check if ray intersects outsphere first
const f64 radius_ = (a_ * u_ + b_ * v_ + c_ * w_).norm() / 2;
Vec3 oc = ray.origin() - centre_;
f64 a = ray.direction().squared();
f64 b_half = dot(oc, ray.direction());
f64 c = oc.squared() - radius_ * radius_;
f64 discr = b_half * b_half - a * c;
if (discr < 0) {
// miss
return std::nullopt;
}
// since the sphere completely envelops the cuboid
// smaller of the ts that hit the sphere
f64 t = (-b_half - std::sqrt(discr)) / a;
if (t >= ts.max()) {
// if the smaller t is already too large, we don't need to check the second one since it
// will be even larger
return std::nullopt;
}
if (t <= ts.min()) {
// if the smaller t is too small, check the larger one
t = (-b_half + std::sqrt(discr)) / a;
if (!ts.surronds(t)) {
// larger t also out of range: return false
return std::nullopt;
}
}
}
private:
Point3 centre_;
// face normals
Vec3 u_;
Vec3 v_;
Vec3 w_;
// face distances
f64 a_ = 0.0;
f64 b_ = 0.0;
f64 c_ = 0.0;
};

View file

@ -21,22 +21,22 @@ class RandomGen {
return rand;
}
constexpr u64 GenU64() { return philox_.Next(); }
constexpr u64 U64() { return philox_.Next(); }
// constexpr u32 GenU32() { return static_cast<u32>(GenU64()); }
constexpr f64 GenUniform() { return philox_.NextF64(); }
constexpr f64 Uniform() { return philox_.NextF64(); }
constexpr f64 GenUniform(f64 min, f64 max) { return min + (max - min) * GenUniform(); }
constexpr f64 Uniform(f64 min, f64 max) { return min + (max - min) * Uniform(); }
constexpr Vec3 GenVec3() { return Vec3{GenUniform(), GenUniform(), GenUniform()}; }
constexpr Vec3 UniformVec3() { return Vec3{Uniform(), Uniform(), Uniform()}; }
constexpr Vec3 GenVec3(f64 min, f64 max) {
return Vec3{GenUniform(min, max), GenUniform(min, max), GenUniform(min, max)};
constexpr Vec3 UniformVec3(f64 min, f64 max) {
return Vec3{Uniform(min, max), Uniform(min, max), Uniform(min, max)};
}
constexpr Vec3 GenInUnitBall() {
constexpr Vec3 UnitBallVec3() {
while (true) {
Vec3 v = GenVec3(-1.0, 1.0);
Vec3 v = UniformVec3(-1.0, 1.0);
if (v.norm() < 1.0) {
return v;
@ -44,10 +44,10 @@ class RandomGen {
}
}
constexpr Vec3 GenOnUnitSphere() { return GenInUnitBall().normed(); }
constexpr Vec3 UnitSphereVec3() { return UnitBallVec3().normed(); }
constexpr Vec3 GenOnHemisphere(const Vec3& normal) {
Vec3 v = GenOnUnitSphere();
constexpr Vec3 HemisphereVec3(Vec3 normal) {
Vec3 v = UnitSphereVec3();
if (dot(v, normal) < 0) {
v = -v;
@ -56,6 +56,17 @@ class RandomGen {
return v;
}
constexpr Vec3 UnitDiskVec3(Vec3 normal) {
while (true) {
Vec3 v = UniformVec3();
v -= dot(v, normal) * normal;
if (v.norm() < 1) {
return v;
}
}
}
private:
RandomGen() = default;

View file

@ -1,5 +1,10 @@
#pragma once
#include <cmath>
#include <iostream>
#include <optional>
#include <utility>
#include "raytracer.h"
#include "vec3.h"
@ -7,15 +12,48 @@ class Ray {
public:
constexpr Ray() = default;
constexpr Ray(const Point3& origin, const Vec3& direction)
: orig_{origin}, dir_{direction.normed()} {}
constexpr Ray(const Point3& origin, Vec3 direction) : orig_{origin}, dir_{direction.normed()} {}
Point3 origin() const { return orig_; }
Vec3 direction() const { return dir_; }
constexpr Point3 At(f64 t) const { return orig_ + t * dir_; }
// return ts at which the ray intersects sphere or nullopt if no intersection
// ts may the the same if tangential
constexpr std::optional<std::pair<f64, f64>> HitSphere(Point3 centre, f64 radius) const {
Vec3 oc = origin() - centre;
f64 a = 1.0; // direction().squared() is always 1
f64 b_half = dot(oc, direction());
f64 c = oc.squared() - radius * radius;
f64 discr = b_half * b_half - a * c;
if (discr < 0) {
// miss
return std::nullopt;
}
// hit
return std::make_pair((-b_half - std::sqrt(discr)) / a, (-b_half + std::sqrt(discr)) / a);
}
// return t a which the ray intersects the plane containing `v` with normal `normal`
std::optional<f64> HitPlane(Point3 v, Vec3 normal) const {
// if the ray is parallel to the plane, return nullopt, even if the ray runs inside the
// plane!
if (is_zero(dot(direction(), normal))) {
return std::nullopt;
}
f64 t = dot(v - origin(), normal) / dot(direction(), normal);
return t;
}
private:
Point3 orig_;
Vec3 dir_;
Vec3 dir_; // unit vector
};

View file

@ -1,5 +1,7 @@
#pragma once
#include <cassert>
#include <cmath>
#include <cstdint>
#include <limits>
#include <numbers>
@ -7,6 +9,9 @@
using u32 = uint32_t;
using u64 = uint64_t;
using i32 = int32_t;
using i64 = int64_t;
using f64 = double;
constexpr f64 kInf = std::numeric_limits<f64>::infinity();
@ -16,6 +21,11 @@ constexpr f64 kPi = std::numbers::pi_v<f64>;
constexpr char newline = '\n';
constexpr u32 FToU8(f64 x) { return static_cast<uint16_t>(255.999 * x); }
constexpr u32 FToU8(f64 x) { return static_cast<u32>(255.999 * x); }
constexpr f64 deg2rad(f64 deg) { return deg * kPi / 180.0; }
constexpr bool is_zero(f64 x, f64 atol = 1e-5) {
assert(atol >= 0.0);
return std::fabs(x) < atol;
}

View file

@ -91,18 +91,22 @@ class Renderer {
private:
Ray SampleRay(u32 i, u32 j) {
auto pixel_centre = camera_.PixelToWorld(i, j);
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();
auto ray_origin = camera_.centre();
pixel_centre += random_shift;
/* if (camera_.defocus_angle() > 0) {
ray_origin += camera_.defocus_radius() * rand.UnitDiskVec3(camera_.w());
} */
auto ray_direction = (pixel_centre - camera_.centre()).normed();
f64 x_dist = rand.Uniform(-0.5, 0.5);
f64 y_dist = rand.Uniform(-0.5, 0.5);
return Ray{camera_.centre(), ray_direction};
auto pixel_centre = camera_.PixelToWorld(i + x_dist, j + y_dist);
auto ray_direction = pixel_centre - camera_.centre();
return Ray{ray_origin, ray_direction};
}
constexpr Colour Cast(const Ray& ray, const RenderObject& world, u32 bounces) {

View file

@ -1,7 +1,6 @@
#pragma once
#include <cassert>
#include <cmath>
#include <memory>
#include <optional>
@ -27,49 +26,50 @@ class Sphere : public RenderObject {
std::optional<HitRecord> hit(const Ray& ray, Interval ts) const override {
HitRecord hit_record{};
Vec3 oc = ray.origin() - centre_;
auto t_low_high = ray.HitSphere(centre_, radius_);
f64 a = ray.direction().squared();
f64 b_half = dot(oc, ray.direction());
f64 c = oc.squared() - radius_ * radius_;
f64 discr = b_half * b_half - a * c;
if (discr < 0) {
if (!t_low_high.has_value()) {
// miss
return std::nullopt;
}
// hit
auto [t_low, t_high] = t_low_high.value();
// smaller of the ts that hit the sphere
f64 t = (-b_half - std::sqrt(discr)) / a;
// we need to only distinguish 4 cases here:
// 1. t_low higher than interval automatically implies t_high is also higher than interval,
// so return
// 2. t_low is in the interval, so return the t_low hit (t_high doesn't matter)
// 3. t_low is too low, t_high is in interval, so return t_high
// 4. both t_low is too low and t_high is too high
if (t >= ts.max()) {
if (t_low >= ts.max()) {
// if the smaller t is already too large, we don't need to check the second one since it
// will be even larger
return std::nullopt;
}
if (t <= ts.min()) {
if (t_low > ts.min()) {
// t_min is hit
hit_record.t = t_low;
// hit_record.front_face = true;
} else {
// if the smaller t is too small, check the larger one
t = (-b_half + std::sqrt(discr)) / a;
if (!ts.surronds(t)) {
if (!ts.surronds(t_high)) {
// larger t also out of range: return false
return std::nullopt;
}
// smaller t hits front side, smaller t hits back side
// TODO: what if our hit t is less than 0? is that case even relevant?
// t_low hits front side, t_high hits back side
hit_record.t = t_high;
hit_record.front_face = false;
}
hit_record.t = t;
hit_record.p = ray.At(t);
hit_record.p = ray.At(hit_record.t);
hit_record.mat = mat_.get();
if (hit_record.front_face) {

View file

@ -39,7 +39,7 @@ class Vec3 {
return xyz_[i];
}
constexpr Vec3& operator+=(const Vec3& v) {
constexpr Vec3& operator+=(Vec3 v) {
x() += v.x();
y() += v.y();
z() += v.z();
@ -47,6 +47,14 @@ class Vec3 {
return *this;
}
constexpr Vec3& operator-=(Vec3 v) {
x() -= v.x();
y() -= v.y();
z() -= v.z();
return *this;
}
constexpr Vec3& operator*=(f64 t) {
x() *= t;
y() *= t;
@ -88,37 +96,35 @@ constexpr Vec3 Vec3::e_z{0.0, 0.0, 1.0};
using Point3 = Vec3;
constexpr std::ostream& operator<<(std::ostream& out, const Vec3& v) {
constexpr std::ostream& operator<<(std::ostream& out, Vec3 v) {
return out << v.x() << ' ' << v.y() << ' ' << v.z();
}
constexpr Vec3 operator+(const Vec3& u, const Vec3& v) {
constexpr Vec3 operator+(Vec3 u, Vec3 v) {
Vec3 out = u;
out += v;
return out;
}
constexpr Vec3 operator-(const Vec3& u, const Vec3& v) { return u + (-v); }
constexpr Vec3 operator-(Vec3 u, 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()};
}
constexpr Vec3 operator*(Vec3 u, Vec3 v) { return {u.x() * v.x(), u.y() * v.y(), u.z() * v.z()}; }
constexpr Vec3 operator*(f64 t, const Vec3& v) {
constexpr Vec3 operator*(f64 t, Vec3 v) {
Vec3 out = v;
out *= t;
return out;
}
constexpr Vec3 operator/(const Vec3& v, f64 t) { return (1 / t) * v; }
constexpr Vec3 operator/(Vec3 v, f64 t) { return (1 / t) * v; }
constexpr f64 dot(const Vec3& u, const Vec3& v) {
constexpr f64 dot(Vec3 u, Vec3 v) {
Vec3 tmp = u * v;
return tmp.x() + tmp.y() + tmp.z();
}
constexpr Vec3 cross(const Vec3& u, const Vec3& v) {
return {u.y() * v.z() - u.z() - v.y(), u.z() * v.x() - u.x() * v.z(),
constexpr Vec3 cross(Vec3 u, Vec3 v) {
return {u.y() * v.z() - u.z() * v.y(), u.z() * v.x() - u.x() * v.z(),
u.x() * v.y() - u.y() * v.x()};
}