diff --git a/output.png b/output.png index fdbd692..8cca54b 100644 Binary files a/output.png and b/output.png differ 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;