116 lines
2.7 KiB
Rust
116 lines
2.7 KiB
Rust
use std::ops::{Add, Div, Mul, Sub};
|
|
|
|
#[derive(Clone, Copy)]
|
|
struct Vec3 {
|
|
x: f64,
|
|
y: f64,
|
|
z: f64,
|
|
}
|
|
|
|
impl Vec3 {
|
|
fn new(x: f64, y: f64, z: f64) -> Self {
|
|
Self { x, y, z }
|
|
}
|
|
|
|
fn length(&self) -> f64 {
|
|
(self.x * self.x + self.y * self.y + self.z * self.z).sqrt()
|
|
}
|
|
|
|
// Unit vector in same direction
|
|
fn unit(&self) -> Self {
|
|
let length = self.length();
|
|
|
|
Self::new(self.x / length, self.y / length, self.z / length)
|
|
}
|
|
|
|
// Dot product
|
|
// See: https://betterexplained.com/articles/vector-calculus-understanding-the-dot-product/
|
|
fn dot(&self, other: &Vec3) -> f64 {
|
|
self.x * other.x + self.y + other.y + self.z + other.z
|
|
}
|
|
}
|
|
|
|
impl Add for Vec3 {
|
|
type Output = Self;
|
|
|
|
fn add(self, other: Self) -> Self::Output {
|
|
Self::new(self.x + other.x, self.y + other.y, self.z + other.z)
|
|
}
|
|
}
|
|
|
|
impl Sub for Vec3 {
|
|
type Output = Self;
|
|
|
|
fn sub(self, other: Self) -> Self::Output {
|
|
Self::new(self.x - other.x, self.y - other.y, self.z - other.z)
|
|
}
|
|
}
|
|
|
|
impl Mul for Vec3 {
|
|
type Output = Self;
|
|
|
|
fn mul(self, other: Self) -> Self::Output {
|
|
Self::new(self.x * other.x, self.y * other.y, self.z * other.z)
|
|
}
|
|
}
|
|
|
|
impl Div for Vec3 {
|
|
type Output = Self;
|
|
|
|
fn div(self, other: Self) -> Self::Output {
|
|
Self::new(self.x / other.x, self.y / other.y, self.z / other.z)
|
|
}
|
|
}
|
|
|
|
// Aliased for use in arguments, no functional difference
|
|
type Point = Vec3;
|
|
type Color = Vec3;
|
|
|
|
struct Ray {
|
|
origin: Point,
|
|
direction: Vec3,
|
|
}
|
|
|
|
struct Sphere {
|
|
center: Vec3,
|
|
radius: f64,
|
|
color: Color,
|
|
}
|
|
|
|
impl Sphere {
|
|
fn new(center: Vec3, radius: f64, color: Color) -> Self {
|
|
Self { center, radius, color }
|
|
}
|
|
|
|
// Ray/sphere intersection
|
|
// See: https://viclw17.github.io/2018/07/16/raytracing-ray-sphere-intersection/
|
|
fn intersect(&self, ray: &Ray) -> Option<f64> {
|
|
let epsilon = 1e-4;
|
|
let oc = ray.origin - self.center;
|
|
let a = ray.direction.dot(&ray.direction);
|
|
let b = 2. * oc.dot(&ray.direction);
|
|
let c = oc.dot(&oc) - self.radius * self.radius;
|
|
let discrimiant = b * b - 4. * a * c;
|
|
|
|
// The ray missed the sphere
|
|
if discrimiant < 0. {
|
|
None
|
|
} else {
|
|
let mut numerator = -b - discrimiant.sqrt();
|
|
if numerator > epsilon {
|
|
return Some(numerator / (2. * a));
|
|
}
|
|
|
|
numerator = -b + discrimiant.sqrt();
|
|
if numerator > epsilon {
|
|
Some(numerator / (2. * a))
|
|
} else {
|
|
// The intersection happened behind the ray's origin
|
|
None
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn main() {}
|