stocking

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

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:
Msrc/hittable.rs | 7+++++--
Msrc/main.rs | 10++++++++--
Msrc/material.rs | 45+++++++++++++++++++++++++++++++++++++++++++++
Msrc/vec3.rs | 7+++++++
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 {