use std::ops::Neg; use super::Material; use crate::ray::Ray; use crate::vec3::Color; use crate::{hittable::HitRecord, vec3::Vec3}; pub struct DielectricAttenuation { pub albedo: Color, pub constant: f64, } pub struct Dielectric { pub index_of_refraction: f64, pub attenuation: Option, } impl Dielectric { fn reflectance(cosine: f64, ref_idx: f64) -> f64 { // Using Schlick's Approximation: let mut r0 = (1.0 - ref_idx) / (1.0 + ref_idx); r0 *= r0; r0 + (1.0 - r0) * (1.0 - cosine).powi(5) } } impl Material for Dielectric { fn scatter( &self, ray_in: &Ray, hit_record: &HitRecord, attenuation: &mut Color, scattered: &mut Ray, ) -> bool { if let Some(props) = &self.attenuation { let outward_normal = if hit_record.front_face { hit_record.normal.clone() } else { -&hit_record.normal }; if outward_normal.dot(&ray_in.direction) > 0.0 { let distance = (&ray_in.origin - &hit_record.p).length(); let falloff_ratio = (props.constant * distance).neg().exp(); *attenuation = &props.albedo * falloff_ratio; } else { *attenuation = props.albedo.clone(); } } else { *attenuation = Color { x: 1.0, y: 1.0, z: 1.0, }; } let refraction_ratio = if hit_record.front_face { 1.0 / self.index_of_refraction } else { self.index_of_refraction }; let unit_direction = ray_in.direction.unit_vector(); let cos_theta = hit_record.normal.dot(&-&unit_direction).min(1.0); let sin_theta = (1.0 - cos_theta * cos_theta).sqrt(); let cannot_refract = refraction_ratio * sin_theta > 1.0; let direction: Vec3; if cannot_refract || Self::reflectance(cos_theta, refraction_ratio) > rand::random::() { direction = unit_direction.reflect(&hit_record.normal) } else { direction = unit_direction.refract(&hit_record.normal, refraction_ratio) } *scattered = Ray { origin: hit_record.p.clone(), direction, time: ray_in.time, }; true } }