diff options
Diffstat (limited to 'src/material.rs')
-rw-r--r-- | src/material.rs | 64 |
1 files changed, 62 insertions, 2 deletions
diff --git a/src/material.rs b/src/material.rs index 3cbbb4a..42e8d0c 100644 --- a/src/material.rs +++ b/src/material.rs @@ -1,8 +1,9 @@ -use cgmath::Vector3; +use cgmath::prelude::*; +use cgmath::{Vector3, vec3, dot}; use rand::prelude::*; use crate::ray::{Ray, NextRay}; use crate::hittable::Hit; -use crate::random::random_unit_vector; +use crate::utils::{random_unit_vector, random_in_unit_sphere, reflect, refract, schlick}; pub trait Material { fn scatter(&self, rng: &mut ThreadRng, ray: &Ray, hit: &Hit) -> Option<NextRay>; @@ -24,3 +25,62 @@ impl Material for Lambertian { Some(NextRay::new(self.albedo, Ray::new(hit.p, scatter_direction))) } } + +pub struct Metal { + pub albedo: Vector3<f64>, + pub fuzz: f64 +} + +impl Metal { + pub fn new(albedo: Vector3<f64>, f: f64) -> Metal { + let fuzz = if f < 1.0 { f } else { 1.0 }; + Metal { albedo, fuzz } + } +} + +impl Material for Metal { + fn scatter(&self, rng: &mut ThreadRng, ray: &Ray, hit: &Hit) -> Option<NextRay> { + let reflected = reflect(&ray.dir.normalize(), &hit.normal); + let scattered = Ray::new(hit.p, reflected + self.fuzz * random_in_unit_sphere(rng)); + if dot(scattered.dir, hit.normal) > 0.0 { + Some(NextRay::new(self.albedo, scattered)) + } else { + None + } + } +} + +pub struct Dielectric { + pub ref_idx: f64 +} + +impl Dielectric { + pub fn new(ref_idx: f64) -> Dielectric { + Dielectric { ref_idx } + } +} + +impl Material for Dielectric { + fn scatter(&self, rng: &mut ThreadRng, ray: &Ray, hit: &Hit) -> Option<NextRay> { + let attenuation = vec3(1.0, 1.0, 1.0); + let etai_over_etat = if hit.front_face { 1.0 / self.ref_idx } else { self.ref_idx }; + + let unit_direction = ray.dir.normalize(); + let cos_theta = f64::min(dot(-unit_direction, hit.normal), 1.0); + let sin_theta = (1.0 - cos_theta*cos_theta).sqrt(); + + if etai_over_etat * sin_theta > 1.0 { + let reflected = reflect(&unit_direction, &hit.normal); + return Some(NextRay::new(attenuation, Ray::new(hit.p, reflected))) + } + + let reflect_prob = schlick(cos_theta, etai_over_etat); + if rng.gen::<f64>() < reflect_prob { + let reflected = reflect(&unit_direction, &hit.normal); + return Some(NextRay::new(attenuation, Ray::new(hit.p, reflected))) + } + + let refracted = refract(&unit_direction, &hit.normal, etai_over_etat); + Some(NextRay::new(attenuation, Ray::new(hit.p, refracted))) + } +} |