use crate::hittable; use crate::material::{Lambertian, Material, MaterialKind, Metal}; use crate::types_defined::{Color, HitRecord, Point, Ray, Sphere}; impl Sphere { pub fn new(c: Point, r: f32, m: Option) -> Self { Self { center: c, radius: r, material: m, } } } impl hittable::Hittable for Sphere { fn hit(&self, r: &Ray, t_min: f32, t_max: f32, hit_record: &mut HitRecord) -> bool { let a: f32 = r.direction.length_squared(); let h = r.direction.dot(self.center - r.point); // let b = -2.0 * r.direction.dot(sphere_center - r.point); let c = (self.center - r.point).length_squared() - self.radius * self.radius; let discriminant = h * h - a * c; if discriminant < 0.0 { return false; } // // 两个交点 let disc_sqrt = discriminant.sqrt(); let near = (h - disc_sqrt) / a; let far = (h + disc_sqrt) / a; let mut root = near; if root <= t_min || root >= t_max { root = far; if root <= t_min || root >= t_max { hit_record.t = -1.0; return false } } let p = r.at(root); let normal = (p - self.center) / self.radius; hit_record.t = root; hit_record.p = p; hit_record.normal = normal; hit_record.front_face = r.direction.dot(normal) < 0.0; hit_record.material = if let Some(ref m) = self.material { match m { MaterialKind::Lambertian(l) => Some(MaterialKind::Lambertian(l.clone())), MaterialKind::Metal(m) => Some(MaterialKind::Metal(m.clone())), } } else { None }; true } }