2024-08-21 13:40:52 +02:00

112 lines
3.6 KiB
Zig

const vec3 = @Vector(3, f64);
const utils = @import("utils.zig");
const Interval = utils.Interval;
const toVec3 = utils.toVec3;
const hit = @import("hittable.zig");
const Sphere = hit.Sphere;
const Hittable = hit.Hittable;
const HittableList = hit.HittableList;
const HitRecord = hit.HitRecord;
const Ray = hit.Ray;
const mat_math = @import("mat_math.zig");
const near_zero = mat_math.near_zero;
const unit_vector = mat_math.unit_vector;
const random_unit_vector = mat_math.random_unit_vector;
const dot = mat_math.dot;
const length_squared = mat_math.length_squared;
const math = @import("std").math;
// Mother struct
pub const Material = union(enum) {
lambertian: Lambertian,
metal: Metal,
dielectric: Dielectric,
pub fn scatter(self: *const Material, ray_in: Ray, rec: *HitRecord, attenuation: *vec3, ray_scattered: *Ray) bool {
return switch (self.*) {
.lambertian => |s| s.scatter(rec, attenuation, ray_scattered),
.metal => |s| s.scatter(ray_in, rec, attenuation, ray_scattered),
.dielectric => |s| s.scatter(ray_in, rec, attenuation, ray_scattered),
};
}
};
// Materiaux
pub const Lambertian = struct {
albedo: vec3,
pub fn scatter(self: *const Lambertian, rec: *HitRecord, attenuation: *vec3, ray_scattered: *Ray) bool {
var scatter_direction = rec.*.normal + random_unit_vector();
if (near_zero(scatter_direction)) {
scatter_direction = rec.*.normal;
}
ray_scattered.* = Ray{ .orig = rec.*.p, .dir = scatter_direction };
attenuation.* = self.albedo;
return true;
}
};
pub const Metal = struct {
albedo: vec3,
fuzz: f64,
pub fn scatter(self: *const Metal, r_in: Ray, rec: *HitRecord, attenuation: *vec3, ray_scattered: *Ray) bool {
var reflected = reflect(r_in.dir, rec.*.normal);
reflected = unit_vector(reflected) + (toVec3(self.fuzz) * random_unit_vector());
ray_scattered.* = Ray{ .orig = rec.p, .dir = reflected };
attenuation.* = self.albedo;
return (dot(ray_scattered.dir, rec.normal) > 0);
}
};
pub const Dielectric = struct {
refraction_index: f64,
pub fn scatter(self: *const Dielectric, r_in: Ray, rec: *HitRecord, attenuation: *vec3, ray_scattered: *Ray) bool {
attenuation.* = vec3{ 1, 1, 1 };
const ri = if (rec.front_face) (1.0 / self.refraction_index) else self.refraction_index;
const unit_direction = unit_vector(r_in.dir);
const cos_theta = @min(dot(-unit_direction, rec.normal), 1.0);
const sin_theta = @sqrt(1.0 - cos_theta * cos_theta);
const cannot_refract = ri * sin_theta > 1.0;
var direction = vec3{ 0, 0, 0 };
if ((cannot_refract) and (reflectance(cos_theta, ri) > utils.rand_01())) {
direction = reflect(unit_direction, rec.normal);
} else {
direction = refract(unit_direction, rec.normal, ri);
}
ray_scattered.* = Ray{ .orig = rec.p, .dir = direction };
return true;
}
};
// Physics
fn reflect(v: vec3, n: vec3) vec3 {
return v - toVec3(2 * dot(v, n)) * n;
}
fn refract(uv: vec3, n: vec3, etai_over_etat: f64) vec3 {
const cos_theta = @min(dot(-uv, n), 1.0);
const r_out_perp = toVec3(etai_over_etat) * (uv + toVec3(cos_theta) * n);
const r_out_parallel = toVec3(-@sqrt(@abs(1.0 - length_squared(r_out_perp)))) * n;
return r_out_perp + r_out_parallel;
}
fn reflectance(cosine: f64, refraction_index: f64) f64 {
var r0 = (1 - refraction_index) / (1 + refraction_index);
r0 = r0 * r0;
return r0 + (1 - r0) * math.pow(f64, (1 - cosine), 5);
}