csci5607/assignment-1b/src/scene/sphere.rs

77 lines
2.1 KiB
Rust
Raw Normal View History

2023-02-06 03:52:42 +00:00
use anyhow::Result;
use nalgebra::Vector3;
use ordered_float::NotNan;
use crate::{ray::Ray, utils::min_f64};
2023-02-15 07:22:42 +00:00
use super::{data::ObjectKind, illumination::IntersectionContext};
2023-02-06 03:52:42 +00:00
#[derive(Debug)]
pub struct Sphere {
pub center: Vector3<f64>,
pub radius: f64,
}
impl ObjectKind for Sphere {
/// Given a sphere, returns the first time at which this ray intersects the
/// sphere.
///
/// If there is no intersection point, returns None.
fn intersects_ray_at(
&self,
ray: &Ray,
) -> Result<Option<IntersectionContext>> {
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 - self.center.x)
+ ray.direction.y * (ray.origin.y - self.center.y)
+ ray.direction.z * (ray.origin.z - self.center.z));
let c = (ray.origin.x - self.center.x).powi(2)
+ (ray.origin.y - self.center.y).powi(2)
+ (ray.origin.z - self.center.z).powi(2)
- self.radius.powi(2);
let discriminant = b * b - 4.0 * a * c;
let time = match discriminant {
// Discriminant < 0, means the equation has no solutions.
d if d < 0.0 => None,
// Discriminant == 0
d if d == 0.0 => 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);
let solutions = [solution_1, solution_2]
.into_iter()
// Remove any t < 0, since that means it's behind the viewer and we
// can't see it.
.filter(|t| *t >= 0.0);
// Return the minimum solution
min_f64(solutions)
}
// Probably hit some NaN or Infinity value due to faulty inputs...
_ => unreachable!("Invalid determinant value: {discriminant}"),
};
let time = match time.and_then(|t| NotNan::new(t).ok()) {
Some(v) => v,
None => return Ok(None),
};
let point = ray.eval(*time);
let normal = (point - self.center).normalize();
Ok(Some(IntersectionContext {
time,
point,
normal,
}))
}
}