aboutsummaryrefslogtreecommitdiffstats
path: root/src/material.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/material.rs')
-rw-r--r--src/material.rs64
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)))
+ }
+}