Compare commits
No commits in common. "4bb6a72e4bb62a1d0c7242fb647b1e377d5dd4e8" and "12c2382327b038305775b6a54ac85ac52edaaf2f" have entirely different histories.
4bb6a72e4b
...
12c2382327
@ -24,18 +24,20 @@ fn main() -> Result<()> {
|
|||||||
let w = 200;
|
let w = 200;
|
||||||
let h = w;
|
let h = w;
|
||||||
let mut c = Canvas::new(w, h);
|
let mut c = Canvas::new(w, h);
|
||||||
let t = Matrix4x4::translation(0., 0.4, 0.);
|
let t = Matrix4x4::translate(0., 0.4, 0.);
|
||||||
let p = Tuple::point(0., 0., 0.);
|
let p = Tuple::point(0., 0., 0.);
|
||||||
let rot_hour = Matrix4x4::rotation_z(-PI / 6.);
|
let rot_hour = Matrix4x4::rotation_z(-PI / 6.);
|
||||||
|
|
||||||
let mut p = t * p;
|
let mut p = t * p;
|
||||||
let w = w as f32;
|
let w = w as f32;
|
||||||
let h = h as f32;
|
let h = h as f32;
|
||||||
|
let h_w = w / 2.0;
|
||||||
|
let h_h = h / 2.0;
|
||||||
// The 'world' exists between -0.5 - 0.5 in X-Y plane.
|
// The 'world' exists between -0.5 - 0.5 in X-Y plane.
|
||||||
// To convert to screen space, we translate by 0.5, scale to canvas size,
|
// To convert to screen space, we translate by 0.5, scale to canvas size,
|
||||||
// and invert the Y-axis.
|
// and invert the Y-axis.
|
||||||
let world_to_screen =
|
let world_to_screen =
|
||||||
Matrix4x4::scaling(w as f32, -h as f32, 1.0) * Matrix4x4::translation(0.5, -0.5, 0.);
|
Matrix4x4::scaling(w as f32, -h as f32, 1.0) * Matrix4x4::translate(0.5, -0.5, 0.);
|
||||||
for _ in 0..12 {
|
for _ in 0..12 {
|
||||||
let canvas_pixel = world_to_screen * p;
|
let canvas_pixel = world_to_screen * p;
|
||||||
draw_dot(&mut c, canvas_pixel.x as usize, canvas_pixel.y as usize);
|
draw_dot(&mut c, canvas_pixel.x as usize, canvas_pixel.y as usize);
|
||||||
|
|||||||
@ -1,41 +0,0 @@
|
|||||||
use core::f32;
|
|
||||||
|
|
||||||
use anyhow::Result;
|
|
||||||
|
|
||||||
use rtchallenge::{
|
|
||||||
canvas::Canvas,
|
|
||||||
rays::Ray,
|
|
||||||
spheres::{intersect, Sphere},
|
|
||||||
tuples::{Color, Tuple},
|
|
||||||
};
|
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
|
||||||
let w = 200;
|
|
||||||
let h = w;
|
|
||||||
let mut c = Canvas::new(w, h);
|
|
||||||
let ray_origin = Tuple::point(0., 0., -5.);
|
|
||||||
let wall_z = 10.;
|
|
||||||
let wall_size = 7.;
|
|
||||||
let pixel_size = wall_size / w as f32;
|
|
||||||
let half = wall_size / 2.;
|
|
||||||
let color = Color::new(1., 0., 0.);
|
|
||||||
let shape = Sphere::default();
|
|
||||||
|
|
||||||
for y in 0..h {
|
|
||||||
let world_y = half - pixel_size * y as f32;
|
|
||||||
for x in 0..w {
|
|
||||||
let world_x = -half + pixel_size * x as f32;
|
|
||||||
let position = Tuple::point(world_x, world_y, wall_z);
|
|
||||||
let r = Ray::new(ray_origin, (position - ray_origin).normalize());
|
|
||||||
let xs = intersect(&shape, &r);
|
|
||||||
if xs.hit().is_some() {
|
|
||||||
c.set(x, y, color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let path = "/tmp/eoc5.png";
|
|
||||||
println!("saving output to {}", path);
|
|
||||||
c.write_to_file(path)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
@ -1,122 +0,0 @@
|
|||||||
use std::ops::Index;
|
|
||||||
|
|
||||||
use crate::spheres::Sphere;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
pub struct Intersection<'i> {
|
|
||||||
pub t: f32,
|
|
||||||
pub object: &'i Sphere,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'i> Intersection<'i> {
|
|
||||||
/// Create new `Intersection` at the given `t` that hits the given `object`.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
/// ```
|
|
||||||
/// use rtchallenge::{intersections::Intersection, spheres::Sphere};
|
|
||||||
///
|
|
||||||
/// // An intersection ecapsulates t and object.
|
|
||||||
/// let s = Sphere::default();
|
|
||||||
/// let i = Intersection::new(3.5, &s);
|
|
||||||
/// assert_eq!(i.t, 3.5);
|
|
||||||
/// assert_eq!(i.object, &s);
|
|
||||||
/// ```
|
|
||||||
pub fn new(t: f32, object: &Sphere) -> Intersection {
|
|
||||||
Intersection { t, object }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Aggregates `Intersection`s.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
/// ```
|
|
||||||
/// use rtchallenge::{
|
|
||||||
/// intersections::{Intersection, Intersections},
|
|
||||||
/// rays::Ray,
|
|
||||||
/// spheres::{intersect, Sphere},
|
|
||||||
/// tuples::Tuple,
|
|
||||||
/// };
|
|
||||||
///
|
|
||||||
/// let s = Sphere::default();
|
|
||||||
/// let i1 = Intersection::new(1., &s);
|
|
||||||
/// let i2 = Intersection::new(2., &s);
|
|
||||||
/// let xs = Intersections::new(vec![i1, i2]);
|
|
||||||
/// assert_eq!(xs.len(), 2);
|
|
||||||
/// assert_eq!(xs[0].t, 1.);
|
|
||||||
/// assert_eq!(xs[1].t, 2.);
|
|
||||||
///
|
|
||||||
/// let r = Ray::new(Tuple::point(0., 0., -5.), Tuple::vector(0., 0., 1.));
|
|
||||||
/// let xs = intersect(&s, &r);
|
|
||||||
/// assert_eq!(xs.len(), 2);
|
|
||||||
/// assert_eq!(xs[0].object, &s);
|
|
||||||
/// assert_eq!(xs[1].object, &s);
|
|
||||||
/// ```
|
|
||||||
#[derive(Debug, Default, PartialEq)]
|
|
||||||
pub struct Intersections<'i>(Vec<Intersection<'i>>);
|
|
||||||
|
|
||||||
impl<'i> Intersections<'i> {
|
|
||||||
pub fn new(xs: Vec<Intersection<'i>>) -> Intersections {
|
|
||||||
Intersections(xs)
|
|
||||||
}
|
|
||||||
pub fn len(&self) -> usize {
|
|
||||||
self.0.len()
|
|
||||||
}
|
|
||||||
/// Finds nearest hit for this collection of intersections.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
/// ```
|
|
||||||
/// use rtchallenge::{
|
|
||||||
/// intersections::{Intersection, Intersections},
|
|
||||||
/// rays::Ray,
|
|
||||||
/// spheres::{intersect, Sphere},
|
|
||||||
/// tuples::Tuple,
|
|
||||||
/// };
|
|
||||||
///
|
|
||||||
/// // The hit, when all intersections have positive t.
|
|
||||||
/// let s = Sphere::default();
|
|
||||||
/// let i1 = Intersection::new(1., &s);
|
|
||||||
/// let i2 = Intersection::new(2., &s);
|
|
||||||
/// let xs = Intersections::new(vec![i2, i1.clone()]);
|
|
||||||
/// let i = xs.hit();
|
|
||||||
/// assert_eq!(i, Some(&i1));
|
|
||||||
///
|
|
||||||
/// // The hit, when some intersections have negative t.
|
|
||||||
/// let s = Sphere::default();
|
|
||||||
/// let i1 = Intersection::new(-1., &s);
|
|
||||||
/// let i2 = Intersection::new(1., &s);
|
|
||||||
/// let xs = Intersections::new(vec![i2.clone(), i1]);
|
|
||||||
/// let i = xs.hit();
|
|
||||||
/// assert_eq!(i, Some(&i2));
|
|
||||||
///
|
|
||||||
/// // The hit, when all intersections have negative t.
|
|
||||||
/// let s = Sphere::default();
|
|
||||||
/// let i1 = Intersection::new(-2., &s);
|
|
||||||
/// let i2 = Intersection::new(-1., &s);
|
|
||||||
/// let xs = Intersections::new(vec![i2, i1]);
|
|
||||||
/// let i = xs.hit();
|
|
||||||
/// assert_eq!(i, None);
|
|
||||||
///
|
|
||||||
/// // The hit is always the lowest nonnegative intersection.
|
|
||||||
/// let s = Sphere::default();
|
|
||||||
/// let i1 = Intersection::new(5., &s);
|
|
||||||
/// let i2 = Intersection::new(7., &s);
|
|
||||||
/// let i3 = Intersection::new(-3., &s);
|
|
||||||
/// let i4 = Intersection::new(2., &s);
|
|
||||||
/// let xs = Intersections::new(vec![i1, i2, i3, i4.clone()]);
|
|
||||||
/// let i = xs.hit();
|
|
||||||
/// assert_eq!(i, Some(&i4));
|
|
||||||
/// ```
|
|
||||||
pub fn hit(&self) -> Option<&Intersection> {
|
|
||||||
self.0.iter().filter(|i| i.t > 0.).min_by(|i1, i2| {
|
|
||||||
i1.t.partial_cmp(&i2.t)
|
|
||||||
.expect("an intersection has a t value that is NaN")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'i> Index<usize> for Intersections<'i> {
|
|
||||||
type Output = Intersection<'i>;
|
|
||||||
fn index(&self, idx: usize) -> &Self::Output {
|
|
||||||
&self.0[idx]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,8 +1,5 @@
|
|||||||
pub mod canvas;
|
pub mod canvas;
|
||||||
pub mod intersections;
|
|
||||||
pub mod matrices;
|
pub mod matrices;
|
||||||
pub mod rays;
|
|
||||||
pub mod spheres;
|
|
||||||
pub mod tuples;
|
pub mod tuples;
|
||||||
|
|
||||||
/// Value considered close enough for PartialEq implementations.
|
/// Value considered close enough for PartialEq implementations.
|
||||||
|
|||||||
@ -175,7 +175,7 @@ impl PartialEq for Matrix3x3 {
|
|||||||
/// let p = Tuple::point(1., 0., 1.);
|
/// let p = Tuple::point(1., 0., 1.);
|
||||||
/// let a = Matrix4x4::rotation_x(PI / 2.);
|
/// let a = Matrix4x4::rotation_x(PI / 2.);
|
||||||
/// let b = Matrix4x4::scaling(5., 5., 5.);
|
/// let b = Matrix4x4::scaling(5., 5., 5.);
|
||||||
/// let c = Matrix4x4::translation(10., 5., 7.);
|
/// let c = Matrix4x4::translate(10., 5., 7.);
|
||||||
/// // Apply rotation first.
|
/// // Apply rotation first.
|
||||||
/// let p2 = a * p;
|
/// let p2 = a * p;
|
||||||
/// assert_eq!(p2, Tuple::point(1., -1., 0.));
|
/// assert_eq!(p2, Tuple::point(1., -1., 0.));
|
||||||
@ -190,7 +190,7 @@ impl PartialEq for Matrix3x3 {
|
|||||||
/// let p = Tuple::point(1., 0., 1.);
|
/// let p = Tuple::point(1., 0., 1.);
|
||||||
/// let a = Matrix4x4::rotation_x(PI / 2.);
|
/// let a = Matrix4x4::rotation_x(PI / 2.);
|
||||||
/// let b = Matrix4x4::scaling(5., 5., 5.);
|
/// let b = Matrix4x4::scaling(5., 5., 5.);
|
||||||
/// let c = Matrix4x4::translation(10., 5., 7.);
|
/// let c = Matrix4x4::translate(10., 5., 7.);
|
||||||
/// let t = c * b * a;
|
/// let t = c * b * a;
|
||||||
/// assert_eq!(t * p, Tuple::point(15., 0., 7.));
|
/// assert_eq!(t * p, Tuple::point(15., 0., 7.));
|
||||||
/// ```
|
/// ```
|
||||||
@ -251,7 +251,7 @@ impl Matrix4x4 {
|
|||||||
/// ```
|
/// ```
|
||||||
/// use rtchallenge::{matrices::Matrix4x4, tuples::Tuple};
|
/// use rtchallenge::{matrices::Matrix4x4, tuples::Tuple};
|
||||||
///
|
///
|
||||||
/// let transform = Matrix4x4::translation(5., -3., 2.);
|
/// let transform = Matrix4x4::translate(5., -3., 2.);
|
||||||
/// let p = Tuple::point(-3., 4., 5.);
|
/// let p = Tuple::point(-3., 4., 5.);
|
||||||
/// assert_eq!(transform * p, Tuple::point(2., 1., 7.));
|
/// assert_eq!(transform * p, Tuple::point(2., 1., 7.));
|
||||||
///
|
///
|
||||||
@ -261,7 +261,7 @@ impl Matrix4x4 {
|
|||||||
/// let v = Tuple::vector(-3., 4., 5.);
|
/// let v = Tuple::vector(-3., 4., 5.);
|
||||||
/// assert_eq!(transform * v, v);
|
/// assert_eq!(transform * v, v);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn translation(x: f32, y: f32, z: f32) -> Matrix4x4 {
|
pub fn translate(x: f32, y: f32, z: f32) -> Matrix4x4 {
|
||||||
Matrix4x4::new(
|
Matrix4x4::new(
|
||||||
[1., 0., 0., x],
|
[1., 0., 0., x],
|
||||||
[0., 1., 0., y],
|
[0., 1., 0., y],
|
||||||
|
|||||||
@ -1,71 +0,0 @@
|
|||||||
use crate::{matrices::Matrix4x4, tuples::Tuple};
|
|
||||||
|
|
||||||
/// Rays have an origin and a direction. This datatype is the 'ray' in 'raytracer'.
|
|
||||||
pub struct Ray {
|
|
||||||
pub origin: Tuple,
|
|
||||||
pub direction: Tuple,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Ray {
|
|
||||||
/// Create a ray with the given origin point and direction vector.
|
|
||||||
/// Will panic if origin not a point or direction not a vector.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
/// ```
|
|
||||||
/// use rtchallenge::{rays::Ray, tuples::Tuple};
|
|
||||||
///
|
|
||||||
/// let origin = Tuple::point(1., 2., 3.);
|
|
||||||
/// let direction = Tuple::vector(4., 5., 6.);
|
|
||||||
/// let r = Ray::new(origin, direction);
|
|
||||||
/// assert_eq!(r.origin, origin);
|
|
||||||
/// assert_eq!(r.direction, direction);
|
|
||||||
/// ```
|
|
||||||
pub fn new(origin: Tuple, direction: Tuple) -> Ray {
|
|
||||||
assert!(origin.is_point(), "Ray origin must be a point");
|
|
||||||
assert!(direction.is_vector(), "Ray direction must be a vector");
|
|
||||||
Ray { origin, direction }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Compute a point from the given distance along the `Ray`.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
/// ```
|
|
||||||
/// use rtchallenge::{rays::Ray, tuples::Tuple};
|
|
||||||
///
|
|
||||||
/// let r = Ray::new(Tuple::point(2., 3., 4.), Tuple::vector(1., 0., 0.));
|
|
||||||
/// assert_eq!(r.position(0.), Tuple::point(2., 3., 4.));
|
|
||||||
/// assert_eq!(r.position(1.), Tuple::point(3., 3., 4.));
|
|
||||||
/// assert_eq!(r.position(-1.), Tuple::point(1., 3., 4.));
|
|
||||||
/// assert_eq!(r.position(2.5), Tuple::point(4.5, 3., 4.));
|
|
||||||
/// ```
|
|
||||||
pub fn position(&self, t: f32) -> Tuple {
|
|
||||||
self.origin + self.direction * t
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Apply Matrix4x4 transforms to Ray.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
/// ```
|
|
||||||
/// use rtchallenge::{matrices::Matrix4x4, rays::Ray, tuples::Tuple};
|
|
||||||
///
|
|
||||||
/// // Translating a ray
|
|
||||||
/// let r = Ray::new(Tuple::point(1., 2., 3.), Tuple::vector(0., 1., 0.));
|
|
||||||
/// let m = Matrix4x4::translation(3., 4., 5.);
|
|
||||||
/// let r2 = r.transform(m);
|
|
||||||
/// assert_eq!(r2.origin, Tuple::point(4., 6., 8.));
|
|
||||||
/// assert_eq!(r2.direction, Tuple::vector(0., 1., 0.));
|
|
||||||
///
|
|
||||||
/// // Scaling a ray
|
|
||||||
/// let r = Ray::new(Tuple::point(1., 2., 3.), Tuple::vector(0., 1., 0.));
|
|
||||||
/// let m = Matrix4x4::scaling(2., 3., 4.);
|
|
||||||
/// let r2 = r.transform(m);
|
|
||||||
/// assert_eq!(r2.origin, Tuple::point(2., 6., 12.));
|
|
||||||
/// assert_eq!(r2.direction, Tuple::vector(0., 3., 0.));
|
|
||||||
/// ```
|
|
||||||
pub fn transform(&self, m: Matrix4x4) -> Ray {
|
|
||||||
Ray {
|
|
||||||
origin: m * self.origin,
|
|
||||||
direction: m * self.direction,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,113 +0,0 @@
|
|||||||
use crate::{
|
|
||||||
intersections::{Intersection, Intersections},
|
|
||||||
matrices::Matrix4x4,
|
|
||||||
rays::Ray,
|
|
||||||
tuples::{dot, Tuple},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
|
||||||
/// Sphere represents the unit-sphere (radius of unit 1.) at the origin 0., 0., 0.
|
|
||||||
pub struct Sphere {
|
|
||||||
// TODO(wathiede): cache inverse to speed up intersect.
|
|
||||||
pub transform: Matrix4x4,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Sphere {
|
|
||||||
/// # Examples
|
|
||||||
/// ```
|
|
||||||
/// use rtchallenge::{matrices::Matrix4x4, spheres::Sphere};
|
|
||||||
///
|
|
||||||
/// // A sphere's default transform is the identity matrix.
|
|
||||||
/// let s = Sphere::default();
|
|
||||||
/// assert_eq!(s.transform, Matrix4x4::identity());
|
|
||||||
///
|
|
||||||
/// // It can be changed by directly setting the transform member.
|
|
||||||
/// let mut s = Sphere::default();
|
|
||||||
/// let t = Matrix4x4::translation(2., 3., 4.);
|
|
||||||
/// s.transform = t.clone();
|
|
||||||
/// assert_eq!(s.transform, t);
|
|
||||||
/// ```
|
|
||||||
fn default() -> Sphere {
|
|
||||||
Sphere {
|
|
||||||
transform: Matrix4x4::identity(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Intersect a ray with a sphere.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
/// ```
|
|
||||||
/// use rtchallenge::{
|
|
||||||
/// intersections::{Intersection, Intersections},
|
|
||||||
/// matrices::Matrix4x4,
|
|
||||||
/// rays::Ray,
|
|
||||||
/// spheres::{intersect, Sphere},
|
|
||||||
/// tuples::Tuple,
|
|
||||||
/// };
|
|
||||||
///
|
|
||||||
/// // A ray intersects a sphere in two points.
|
|
||||||
/// let r = Ray::new(Tuple::point(0., 0., -5.), Tuple::vector(0., 0., 1.));
|
|
||||||
/// let s = Sphere::default();
|
|
||||||
/// let xs = intersect(&s, &r);
|
|
||||||
/// assert_eq!(
|
|
||||||
/// xs,
|
|
||||||
/// Intersections::new(vec![Intersection::new(4., &s), Intersection::new(6., &s)])
|
|
||||||
/// );
|
|
||||||
///
|
|
||||||
/// // A ray intersects a sphere at a tangent.
|
|
||||||
/// let r = Ray::new(Tuple::point(0., 2., -5.), Tuple::vector(0., 0., 1.));
|
|
||||||
/// let s = Sphere::default();
|
|
||||||
/// let xs = intersect(&s, &r);
|
|
||||||
/// assert_eq!(xs, Intersections::default());
|
|
||||||
///
|
|
||||||
/// // A ray originates inside a sphere.
|
|
||||||
/// let r = Ray::new(Tuple::point(0., 0., 0.), Tuple::vector(0., 0., 1.));
|
|
||||||
/// let s = Sphere::default();
|
|
||||||
/// let xs = intersect(&s, &r);
|
|
||||||
/// assert_eq!(
|
|
||||||
/// xs,
|
|
||||||
/// Intersections::new(vec![Intersection::new(-1., &s), Intersection::new(1., &s)])
|
|
||||||
/// );
|
|
||||||
///
|
|
||||||
/// // A sphere is behind a ray.
|
|
||||||
/// let r = Ray::new(Tuple::point(0., 0., 5.), Tuple::vector(0., 0., 1.));
|
|
||||||
/// let s = Sphere::default();
|
|
||||||
/// let xs = intersect(&s, &r);
|
|
||||||
/// assert_eq!(
|
|
||||||
/// xs,
|
|
||||||
/// Intersections::new(vec![Intersection::new(-6., &s), Intersection::new(-4., &s)])
|
|
||||||
/// );
|
|
||||||
///
|
|
||||||
/// // Intersect a scaled sphere with a ray.
|
|
||||||
/// let r = Ray::new(Tuple::point(0., 0., -5.), Tuple::vector(0., 0., 1.));
|
|
||||||
/// let mut s = Sphere::default();
|
|
||||||
/// s.transform = Matrix4x4::scaling(2., 2., 2.);
|
|
||||||
/// let xs = intersect(&s, &r);
|
|
||||||
/// assert_eq!(xs.len(), 2, "xs {:?}", xs);
|
|
||||||
/// assert_eq!(xs[0].t, 3., "xs {:?}", xs);
|
|
||||||
/// assert_eq!(xs[1].t, 7., "xs {:?}", xs);
|
|
||||||
///
|
|
||||||
/// // Intersect a translated sphere with a ray.
|
|
||||||
/// let r = Ray::new(Tuple::point(0., 0., -5.), Tuple::vector(0., 0., 1.));
|
|
||||||
/// let mut s = Sphere::default();
|
|
||||||
/// s.transform = Matrix4x4::translation(5., 0., 0.);
|
|
||||||
/// let xs = intersect(&s, &r);
|
|
||||||
/// assert_eq!(xs.len(), 0);
|
|
||||||
/// ```
|
|
||||||
pub fn intersect<'s>(sphere: &'s Sphere, ray: &Ray) -> Intersections<'s> {
|
|
||||||
let ray = ray.transform(sphere.transform.inverse());
|
|
||||||
let sphere_to_ray = ray.origin - Tuple::point(0., 0., 0.);
|
|
||||||
let a = dot(ray.direction, ray.direction);
|
|
||||||
let b = 2. * dot(ray.direction, sphere_to_ray);
|
|
||||||
let c = dot(sphere_to_ray, sphere_to_ray) - 1.;
|
|
||||||
let discriminant = b * b - 4. * a * c;
|
|
||||||
if discriminant < 0. {
|
|
||||||
Intersections::default()
|
|
||||||
} else {
|
|
||||||
Intersections::new(vec![
|
|
||||||
Intersection::new((-b - discriminant.sqrt()) / (2. * a), &sphere),
|
|
||||||
Intersection::new((-b + discriminant.sqrt()) / (2. * a), &sphere),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user