From ebf39d014bfdad7035f072ced459df71cdcc21d1 Mon Sep 17 00:00:00 2001 From: djairoh Date: Wed, 15 Apr 2026 13:32:29 +0200 Subject: [PATCH] ft: 5,6.1: sphere class + normals --- output.png | Bin 1999 -> 33546 bytes src/main.rs | 32 ++++++++++++++++++++++++++---- src/objects.rs | 3 +++ src/objects/hit.rs | 25 ++++++++++++++++++++++++ src/objects/sphere.rs | 44 ++++++++++++++++++++++++++++++++++++++++++ src/objects/traits.rs | 11 +++++++++++ src/ray.rs | 2 +- src/vec3.rs | 21 +++++++++++++------- 8 files changed, 126 insertions(+), 12 deletions(-) create mode 100644 src/objects.rs create mode 100644 src/objects/hit.rs create mode 100644 src/objects/sphere.rs create mode 100644 src/objects/traits.rs diff --git a/output.png b/output.png index fdbd6927e17d062b21e38688b16f7248049d99e5..8cca54b0379d7591d6637108edb0c1506f41b4aa 100644 GIT binary patch literal 33546 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 K=yv!qD3gTe~ HDWM4fM>uw6 diff --git a/src/main.rs b/src/main.rs index a28a657..713cd85 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,16 +1,40 @@ #![feature(core_float_math)] +mod objects; mod ray; mod vec3; +use core::f32; use std::cmp::max; +use crate::objects::hit::Hit; +use crate::objects::sphere::Sphere; +use crate::objects::traits::{Hittable, Normal}; use crate::ray::Ray; use crate::vec3::{Colour, Vec3}; use log::info; use pretty_env_logger; -fn ray_colour(r: &Ray) -> Colour { +fn ray_colour(hittables: &Vec, r: &Ray) -> Colour +where + T: Hittable + Normal, +{ + let mut closest: Option = None; + for hittable in hittables { + if let Some(hit) = hittable.hit(r) { + // hit happens in front of camera and is closer than closest + if *hit.t() > 0. && (closest.is_none() || closest.as_ref().unwrap().t() > hit.t()) { + closest = Some(hit); + } + } + } + + if let Some(hit) = closest { + let n = hit.n(); + return 0.5 * Colour::new(n.x() + 1., n.y() + 1., n.z() + 1.); + } + + // background let unit_dir = r.dir().get_unit(); let a = 0.5 * (unit_dir.y() + 1.); @@ -19,8 +43,8 @@ fn ray_colour(r: &Ray) -> Colour { fn main() { // use structs so rust-analyzer can provide lsp - let v = Vec3::new(255., 0., 255.); - let _ = Ray::new(v.clone(), v.clone()); + let s = Sphere::new(Vec3::new(0., 0., -1.), 0.5); + let objects = vec![s]; pretty_env_logger::init(); @@ -56,7 +80,7 @@ fn main() { let r = Ray::new(camera_center, ray_dir); let pixel = imgbuf.get_pixel_mut(i, j); - *pixel = ray_colour(&r).output(); + *pixel = ray_colour(&objects, &r).output(); } } diff --git a/src/objects.rs b/src/objects.rs new file mode 100644 index 0000000..b7c1286 --- /dev/null +++ b/src/objects.rs @@ -0,0 +1,3 @@ +pub mod hit; +pub mod sphere; +pub mod traits; diff --git a/src/objects/hit.rs b/src/objects/hit.rs new file mode 100644 index 0000000..e801ec7 --- /dev/null +++ b/src/objects/hit.rs @@ -0,0 +1,25 @@ +use crate::vec3::Vec3; + +pub struct Hit { + t: f32, + p: Vec3, + n: Vec3, +} + +impl Hit { + pub fn new(t: f32, p: Vec3, n: Vec3) -> Hit { + Hit { t: t, p: p, n: n } + } + + pub fn t(&self) -> &f32 { + &self.t + } + + pub fn p(&self) -> &Vec3 { + &self.p + } + + pub fn n(&self) -> &Vec3 { + &self.n + } +} diff --git a/src/objects/sphere.rs b/src/objects/sphere.rs new file mode 100644 index 0000000..79f464a --- /dev/null +++ b/src/objects/sphere.rs @@ -0,0 +1,44 @@ +use core::f32::math::sqrt; + +use crate::objects::hit::Hit; +use crate::objects::traits::{Hittable, Normal}; +use crate::Vec3; + +pub struct Sphere { + center: Vec3, + radius: f32, +} + +impl Sphere { + pub fn new(center: Vec3, radius: f32) -> Sphere { + Sphere { + center: center, + radius: radius, + } + } +} + +impl Hittable for Sphere { + fn hit(&self, r: &crate::ray::Ray) -> Option { + let oc = self.center - r.origin(); + let a = r.dir().dot(r.dir()); + let b = -2. * r.dir().dot(&oc); + let c = oc.dot(&oc) - self.radius * self.radius; + + let d = b * b - 4. * a * c; + + if d < 0. { + None + } else { + let t = (-b - sqrt(d)) / (2.0 * a); + let p = r.at(t); + Some(Hit::new(t, p, self.normal_at(&p))) + } + } +} + +impl Normal for Sphere { + fn normal_at(&self, p: &Vec3) -> Vec3 { + (*p - self.center).get_unit() + } +} diff --git a/src/objects/traits.rs b/src/objects/traits.rs new file mode 100644 index 0000000..48d571e --- /dev/null +++ b/src/objects/traits.rs @@ -0,0 +1,11 @@ +use crate::objects::hit::Hit; +use crate::Ray; +use crate::Vec3; + +pub trait Hittable { + fn hit(&self, r: &Ray) -> Option; +} + +pub trait Normal { + fn normal_at(&self, p: &Vec3) -> Vec3; +} diff --git a/src/ray.rs b/src/ray.rs index db8475a..80f23da 100644 --- a/src/ray.rs +++ b/src/ray.rs @@ -6,7 +6,7 @@ pub struct Ray { } impl Ray { - pub fn at(self, t: f32) -> Vec3 { + pub fn at(&self, t: f32) -> Vec3 { self.origin + t * self.dir } diff --git a/src/vec3.rs b/src/vec3.rs index 3ba2014..f67018d 100644 --- a/src/vec3.rs +++ b/src/vec3.rs @@ -46,15 +46,11 @@ impl Vec3 { (self.r * self.r + self.g * self.g + self.b * self.b) as f32 } - pub fn dot(self, other: Vec3) -> Vec3 { - Vec3 { - r: self.r * other.r, - g: self.g * other.g, - b: self.b * other.b, - } + pub fn dot(&self, other: &Vec3) -> f32 { + self.r * other.r + self.g * other.g + self.b * other.b } - pub fn cross(self, other: Vec3) -> Vec3 { + pub fn cross(&self, other: &Vec3) -> Vec3 { Vec3 { r: self.g * other.b - self.b * other.g, g: self.b * other.r - self.r * other.b, @@ -142,6 +138,17 @@ impl AddAssign for Vec3 { }; } } +impl Sub<&Vec3> for Vec3 { + type Output = Self; + + fn sub(self, rhs: &Self) -> Self { + Self { + r: self.r - rhs.r, + g: self.g - rhs.g, + b: self.b - rhs.b, + } + } +} impl Sub for Vec3 { type Output = Self;