csci5607/assignment-1/src/ray.rs

96 lines
2.6 KiB
Rust
Raw Normal View History

2023-01-31 07:38:03 +00:00
use crate::scene_data::Sphere;
use crate::vec3::Vec3;
2023-01-31 07:15:22 +00:00
2023-01-31 07:38:03 +00:00
/// A normalized parametric Ray of the form (origin + direction * time)
///
/// That means at any time t: f64, the point represented by origin + direction * time occurs on the
/// ray.
2023-01-31 07:15:22 +00:00
pub struct Ray {
origin: Vec3,
direction: Vec3,
}
impl Ray {
2023-01-31 07:38:03 +00:00
/// Evaluate the ray at a certain point in time, yielding a point
2023-01-31 07:15:22 +00:00
pub fn eval(&self, time: f64) -> Vec3 {
self.origin + self.direction * time
}
}
2023-01-31 07:38:03 +00:00
/// Given a ray and a sphere, returns the first time at which this ray intersects the sphere.
2023-01-31 07:15:22 +00:00
///
/// 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));
}
2023-01-31 07:38:03 +00:00
// Probably hit some NaN or Infinity value due to faulty inputs...
2023-01-31 07:15:22 +00:00
_ => unreachable!("Invalid determinant value: {discriminant}"),
}
}
#[cfg(test)]
mod tests {
2023-01-31 07:38:03 +00:00
use crate::scene_data::Sphere;
use crate::vec3::Vec3;
2023-01-31 07:15:22 +00:00
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,
};
2023-01-31 07:38:03 +00:00
let point = ray_intersection_time(&ray, &sphere).map(|t| ray.eval(t));
// the intersection point in this case is (0, 0, 6)
assert_eq!(point, Some(Vec3::new(0.0, 0.0, -6.0)));
}
#[test]
fn practice_problem_slide_158() {
let ray = Ray {
origin: Vec3::new(0.0, 0.0, 0.0),
direction: Vec3::new(0.0, 0.5, -1.0),
};
let sphere = Sphere {
center: Vec3::new(0.0, 0.0, -10.0),
radius: 4.0,
};
// oops! In this case, the ray does not intersect the sphere.
assert_eq!(ray_intersection_time(&ray, &sphere), None);
2023-01-31 07:15:22 +00:00
}
}