use point::Point; use scene::Color; use scene::Scene; use vector::Vector3; pub trait Intersectable { fn intersect(&self, ray: &Ray) -> Option; fn surface_normal(&self, hit_point: &Point) -> Vector3; } pub struct Sphere { pub center: Point, pub radius: f32, pub color: Color, } impl Intersectable for Sphere { fn intersect(&self, ray: &Ray) -> Option { //Create a line segment between the ray origin and the center of the sphere let l: Vector3 = (self.center - ray.origin).into(); //Use l as a hypotenuse and find the length of the adjacent side let adj = l.dot(&ray.direction); //Find the length-squared of the opposite side //This is equivalent to (but faster than) (l.length() * l.length()) - (adj * adj) let d2 = l.dot(&l) - (adj * adj); let radius2 = self.radius * self.radius; if d2 > radius2 { return None; } let thc = (radius2 - d2).sqrt(); let t0 = adj - thc; let t1 = adj + thc; if t0 < 0.0 && t1 < 0.0 { return None; } let distance = if t0 < t1 { t0 } else { t1 }; Some(distance) } fn surface_normal(&self, hit_point: &Point) -> Vector3 { Vector3::from(*hit_point - self.center).normalize() } } pub struct Plane { pub origin: Point, pub normal: Vector3, pub color: Color, } impl Intersectable for Plane { fn intersect(&self, ray: &Ray) -> Option { let normal = &self.normal; let denom = normal.dot(&ray.direction); if denom > 1e-6 { let v: Vector3 = (self.origin - ray.origin).into(); let distance = v.dot(&normal) / denom; if distance >= 0.0 { return Some(distance); } } None } fn surface_normal(&self, _: &Point) -> Vector3 { -self.normal } } pub enum Element { Sphere(Sphere), Plane(Plane), } impl Element { pub fn color(&self) -> &Color { match *self { Element::Sphere(ref s) => &s.color, Element::Plane(ref p) => &p.color, } } pub fn surface_normal(&self, hit_point: &Point) -> Vector3 { match *self { Element::Sphere(ref s) => s.surface_normal(hit_point), Element::Plane(ref p) => p.surface_normal(hit_point), } } } impl Intersectable for Element { fn intersect(&self, ray: &Ray) -> Option { match *self { Element::Sphere(ref s) => s.intersect(ray), Element::Plane(ref p) => p.intersect(ray), } } fn surface_normal(&self, hit_point: &Point) -> Vector3 { match *self { Element::Sphere(ref s) => s.surface_normal(hit_point), Element::Plane(ref p) => p.surface_normal(hit_point), } } } pub struct Ray { pub origin: Point, pub direction: Vector3, } impl Ray { pub fn create_prime(x: u32, y: u32, scene: &Scene) -> Ray { assert!(scene.width > scene.height); let fov_adjustment = (scene.fov.to_radians() / 2.0).tan(); let aspect_ratio = (scene.width as f32) / (scene.height as f32); let sensor_x = ((((x as f32 + 0.5) / scene.width as f32) * 2.0 - 1.0) * aspect_ratio) * fov_adjustment; let sensor_y = (1.0 - ((y as f32 + 0.5) / scene.height as f32) * 2.0) * fov_adjustment; Ray { origin: Point::zero(), direction: Vector3 { x: sensor_x, y: sensor_y, z: -1.0, }.normalize(), } } }