commit d60d1c0ee69d379f622f276e9c2bbfd3e0c3d622
parent 51fe57edbc27aee94434264e3ac0b673d7f390d9
Author: Bradley Gannon <bradley@bradleygannon.com>
Date: Tue, 16 Dec 2025 12:43:03 -0500
Add Dielectric material
Complete Chapter 11
Diffstat:
4 files changed, 65 insertions(+), 4 deletions(-)
diff --git a/src/hittable.rs b/src/hittable.rs
@@ -7,7 +7,7 @@ pub struct HitRecord {
normal: Vec3,
t: f64,
material: Rc<dyn Material>,
- _front_face: bool,
+ front_face: bool,
}
impl HitRecord {
@@ -25,7 +25,7 @@ impl HitRecord {
normal,
t,
material,
- _front_face: front_face,
+ front_face,
}
}
pub fn normal(&self) -> Vec3 {
@@ -34,6 +34,9 @@ impl HitRecord {
pub fn point(&self) -> Vec3 {
self.point
}
+ pub fn front_face(&self) -> bool {
+ self.front_face
+ }
pub fn material(&self) -> Rc<dyn Material> {
self.material.clone()
}
diff --git a/src/main.rs b/src/main.rs
@@ -4,7 +4,7 @@ use crate::{
camera::Camera,
color::LinearColor,
hittable::{Hittable, Sphere},
- material::{Lambertian, Metal},
+ material::{Dielectric, Lambertian, Metal},
vec3::Vec3,
};
@@ -20,7 +20,8 @@ mod vec3;
fn main() {
let material_ground = Lambertian::new(LinearColor::new(0.8, 0.8, 0.));
let material_center = Lambertian::new(LinearColor::new(0.1, 0.2, 0.5));
- let material_left = Metal::new(LinearColor::new(0.8, 0.8, 0.8), 0.3);
+ let material_left = Dielectric::new(1.5);
+ let material_bubble = Dielectric::new(1. / 1.5);
let material_right = Metal::new(LinearColor::new(0.8, 0.6, 0.2), 1.0);
let world: Vec<Box<dyn Hittable>> = vec![
@@ -40,6 +41,11 @@ fn main() {
Rc::new(material_left),
)),
Box::new(Sphere::new(
+ Vec3::new(-1., 0., -1.),
+ 0.4,
+ Rc::new(material_bubble),
+ )),
+ Box::new(Sphere::new(
Vec3::new(1., 0., -1.),
0.5,
Rc::new(material_right),
diff --git a/src/material.rs b/src/material.rs
@@ -95,3 +95,48 @@ impl Material for Metal {
}
}
}
+
+#[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd)]
+pub struct Dielectric {
+ refractive_index: f64,
+}
+
+impl Dielectric {
+ pub const fn new(refractive_index: f64) -> Self {
+ Dielectric { refractive_index }
+ }
+ fn reflectance(cosine: f64, refractive_index: f64) -> f64 {
+ let r0 = ((1. - refractive_index) / (1. + refractive_index)).powi(2);
+ r0 + (1. - r0) * (1. - cosine).powi(5)
+ }
+}
+
+impl Material for Dielectric {
+ fn scatter(
+ &self,
+ r_in: &Ray,
+ hit: &HitRecord,
+ rng: &mut RandomNumberGenerator,
+ ) -> Option<ScatteredRay> {
+ let refractive_index = if hit.front_face() {
+ 1. / self.refractive_index
+ } else {
+ self.refractive_index
+ };
+ let unit_direction = r_in.direction().unit();
+ let cosine_theta = (-unit_direction).dot(&hit.normal()).min(1.);
+ let sine_theta = (1. - cosine_theta * cosine_theta).sqrt();
+ let cannot_refract = refractive_index * sine_theta > 1.;
+ let direction = if cannot_refract
+ || Dielectric::reflectance(cosine_theta, refractive_index) > rng.next_f64()
+ {
+ unit_direction.reflect(&hit.normal())
+ } else {
+ unit_direction.refract(&hit.normal(), refractive_index)
+ };
+ Some(ScatteredRay {
+ ray: Ray::new(hit.point(), direction),
+ attenuation: LinearColor::new(1., 1., 1.),
+ })
+ }
+}
diff --git a/src/vec3.rs b/src/vec3.rs
@@ -88,6 +88,13 @@ impl Vec3 {
pub fn reflect(&self, normal: &Vec3) -> Vec3 {
*self - 2. * self.dot(normal) * *normal
}
+
+ pub fn refract(&self, normal: &Vec3, refractive_index_ratio: f64) -> Vec3 {
+ let cosine_theta = (-*self).dot(normal).min(1.);
+ let r_out_perpendicular = refractive_index_ratio * (*self + cosine_theta * *normal);
+ let r_out_parallel = -(1. - r_out_perpendicular.length_squared()).abs().sqrt() * *normal;
+ r_out_perpendicular + r_out_parallel
+ }
}
impl Add for Vec3 {