Skip to content

Instantly share code, notes, and snippets.

@gue-ni
Created October 9, 2023 06:06
Show Gist options
  • Save gue-ni/0bb733cbe63439aa2878a35cc1b105a2 to your computer and use it in GitHub Desktop.
Save gue-ni/0bb733cbe63439aa2878a35cc1b105a2 to your computer and use it in GitHub Desktop.
A Simple Ray-Sphere Intersection Implementation in Rust
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