minimal-raytracer/src/main.rs

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() {}