stocking

git clone https://git.bradleygannon.com/stocking.git
Log | Files | Refs

material.rs (3699B)


      1 use crate::{
      2     color::LinearColor, hittable::HitRecord, random::RandomNumberGenerator, ray::Ray, vec3::Vec3,
      3 };
      4 
      5 #[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd)]
      6 pub struct ScatteredRay {
      7     ray: Ray,
      8     attenuation: LinearColor,
      9 }
     10 
     11 impl ScatteredRay {
     12     pub const fn ray(&self) -> Ray {
     13         self.ray
     14     }
     15     pub const fn attenuation(&self) -> LinearColor {
     16         self.attenuation
     17     }
     18 }
     19 
     20 pub trait Material {
     21     fn scatter(
     22         &self,
     23         r_in: &Ray,
     24         hit: &HitRecord,
     25         rng: &mut RandomNumberGenerator,
     26     ) -> Option<ScatteredRay>;
     27 }
     28 
     29 #[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd)]
     30 pub struct Lambertian {
     31     albedo: LinearColor,
     32 }
     33 
     34 impl Lambertian {
     35     pub const fn new(albedo: LinearColor) -> Self {
     36         Lambertian { albedo }
     37     }
     38 }
     39 
     40 impl Material for Lambertian {
     41     fn scatter(
     42         &self,
     43         _r_in: &Ray,
     44         hit: &HitRecord,
     45         rng: &mut RandomNumberGenerator,
     46     ) -> Option<ScatteredRay> {
     47         let scatter_direction = hit.normal() + Vec3::random_unit(rng);
     48         Some(ScatteredRay {
     49             ray: Ray::new(
     50                 hit.point(),
     51                 if scatter_direction.near_zero() {
     52                     hit.normal()
     53                 } else {
     54                     scatter_direction
     55                 },
     56             ),
     57             attenuation: self.albedo,
     58         })
     59     }
     60 }
     61 
     62 #[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd)]
     63 pub struct Metal {
     64     albedo: LinearColor,
     65     fuzz: f64,
     66 }
     67 
     68 impl Metal {
     69     pub const fn new(albedo: LinearColor, fuzz: f64) -> Self {
     70         Metal {
     71             albedo,
     72             fuzz: fuzz.clamp(0., 1.),
     73         }
     74     }
     75 }
     76 
     77 impl Material for Metal {
     78     fn scatter(
     79         &self,
     80         r_in: &Ray,
     81         hit: &HitRecord,
     82         rng: &mut RandomNumberGenerator,
     83     ) -> Option<ScatteredRay> {
     84         let ray = Ray::new(
     85             hit.point(),
     86             r_in.direction().reflect(&hit.normal()).unit() + (self.fuzz * Vec3::random_unit(rng)),
     87         );
     88         if ray.direction().dot(&hit.normal()) > 0. {
     89             Some(ScatteredRay {
     90                 ray,
     91                 attenuation: self.albedo,
     92             })
     93         } else {
     94             None
     95         }
     96     }
     97 }
     98 
     99 #[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd)]
    100 pub struct Dielectric {
    101     refractive_index: f64,
    102 }
    103 
    104 impl Dielectric {
    105     pub const fn new(refractive_index: f64) -> Self {
    106         Dielectric { refractive_index }
    107     }
    108     fn reflectance(cosine: f64, refractive_index: f64) -> f64 {
    109         let r0 = ((1. - refractive_index) / (1. + refractive_index)).powi(2);
    110         r0 + (1. - r0) * (1. - cosine).powi(5)
    111     }
    112 }
    113 
    114 impl Material for Dielectric {
    115     fn scatter(
    116         &self,
    117         r_in: &Ray,
    118         hit: &HitRecord,
    119         rng: &mut RandomNumberGenerator,
    120     ) -> Option<ScatteredRay> {
    121         let refractive_index = if hit.front_face() {
    122             1. / self.refractive_index
    123         } else {
    124             self.refractive_index
    125         };
    126         let unit_direction = r_in.direction().unit();
    127         let cosine_theta = (-unit_direction).dot(&hit.normal()).min(1.);
    128         let sine_theta = (1. - cosine_theta * cosine_theta).sqrt();
    129         let cannot_refract = refractive_index * sine_theta > 1.;
    130         let direction = if cannot_refract
    131             || Dielectric::reflectance(cosine_theta, refractive_index) > rng.next_f64()
    132         {
    133             unit_direction.reflect(&hit.normal())
    134         } else {
    135             unit_direction.refract(&hit.normal(), refractive_index)
    136         };
    137         Some(ScatteredRay {
    138             ray: Ray::new(hit.point(), direction),
    139             attenuation: LinearColor::new(1., 1., 1.),
    140         })
    141     }
    142 }