Created
October 9, 2023 06:06
-
-
Save gue-ni/0bb733cbe63439aa2878a35cc1b105a2 to your computer and use it in GitHub Desktop.
A Simple Ray-Sphere Intersection Implementation in Rust
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
mod vecmath { | |
use std::ops::{Add, Div, Mul, Sub}; | |
#[derive(Debug, Copy, Clone)] | |
pub struct Vec3 { | |
pub x: f64, | |
pub y: f64, | |
pub z: f64 | |
} | |
impl Vec3 { | |
pub fn new(x: f64, y: f64, z: f64) -> Self { | |
Self { x, y, z } | |
} | |
pub fn dot(self, other: Self) -> f64 { | |
self.x * other.x + self.y * other.y + self.z * other.z | |
} | |
pub fn cross(self, other: Self) -> Self { | |
Self::new( | |
self.y * other.z - self.z * other.y, | |
self.z * other.x - self.x * other.z, | |
self.x * other.y - self.y * other.x | |
) | |
} | |
pub fn length(self) -> f64 { | |
Vec3::dot(self, self).sqrt() | |
} | |
pub fn normalize(self) -> Self { | |
let len = self.length(); | |
assert!(0.0 < len); | |
self / len | |
} | |
} | |
impl Mul<f64> for Vec3 { | |
type Output = Self; | |
fn mul(self, scalar: f64) -> Self { | |
Self::new(self.x * scalar, self.y * scalar, self.z * scalar) | |
} | |
} | |
impl Div<f64> for Vec3 { | |
type Output = Self; | |
fn div(self, scalar: f64) -> Self { | |
Self::new(self.x / scalar, self.y / scalar, self.z / scalar) | |
} | |
} | |
impl Mul for Vec3 { | |
type Output = Self; | |
fn mul(self, other: Self) -> Self { | |
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 { | |
Self::new(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 { | |
Self::new(self.x + other.x, self.y + other.y, self.z + other.z) | |
} | |
} | |
} | |
use vecmath::*; | |
#[derive(Debug, Copy, Clone)] | |
struct Ray { | |
origin: Vec3, | |
direction: Vec3 | |
} | |
impl Ray { | |
fn point_at(&self, t: f64) -> Vec3 { | |
self.origin + self.direction * t | |
} | |
} | |
#[derive(Debug, Copy, Clone)] | |
struct Sphere { | |
center: Vec3, | |
radius: f64 | |
} | |
#[derive(Debug, Copy, Clone)] | |
struct Hit { | |
t: f64, | |
normal: Vec3, | |
point: Vec3, | |
} | |
trait Hittable { | |
fn hit(&self, ray: &Ray, min_t: f64, max_t: f64) -> Option<Hit>; | |
} | |
impl Hittable for Sphere { | |
fn hit(&self, ray: &Ray, min_t: f64, max_t: f64) -> Option<Hit> { | |
let r2 = self.radius * self.radius; | |
let oc = self.center - ray.origin; | |
let tca = Vec3::dot(oc, ray.direction); | |
if tca < 0.0 { | |
return None; | |
} | |
let d2 = Vec3::dot(oc, oc) - tca * tca; | |
if r2 < d2 { | |
return None; | |
} | |
let thc = f64::sqrt(r2 - d2); | |
let t0 = tca - thc; | |
let t1 = tca + thc; | |
if t0 < 0.0 && t1 < 0.0 { | |
return None; | |
} | |
let t = { | |
if t0 < 0.0 { | |
t1 // we are inside the sphere | |
} else { | |
f64::min(t0, t1) // return closest hit | |
} | |
}; | |
if t < min_t && max_t < t { | |
return None; | |
} | |
let point = ray.point_at(t); | |
let normal = (point - self.center) / self.radius; | |
Some(Hit { t, normal, point }) | |
} | |
} | |
pub fn main() { | |
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, 5.0), | |
radius: 1.0 | |
}; | |
if let Some(hit) = sphere.hit(&ray, 0.001, f64::INFINITY) { | |
println!("Hit = {:?}", hit); | |
} else { | |
println!("No hit!"); | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment