From 993136ea9e5c72247a246ed574ab1c3126c07dc1 Mon Sep 17 00:00:00 2001 From: k Date: Mon, 13 Apr 2026 11:13:34 -0400 Subject: [PATCH 1/7] Updated types to play nicer with threading --- src/main.rs | 14 +++++--------- src/object.rs | 7 ++++--- src/render.rs | 5 +++-- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/main.rs b/src/main.rs index 6c6428b..0e3ee25 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,7 +9,7 @@ use render::{World,Camera}; use vec3::{Point3,Vec3}; use material::{Metal,Lambertian,Material}; - +use std::sync::Arc; fn main() { @@ -21,21 +21,17 @@ fn main() { let m = Lambertian::new(Vec3::new(0.5,0.5,0.5)); - let b: Box = Box::new(m); - let world_m = std::rc::Rc::new(b); + let world_m: Arc = Arc::new(m); let m = Lambertian::new(Vec3::new(0.1, 0.2, 0.5)); - let b: Box = Box::new(m); - let center_m = std::rc::Rc::new(b); + let center_m: Arc = Arc::new(m); let m = Metal::new(Vec3::new(0.8, 0.8, 0.8), 0.3); - let b: Box = Box::new(m); - let left_m = std::rc::Rc::new(b); + let left_m: Arc = Arc::new(m); let m = Metal::new(Vec3::new(0.8, 0.6, 0.2), 0.5); - let b: Box = Box::new(m); - let right_m = std::rc::Rc::new(b); + let right_m: Arc = Arc::new(m); let mut camera = Camera::new(aspect_ratio,image_width,focal_length,viewport_height,camera_center); let mut world = World::new(); diff --git a/src/object.rs b/src/object.rs index b3c5852..c465e72 100644 --- a/src/object.rs +++ b/src/object.rs @@ -2,6 +2,7 @@ use crate::vec3::{Point3, Ray, Vec3, Interval}; use crate::material::{Material,Scatter}; use rand::prelude::*; +use std::sync::Arc; pub trait Hittable { @@ -13,7 +14,7 @@ pub struct HitRecord { normal: Vec3, root: f64, front_face: bool, - material: std::rc::Rc>, + material: Arc, } impl HitRecord { @@ -43,11 +44,11 @@ impl HitRecord { pub struct Sphere { radius: f64, center: Point3, - material: std::rc::Rc> + material: Arc } impl Sphere { - pub fn new(radius: f64, center: Point3, material:std::rc::Rc>) -> Self { + pub fn new(radius: f64, center: Point3, material: Arc) -> Self { Sphere { radius, center , material} } } diff --git a/src/render.rs b/src/render.rs index c28d22c..a13581f 100644 --- a/src/render.rs +++ b/src/render.rs @@ -1,11 +1,12 @@ use crate::object::{HitRecord, Hittable}; use crate::vec3::{Interval, Ray, Vec3, Point3}; use crate::image::Image; +use std::sync::Arc; use rand::prelude::*; pub struct World { - pub objects: Vec>, + pub objects: Vec>, } impl World { @@ -16,7 +17,7 @@ impl World { } pub fn add(&mut self, object: impl Hittable + 'static) { - self.objects.push(Box::new(object)); + self.objects.push(Arc::new(object)); } pub fn hit(&self, ray: Ray, interval:Interval) -> Option { From 8ec807c1471697e07a2e9eb4fbf7a682722c17d3 Mon Sep 17 00:00:00 2001 From: k Date: Mon, 13 Apr 2026 14:02:39 -0400 Subject: [PATCH 2/7] Working threading --- src/material.rs | 2 +- src/object.rs | 2 +- src/render.rs | 69 +++++++++++++++++++++++++++++++++---------------- 3 files changed, 49 insertions(+), 24 deletions(-) diff --git a/src/material.rs b/src/material.rs index c7d0efb..4ce924c 100644 --- a/src/material.rs +++ b/src/material.rs @@ -9,7 +9,7 @@ pub struct Scatter { pub ray: Ray, } -pub trait Material { +pub trait Material: Send + Sync { fn scatter(&self, r_in: &Ray, rec: &HitRecord, rng: &mut ThreadRng) -> Option; } diff --git a/src/object.rs b/src/object.rs index c465e72..3eea3ff 100644 --- a/src/object.rs +++ b/src/object.rs @@ -5,7 +5,7 @@ use rand::prelude::*; use std::sync::Arc; -pub trait Hittable { +pub trait Hittable: Send + Sync{ fn hit(&self, ray: Ray, interval:Interval) -> Option; } diff --git a/src/render.rs b/src/render.rs index a13581f..91763e3 100644 --- a/src/render.rs +++ b/src/render.rs @@ -2,6 +2,7 @@ use crate::object::{HitRecord, Hittable}; use crate::vec3::{Interval, Ray, Vec3, Point3}; use crate::image::Image; use std::sync::Arc; +use std::thread; use rand::prelude::*; @@ -70,29 +71,53 @@ impl Camera { pub fn render(&mut self, world: &World){ - let mut rng:ThreadRng = rand::rng(); - for y in 0..self.img.height { - for x in 0..self.img.width { - let (mut r,mut g,mut b) = (0.0,0.0,0.0); - let num_samples=10.0; - let depth=10; - for _ in 0..num_samples as i32{ - let ray = self.get_ray(x,y,&mut rng); - let (tr,tg,tb)= trace(ray, &world,depth,&mut rng); - r+= tr; - g+= tg; - b+= tb; - } - r /= num_samples; - g /= num_samples; - b /= num_samples; + let thread_count = thread::available_parallelism().unwrap().get() as i32; + let height = self.img.height; + let width = self.img.width; + let total_pixels = height*width; + let num_samples = 5; - r = r.clamp(0.0,1.0).sqrt(); - g = g.clamp(0.0,1.0).sqrt(); - b = b.clamp(0.0,1.0).sqrt(); - if let Some(px) = self.img.get_pixel(x,y){ - px.set_color(r,g,b); - } else{/*ignore*/} + //cast to const ref + let const_self = &*self; + + let threads:Vec> = thread::scope(|s|{ + (0..thread_count).map(|i|{ + s.spawn(move ||{ + let mut rng:ThreadRng = rand::rng(); + let start = (i*total_pixels)/thread_count; + let end = ((i+1)*total_pixels)/thread_count; + (start..end).map(|i|{ + let x = i%width; + let y = i/width; + let (mut r,mut g,mut b) = (0.0,0.0,0.0); + let ray = const_self.get_ray(x,y,&mut rng); + for _ in 0..num_samples{ + let (tr,tg,tb) = trace(ray, &world,10,&mut rng); + r+=tr; + g+=tg; + b+=tb; + } + + r/=num_samples as f64; + g/=num_samples as f64; + b/=num_samples as f64; + + r = r.clamp(0.0,1.0).sqrt(); + g = g.clamp(0.0,1.0).sqrt(); + b = b.clamp(0.0,1.0).sqrt(); + + ((x,y),(r,g,b)) + }).collect::>() + }) + }).collect::>().into_iter().map(|t|{t.join().unwrap()}).collect() + }); + + for t in threads { + for c in t { + let ((x,y),rgb) = c; + let px = self.img.get_pixel(x,y).unwrap(); + let (r,g,b)= rgb; + px.set_color(r,g,b); } } self.img.save("./foo.ppm").unwrap(); From b73b1209e4acd41d0b06b14af2741ccfc91ef9bf Mon Sep 17 00:00:00 2001 From: k Date: Mon, 13 Apr 2026 14:51:19 -0400 Subject: [PATCH 3/7] Movable camera --- src/main.rs | 2 +- src/render.rs | 25 ++++++++++++++++--------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/main.rs b/src/main.rs index 0e3ee25..ec56fc9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -33,7 +33,7 @@ fn main() { let m = Metal::new(Vec3::new(0.8, 0.6, 0.2), 0.5); let right_m: Arc = Arc::new(m); - let mut camera = Camera::new(aspect_ratio,image_width,focal_length,viewport_height,camera_center); + let mut camera = Camera::new(aspect_ratio,image_width,focal_length,Point3::new(-2.0,2.0,1.0),Point3::new(0.0,0.0,-1.0),90.0); let mut world = World::new(); world.add(Sphere::new(0.5, Point3::new(0.0, 0.0, -1.0),center_m)); world.add(Sphere::new(0.5, Point3::new(-1.0, 0.0, -1.0),left_m)); diff --git a/src/render.rs b/src/render.rs index 91763e3..c3aa5a8 100644 --- a/src/render.rs +++ b/src/render.rs @@ -46,27 +46,34 @@ pub struct Camera { } impl Camera { - pub fn new(aspect_ratio:f64,image_width: i32, focal_length:f64, viewport_height:f64, camera_center: Point3) -> Self{ + pub fn new(aspect_ratio:f64,image_width: i32, focal_length:f64, look_from: Point3, look_at: Point3,fov:f64) -> Self{ let image_height = (image_width as f64 / aspect_ratio) as i32; + + let theta = (fov as f64).to_radians(); + let h = (theta/2.0).tan(); + + let viewport_height = 2.0 * h * focal_length; let viewport_width = viewport_height * (image_width as f64 / image_height as f64); - let viewport_u = Vec3::new(viewport_width as f64, 0.0, 0.0); - let viewport_v = Vec3::new(0.0, -viewport_height as f64, 0.0); + let w = (look_from - look_at).to_vec().unit(); + let u = Vec3::new(0.0,1.0,0.0).cross(w).unit(); + let v = w.cross(u); + + + let viewport_u = u * viewport_width; + let viewport_v = (v*-1.0)*viewport_height; let pixel_delta_u = viewport_u / image_width as f64; let pixel_delta_v = viewport_v / image_height as f64; - let viewport_upper_left = (camera_center.to_vec() - - Vec3::new(0.0, 0.0, focal_length) - - viewport_u / 2.0 - - viewport_v / 2.0).to_point(); + let viewport_upper_left = look_from.to_vec() - (w * focal_length) - viewport_u/2.0 - viewport_v/2.0; - let pixel00_loc = viewport_upper_left + ((pixel_delta_u + pixel_delta_v) * 0.5).to_point(); + let pixel00_loc = (viewport_upper_left + ((pixel_delta_u + pixel_delta_v) * 0.5)).to_point(); let img = Image::new(image_width, image_height); - Self{img,pixel00_loc,camera_center,pixel_delta_u,pixel_delta_v} + Self{img,pixel00_loc,camera_center:look_from,pixel_delta_u,pixel_delta_v} } From 63d4cbb042ab4b700c80aed6097ec8ce8723e17c Mon Sep 17 00:00:00 2001 From: k Date: Sun, 26 Apr 2026 21:54:19 -0400 Subject: [PATCH 4/7] remove unused dep --- Cargo.lock | 52 ---------------------------------------------------- Cargo.toml | 1 - 2 files changed, 53 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2c46dbd..7f2d519 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -40,37 +40,6 @@ dependencies = [ "libc", ] -[[package]] -name = "crossbeam-deque" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" - -[[package]] -name = "either" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" - [[package]] name = "equivalent" version = "1.0.2" @@ -217,32 +186,11 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba" -[[package]] -name = "rayon" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" -dependencies = [ - "crossbeam-deque", - "crossbeam-utils", -] - [[package]] name = "rust-ray" version = "0.1.0" dependencies = [ "rand", - "rayon", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index e154253..a637a3c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,4 +5,3 @@ edition = "2024" [dependencies] rand = "0.10.0" -rayon = "1.11.0" From 5857244ca87210c052a8b4fd621f9872241a6378 Mon Sep 17 00:00:00 2001 From: k Date: Sun, 26 Apr 2026 21:54:39 -0400 Subject: [PATCH 5/7] better image --- src/main.rs | 7 ++----- src/render.rs | 4 ++-- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/main.rs b/src/main.rs index ec56fc9..73e125f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,11 +14,9 @@ use std::sync::Arc; fn main() { let aspect_ratio = 16.0 / 9.0; - let image_width = 800; + let image_width = 1920; let focal_length = 1.0; let viewport_height = 2.0; - let camera_center = Point3::new(0.0, 0.0, 0.0); - let m = Lambertian::new(Vec3::new(0.5,0.5,0.5)); let world_m: Arc = Arc::new(m); @@ -29,11 +27,10 @@ fn main() { let m = Metal::new(Vec3::new(0.8, 0.8, 0.8), 0.3); let left_m: Arc = Arc::new(m); - let m = Metal::new(Vec3::new(0.8, 0.6, 0.2), 0.5); let right_m: Arc = Arc::new(m); - let mut camera = Camera::new(aspect_ratio,image_width,focal_length,Point3::new(-2.0,2.0,1.0),Point3::new(0.0,0.0,-1.0),90.0); + let mut camera = Camera::new(aspect_ratio,image_width,focal_length,Point3::new(-1.0,1.0,1.0),Point3::new(0.0,0.0,-1.0),90.0); let mut world = World::new(); world.add(Sphere::new(0.5, Point3::new(0.0, 0.0, -1.0),center_m)); world.add(Sphere::new(0.5, Point3::new(-1.0, 0.0, -1.0),left_m)); diff --git a/src/render.rs b/src/render.rs index c3aa5a8..f4477e5 100644 --- a/src/render.rs +++ b/src/render.rs @@ -82,7 +82,7 @@ impl Camera { let height = self.img.height; let width = self.img.width; let total_pixels = height*width; - let num_samples = 5; + let num_samples = 25; //cast to const ref let const_self = &*self; @@ -97,8 +97,8 @@ impl Camera { let x = i%width; let y = i/width; let (mut r,mut g,mut b) = (0.0,0.0,0.0); - let ray = const_self.get_ray(x,y,&mut rng); for _ in 0..num_samples{ + let ray = const_self.get_ray(x,y,&mut rng); let (tr,tg,tb) = trace(ray, &world,10,&mut rng); r+=tr; g+=tg; From 8df48d38248db2773ac355ff4664956f5743245c Mon Sep 17 00:00:00 2001 From: k Date: Tue, 28 Apr 2026 16:21:18 -0400 Subject: [PATCH 6/7] Replace arc --- src/main.rs | 9 ++++----- src/object.rs | 17 ++++++++--------- src/render.rs | 2 +- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/main.rs b/src/main.rs index 73e125f..4c9838b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,7 +9,6 @@ use render::{World,Camera}; use vec3::{Point3,Vec3}; use material::{Metal,Lambertian,Material}; -use std::sync::Arc; fn main() { @@ -19,16 +18,16 @@ fn main() { let viewport_height = 2.0; let m = Lambertian::new(Vec3::new(0.5,0.5,0.5)); - let world_m: Arc = Arc::new(m); + let world_m: Box = Box::new(m); let m = Lambertian::new(Vec3::new(0.1, 0.2, 0.5)); - let center_m: Arc = Arc::new(m); + let center_m: Box = Box::new(m); let m = Metal::new(Vec3::new(0.8, 0.8, 0.8), 0.3); - let left_m: Arc = Arc::new(m); + let left_m: Box = Box::new(m); let m = Metal::new(Vec3::new(0.8, 0.6, 0.2), 0.5); - let right_m: Arc = Arc::new(m); + let right_m: Box = Box::new(m); let mut camera = Camera::new(aspect_ratio,image_width,focal_length,Point3::new(-1.0,1.0,1.0),Point3::new(0.0,0.0,-1.0),90.0); let mut world = World::new(); diff --git a/src/object.rs b/src/object.rs index 3eea3ff..5ebe938 100644 --- a/src/object.rs +++ b/src/object.rs @@ -2,22 +2,21 @@ use crate::vec3::{Point3, Ray, Vec3, Interval}; use crate::material::{Material,Scatter}; use rand::prelude::*; -use std::sync::Arc; pub trait Hittable: Send + Sync{ - fn hit(&self, ray: Ray, interval:Interval) -> Option; + fn hit(&self, ray: Ray, interval:Interval) -> Option>; } -pub struct HitRecord { +pub struct HitRecord<'a> { point: Point3, normal: Vec3, root: f64, front_face: bool, - material: Arc, + material: &'a dyn Material, } -impl HitRecord { +impl HitRecord <'_> { pub fn normal(&self) -> Vec3 { self.normal } @@ -44,17 +43,17 @@ impl HitRecord { pub struct Sphere { radius: f64, center: Point3, - material: Arc + material: Box } impl Sphere { - pub fn new(radius: f64, center: Point3, material: Arc) -> Self { + pub fn new(radius: f64, center: Point3, material: Box) -> Self { Sphere { radius, center , material} } } impl Hittable for Sphere { - fn hit(&self, ray: Ray, interval:Interval) -> Option { + fn hit(&self, ray: Ray, interval:Interval) -> Option> { let oc = self.center - ray.origin(); let a = ray.direction().length_squared(); let h = Vec3::dot(ray.direction(), oc.to_vec()); @@ -80,7 +79,7 @@ impl Hittable for Sphere { point, normal, front_face: false, - material: self.material.clone() + material: self.material.as_ref(), }; tmp.set_face_normal(ray, normal); Some(tmp) diff --git a/src/render.rs b/src/render.rs index f4477e5..142fcf4 100644 --- a/src/render.rs +++ b/src/render.rs @@ -21,7 +21,7 @@ impl World { self.objects.push(Arc::new(object)); } - pub fn hit(&self, ray: Ray, interval:Interval) -> Option { + pub fn hit(&self, ray: Ray, interval:Interval) -> Option> { let mut hit_anything = None; let mut closest_so_far = interval.max(); From b9ac41b45335047325de0c23568a5a297b87def2 Mon Sep 17 00:00:00 2001 From: k Date: Tue, 28 Apr 2026 19:32:16 -0400 Subject: [PATCH 7/7] Minor changes for speed up --- src/render.rs | 9 ++++++--- src/vec3.rs | 12 ++++++++---- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/render.rs b/src/render.rs index 142fcf4..9401573 100644 --- a/src/render.rs +++ b/src/render.rs @@ -122,9 +122,12 @@ impl Camera { for t in threads { for c in t { let ((x,y),rgb) = c; - let px = self.img.get_pixel(x,y).unwrap(); - let (r,g,b)= rgb; - px.set_color(r,g,b); + if let Some(px) = self.img.get_pixel(x,y){ + let (r,g,b)= rgb; + px.set_color(r,g,b); + } else { + eprintln!("Invalid pixel: ({} {})",x,y); + } } } self.img.save("./foo.ppm").unwrap(); diff --git a/src/vec3.rs b/src/vec3.rs index 0b4eaeb..56bc0b5 100644 --- a/src/vec3.rs +++ b/src/vec3.rs @@ -1,5 +1,6 @@ use std::ops::{Add, Div, Mul, Sub}; +#[repr(align(32))] #[derive(Clone, Copy)] pub struct Vec3 { x: f64, @@ -7,6 +8,7 @@ pub struct Vec3 { z: f64, } +#[repr(align(32))] #[derive(Clone, Copy)] pub struct Point3 { x: f64, @@ -132,6 +134,7 @@ impl Vec3 { z: self.z, } } + pub fn dot(self, other: Vec3) -> f64 { self.x * other.x + self.y * other.y + self.z * other.z } @@ -149,10 +152,11 @@ impl Vec3 { } pub fn cross(self, other: Vec3) -> Vec3 { - let x = self.y * other.z - self.z * other.y; - let y = self.z * other.x - self.x * other.z; - let z = self.x * other.y - self.y * other.x; - Vec3 { x, y, z } + Vec3{ + x: self.y * other.z - self.z * other.y, + y: self.z * other.x - self.x * other.z, + z: self.x * other.y - self.y * other.x, + } } pub fn length_squared(self) -> f64 {