use crate::hit::HitRecord;
use crate::ray::Ray;
use crate::vec3::{near_zero, random_unit_vec, reflect, Vec3};
use rand_xoshiro::Xoshiro256PlusPlus;
pub struct Scatter {
pub scattered: Ray,
pub attenuation: Vec3,
}
pub trait Material: Send + Sync {
fn scatter(
&self,
ray_in: &Ray,
rec: &HitRecord,
rng: &mut Xoshiro256PlusPlus,
) -> Option<Scatter>;
fn emitted(&self) -> Vec3 { Vec3::ZERO }
}
pub struct Lambertian {
pub albedo: Vec3,
}
impl Lambertian {
pub fn new(albedo: Vec3) -> Self { Self { albedo } }
}
impl Material for Lambertian {
fn scatter(
&self,
ray_in: &Ray,
rec: &HitRecord,
rng: &mut Xoshiro256PlusPlus,
) -> Option<Scatter> {
let mut dir = rec.normal + random_unit_vec(rng);
if near_zero(dir) { dir = rec.normal; }
let scattered = Ray::new(rec.p, dir, ray_in.path_length + rec.t);
Some(Scatter { scattered, attenuation: self.albedo })
}
}
pub struct Metal {
pub albedo: Vec3,
pub fuzz: f32,
}
impl Metal {
pub fn new(albedo: Vec3, fuzz: f32) -> Self {
Self { albedo, fuzz: fuzz.clamp(0.0, 1.0) }
}
}
impl Material for Metal {
fn scatter(
&self,
ray_in: &Ray,
rec: &HitRecord,
rng: &mut Xoshiro256PlusPlus,
) -> Option<Scatter> {
let reflected = reflect(ray_in.dir, rec.normal);
let dir = reflected + self.fuzz * random_unit_vec(rng);
if dir.dot(rec.normal) <= 0.0 { return None; }
let scattered = Ray::new(rec.p, dir, ray_in.path_length + rec.t);
Some(Scatter { scattered, attenuation: self.albedo })
}
}
pub struct DiffuseLight {
pub intensity: Vec3,
}
impl DiffuseLight {
pub fn new(intensity: Vec3) -> Self { Self { intensity } }
}
impl Material for DiffuseLight {
fn scatter(
&self,
_ray_in: &Ray,
_rec: &HitRecord,
_rng: &mut Xoshiro256PlusPlus,
) -> Option<Scatter> {
None
}
fn emitted(&self) -> Vec3 { self.intensity }
}