diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml deleted file mode 100644 index f8e417e..0000000 --- a/.gitea/workflows/ci.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: CI - -on: - push: - pull_request: - -jobs: - test: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Install Rust - uses: dtolnay/rust-toolchain@stable - - - name: Build - run: cargo build --release --verbose - - - name: Run tests - run: cargo test --verbose diff --git a/.gitea/workflows/image.yml b/.gitea/workflows/image.yml deleted file mode 100644 index c337d51..0000000 --- a/.gitea/workflows/image.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: Render Image - -on: - push: - workflow_dispatch: - -jobs: - render: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Install Rust - uses: dtolnay/rust-toolchain@stable - - - name: Build - run: cargo build --release - - - name: Run ray tracer - run: ./target/release/rust-ray - - - name: Upload image - uses: https://github.com/christopherHX/gitea-upload-artifact@v4 - with: - name: rendered-image - path: foo.ppm diff --git a/Cargo.lock b/Cargo.lock index 7f2d519..2c46dbd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -40,6 +40,37 @@ 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" @@ -186,11 +217,32 @@ 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 a637a3c..e154253 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,3 +5,4 @@ edition = "2024" [dependencies] rand = "0.10.0" +rayon = "1.11.0" diff --git a/src/main.rs b/src/main.rs index 4c9838b..0e3ee25 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,27 +9,31 @@ use render::{World,Camera}; use vec3::{Point3,Vec3}; use material::{Metal,Lambertian,Material}; +use std::sync::Arc; fn main() { let aspect_ratio = 16.0 / 9.0; - let image_width = 1920; + let image_width = 800; 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: Box = Box::new(m); + let world_m: Arc = Arc::new(m); let m = Lambertian::new(Vec3::new(0.1, 0.2, 0.5)); - let center_m: Box = Box::new(m); + let center_m: Arc = Arc::new(m); let m = Metal::new(Vec3::new(0.8, 0.8, 0.8), 0.3); - let left_m: Box = Box::new(m); + let left_m: Arc = Arc::new(m); + let m = Metal::new(Vec3::new(0.8, 0.6, 0.2), 0.5); - let right_m: Box = Box::new(m); + let right_m: Arc = Arc::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 camera = Camera::new(aspect_ratio,image_width,focal_length,viewport_height,camera_center); 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/object.rs b/src/object.rs index 5ebe938..3eea3ff 100644 --- a/src/object.rs +++ b/src/object.rs @@ -2,21 +2,22 @@ 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<'a> { +pub struct HitRecord { point: Point3, normal: Vec3, root: f64, front_face: bool, - material: &'a dyn Material, + material: Arc, } -impl HitRecord <'_> { +impl HitRecord { pub fn normal(&self) -> Vec3 { self.normal } @@ -43,17 +44,17 @@ impl HitRecord <'_> { pub struct Sphere { radius: f64, center: Point3, - material: Box + material: Arc } impl Sphere { - pub fn new(radius: f64, center: Point3, material: Box) -> Self { + pub fn new(radius: f64, center: Point3, material: Arc) -> 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()); @@ -79,7 +80,7 @@ impl Hittable for Sphere { point, normal, front_face: false, - material: self.material.as_ref(), + material: self.material.clone() }; tmp.set_face_normal(ray, normal); Some(tmp) diff --git a/src/render.rs b/src/render.rs index 9401573..91763e3 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(); @@ -46,34 +46,27 @@ pub struct Camera { } impl Camera { - pub fn new(aspect_ratio:f64,image_width: i32, focal_length:f64, look_from: Point3, look_at: Point3,fov:f64) -> Self{ + pub fn new(aspect_ratio:f64,image_width: i32, focal_length:f64, viewport_height:f64, camera_center: Point3) -> 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 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 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 pixel_delta_u = viewport_u / image_width as f64; let pixel_delta_v = viewport_v / image_height as f64; - let viewport_upper_left = look_from.to_vec() - (w * focal_length) - viewport_u/2.0 - viewport_v/2.0; + 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 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:look_from,pixel_delta_u,pixel_delta_v} + Self{img,pixel00_loc,camera_center,pixel_delta_u,pixel_delta_v} } @@ -82,7 +75,7 @@ impl Camera { let height = self.img.height; let width = self.img.width; let total_pixels = height*width; - let num_samples = 25; + let num_samples = 5; //cast to const ref let const_self = &*self; @@ -97,8 +90,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; @@ -122,12 +115,9 @@ impl Camera { for t in threads { for c in t { let ((x,y),rgb) = c; - 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); - } + 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(); diff --git a/src/vec3.rs b/src/vec3.rs index 56bc0b5..0b4eaeb 100644 --- a/src/vec3.rs +++ b/src/vec3.rs @@ -1,6 +1,5 @@ use std::ops::{Add, Div, Mul, Sub}; -#[repr(align(32))] #[derive(Clone, Copy)] pub struct Vec3 { x: f64, @@ -8,7 +7,6 @@ pub struct Vec3 { z: f64, } -#[repr(align(32))] #[derive(Clone, Copy)] pub struct Point3 { x: f64, @@ -134,7 +132,6 @@ impl Vec3 { z: self.z, } } - pub fn dot(self, other: Vec3) -> f64 { self.x * other.x + self.y * other.y + self.z * other.z } @@ -152,11 +149,10 @@ impl Vec3 { } pub fn cross(self, other: Vec3) -> Vec3 { - 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, - } + 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 } } pub fn length_squared(self) -> f64 {