use super::{utility, Color, HitRecord, Ray, Vec3}; pub struct Lambertian { albedo: Color, } pub struct Metal { albedo: Color, fuzz: f64, } pub struct Mirror { albedo: Color, } pub struct Dielectric { ir: f64, } pub struct Rainbow { } pub trait Material: Sync + Send { fn scatter( &self, r_in: &Ray, rec: &HitRecord, attenuation: &mut Color, scattered: &mut Ray, ) -> bool; } impl Lambertian { pub fn new(a: &Color) -> Self { Lambertian { albedo: a.clone() } } } impl Material for Lambertian { fn scatter( &self, _r_in: &Ray, rec: &HitRecord, attenuation: &mut Color, scattered: &mut Ray, ) -> bool { let mut scatter_direction = rec.normal + Vec3::random_unit_vector(); if scatter_direction.near_zero() { scatter_direction = rec.normal; } *scattered = Ray::new(rec.p, scatter_direction); *attenuation = self.albedo.clone(); return true; } } impl Metal { pub fn new(a: &Color, f: f64) -> Self { Metal { albedo: *a, fuzz: f, } } } impl Material for Metal { fn scatter( &self, r_in: &Ray, rec: &HitRecord, attenuation: &mut Color, scattered: &mut Ray, ) -> bool { let reflected = Vec3::reflect(&Vec3::unit_vector(r_in.direction()), &rec.normal); *scattered = Ray::new(rec.p, reflected + self.fuzz * Vec3::random_in_unit_sphere()); *attenuation = self.albedo.clone(); return Vec3::dot(scattered.direction(), rec.normal) > 0.0; } } impl Mirror { pub fn new(a: &Color) -> Self { Mirror { albedo: *a } } } impl Material for Mirror { fn scatter( &self, r_in: &Ray, rec: &HitRecord, attenuation: &mut Color, scattered: &mut Ray, ) -> bool { if utility::random_f64() > 0.8 { // Reflektiert let reflected = Vec3::reflect(&Vec3::unit_vector(r_in.direction()), &rec.normal); *scattered = Ray::new(rec.p, reflected); *attenuation = self.albedo.clone(); return Vec3::dot(scattered.direction(), rec.normal) > 0.0; } else { // Geht geradeaus durch let reflected = r_in.direction().clone(); *scattered = Ray::new(rec.p, reflected); *attenuation = self.albedo.clone(); return true; }; } } impl Dielectric { pub fn new(index_of_refraction: f64) -> Self { Dielectric { ir: index_of_refraction, } } fn reflectance(cosine: f64, ref_idx: f64) -> f64 { let r0 = ((1.0 - ref_idx) / (1.0 + ref_idx)).powi(2); return r0 + (1.0 - r0) * (1.0 - cosine).powi(5); } } impl Material for Dielectric { fn scatter( &self, r_in: &Ray, rec: &HitRecord, attenuation: &mut Color, scattered: &mut Ray, ) -> bool { *attenuation = Color::new(1.0, 1.0, 1.0); let refraction_ratio = if rec.front_face { 1.0 / self.ir } else { self.ir }; let unit_direction = Vec3::unit_vector(r_in.direction()); let cos_theta = Vec3::dot(-1.0 * unit_direction, rec.normal).min(1.0); let sin_theta = (1.0 - cos_theta * cos_theta).sqrt(); let mut direction = Vec3::refract(&unit_direction, &rec.normal, refraction_ratio); // can reflect if refraction_ratio * sin_theta > 1.0 || Dielectric::reflectance(cos_theta, refraction_ratio) > utility::random_f64() { // must reflect direction = Vec3::reflect(&unit_direction, &rec.normal); } *scattered = Ray::new(rec.p, direction); return true; } } impl Rainbow { pub fn new() -> Self { Rainbow {} } } impl Material for Rainbow { fn scatter( &self, r_in: &Ray, rec: &HitRecord, attenuation: &mut Color, scattered: &mut Ray, ) -> bool { let mut scatter_direction = rec.normal + Vec3::random_unit_vector(); if scatter_direction.near_zero() { scatter_direction = rec.normal; } *scattered = Ray::new(rec.p, scatter_direction); let color = 0.5*Color::new( rec.normal.x()+1.0, rec.normal.y()+1.0, rec.normal.z()+1.0 ); *attenuation = color; return true; } }