ft: 5,6.1: sphere class + normals

This commit is contained in:
2026-04-15 13:32:29 +02:00
parent 67c1eb861d
commit ebf39d014b
8 changed files with 126 additions and 12 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 33 KiB

View File

@@ -1,16 +1,40 @@
#![feature(core_float_math)] #![feature(core_float_math)]
mod objects;
mod ray; mod ray;
mod vec3; mod vec3;
use core::f32;
use std::cmp::max; 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::ray::Ray;
use crate::vec3::{Colour, Vec3}; use crate::vec3::{Colour, Vec3};
use log::info; use log::info;
use pretty_env_logger; use pretty_env_logger;
fn ray_colour(r: &Ray) -> Colour { fn ray_colour<T>(hittables: &Vec<T>, r: &Ray) -> Colour
where
T: Hittable + Normal,
{
let mut closest: Option<Hit> = 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 unit_dir = r.dir().get_unit();
let a = 0.5 * (unit_dir.y() + 1.); let a = 0.5 * (unit_dir.y() + 1.);
@@ -19,8 +43,8 @@ fn ray_colour(r: &Ray) -> Colour {
fn main() { fn main() {
// use structs so rust-analyzer can provide lsp // use structs so rust-analyzer can provide lsp
let v = Vec3::new(255., 0., 255.); let s = Sphere::new(Vec3::new(0., 0., -1.), 0.5);
let _ = Ray::new(v.clone(), v.clone()); let objects = vec![s];
pretty_env_logger::init(); pretty_env_logger::init();
@@ -56,7 +80,7 @@ fn main() {
let r = Ray::new(camera_center, ray_dir); let r = Ray::new(camera_center, ray_dir);
let pixel = imgbuf.get_pixel_mut(i, j); let pixel = imgbuf.get_pixel_mut(i, j);
*pixel = ray_colour(&r).output(); *pixel = ray_colour(&objects, &r).output();
} }
} }

3
src/objects.rs Normal file
View File

@@ -0,0 +1,3 @@
pub mod hit;
pub mod sphere;
pub mod traits;

25
src/objects/hit.rs Normal file
View File

@@ -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
}
}

44
src/objects/sphere.rs Normal file
View File

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

11
src/objects/traits.rs Normal file
View File

@@ -0,0 +1,11 @@
use crate::objects::hit::Hit;
use crate::Ray;
use crate::Vec3;
pub trait Hittable {
fn hit(&self, r: &Ray) -> Option<Hit>;
}
pub trait Normal {
fn normal_at(&self, p: &Vec3) -> Vec3;
}

View File

@@ -6,7 +6,7 @@ pub struct Ray {
} }
impl Ray { impl Ray {
pub fn at(self, t: f32) -> Vec3 { pub fn at(&self, t: f32) -> Vec3 {
self.origin + t * self.dir self.origin + t * self.dir
} }

View File

@@ -46,15 +46,11 @@ impl Vec3 {
(self.r * self.r + self.g * self.g + self.b * self.b) as f32 (self.r * self.r + self.g * self.g + self.b * self.b) as f32
} }
pub fn dot(self, other: Vec3) -> Vec3 { pub fn dot(&self, other: &Vec3) -> f32 {
Vec3 { self.r * other.r + self.g * other.g + self.b * other.b
r: self.r * other.r,
g: self.g * other.g,
b: self.b * other.b,
}
} }
pub fn cross(self, other: Vec3) -> Vec3 { pub fn cross(&self, other: &Vec3) -> Vec3 {
Vec3 { Vec3 {
r: self.g * other.b - self.b * other.g, r: self.g * other.b - self.b * other.g,
g: self.b * other.r - self.r * other.b, g: self.b * other.r - self.r * other.b,
@@ -142,6 +138,17 @@ impl AddAssign<f32> 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<Vec3> for Vec3 { impl Sub<Vec3> for Vec3 {
type Output = Self; type Output = Self;