71 lines
1.9 KiB
Rust
71 lines
1.9 KiB
Rust
|
use crate::scene_data::{Sphere, Vec3};
|
||
|
|
||
|
/// A normalized Ray
|
||
|
pub struct Ray {
|
||
|
origin: Vec3,
|
||
|
direction: Vec3,
|
||
|
}
|
||
|
|
||
|
impl Ray {
|
||
|
pub fn eval(&self, time: f64) -> Vec3 {
|
||
|
self.origin + self.direction * time
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// Given a ray and a sphere, returns the first point at which this ray intersects the sphere.
|
||
|
///
|
||
|
/// If there is no intersection point, returns None.
|
||
|
pub fn ray_intersection_time(ray: &Ray, sphere: &Sphere) -> Option<f64> {
|
||
|
let a =
|
||
|
ray.direction.x.powi(2) + ray.direction.y.powi(2) + ray.direction.z.powi(2);
|
||
|
let b = 2.0
|
||
|
* (ray.direction.x * (ray.origin.x - sphere.center.x)
|
||
|
+ ray.direction.y * (ray.origin.y - sphere.center.y)
|
||
|
+ ray.direction.z * (ray.origin.z - sphere.center.z));
|
||
|
let c = (ray.origin.x - sphere.center.x).powi(2)
|
||
|
+ (ray.origin.y - sphere.center.y).powi(2)
|
||
|
+ (ray.origin.z - sphere.center.z).powi(2)
|
||
|
- sphere.radius.powi(2);
|
||
|
let discriminant = b * b - 4.0 * a * c;
|
||
|
|
||
|
match discriminant {
|
||
|
// Discriminant < 0, means the equation has no solutions.
|
||
|
d if d < 0.0 => return None,
|
||
|
|
||
|
// Discriminant == 0
|
||
|
d if d == 0.0 => {
|
||
|
return Some(-b / (2.0 * a));
|
||
|
}
|
||
|
|
||
|
d if d > 0.0 => {
|
||
|
let solution_1 = (-b + discriminant.sqrt()) / (2.0 * a);
|
||
|
let solution_2 = (-b - discriminant.sqrt()) / (2.0 * a);
|
||
|
|
||
|
return Some(solution_1.min(solution_2));
|
||
|
}
|
||
|
_ => unreachable!("Invalid determinant value: {discriminant}"),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#[cfg(test)]
|
||
|
mod tests {
|
||
|
use crate::scene_data::{Sphere, Vec3};
|
||
|
|
||
|
use super::{ray_intersection_time, Ray};
|
||
|
|
||
|
#[test]
|
||
|
fn practice_problem_slide_154() {
|
||
|
let ray = Ray {
|
||
|
origin: Vec3::new(0.0, 0.0, 0.0),
|
||
|
direction: Vec3::new(0.0, 0.0, -1.0),
|
||
|
};
|
||
|
let sphere = Sphere {
|
||
|
center: Vec3::new(0.0, 0.0, -10.0),
|
||
|
radius: 4.0,
|
||
|
};
|
||
|
|
||
|
let t = ray_intersection_time(&ray, &sphere).unwrap();
|
||
|
assert_eq!(ray.eval(t), Vec3::new(0.0, 0.0, -6.0));
|
||
|
}
|
||
|
}
|