Skip to content

Commit

Permalink
Merge pull request #14 from soumyasen1809/tasks
Browse files Browse the repository at this point in the history
Parallelize using Rayon
  • Loading branch information
soumyasen1809 authored Aug 16, 2024
2 parents a947e1d + 9cb1939 commit 3ec5954
Show file tree
Hide file tree
Showing 9 changed files with 152 additions and 55 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/target
/.[Vv]scode
*.ppm
*.png
53 changes: 53 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ authors = ["Sen"]
[dependencies]
lib ={ path = "lib"} # needed for the tests to work
rand="0.3.14"
rayon = "1.10.0"

[[bin]]
name = "bin"
Expand Down
3 changes: 2 additions & 1 deletion lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ license = "GNU GENERAL PUBLIC LICENSE"
authors = ["Sen"]

[dependencies]
rand="0.3.14"
rand="0.3.14"
rayon = "1.10.0"
42 changes: 28 additions & 14 deletions lib/src/utilities/camera.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use rayon::prelude::*;

use rand::Rng;

use super::{
Expand Down Expand Up @@ -58,20 +60,32 @@ impl Camera {
let file_path = "image_test.ppm";
let mut file = File::create(&file_path).unwrap();
writeln!(file, "P3\n{} {}\n255", self.image_width, self.image_height).unwrap();
for y_index in 0..self.image_height {
println!("Remaining scanlines: {}", self.image_height - y_index); // Adding a Progress Indicator
for x_index in 0..self.image_width {
let mut pixel_color: Color = Color::new(0.0, 0.0, 0.0);
for _ in 0..self.samples_per_pixel {
let ray_sent: Ray = self.get_ray(x_index, y_index);
pixel_color += Self::ray_color(ray_sent, self.max_depth, &world[..]);
}

let write_res = (pixel_color * self.pixel_samples_scale).write_color(&mut file);
match write_res {
Err(e) => println!("Error in writing result to file: {}", e),
_ => (), // The () is just the unit value, so nothing will happen
}

let pixel_color_vec: Vec<Color> = (0..self.image_height)
.into_par_iter()
.flat_map(|y_index| {
// If we use a .map(..) here, we will get output as Vec<Vec<Color>> instead
(0..self.image_width)
.into_par_iter()
.map(|x_index| {
let pixel_color: Color = (0..self.samples_per_pixel)
.into_par_iter()
.map(|_| {
let ray_sent: Ray = self.get_ray(x_index, y_index);
Self::ray_color(ray_sent, self.max_depth, &world[..])
})
.sum(); // need to implement sum trait for Color
return pixel_color; // Return the Color from the map closure
})
.collect::<Vec<Color>>() // Collect the inner Vec<Color>
})
.collect(); // Collect the outer Vec<Color>

for pixel in pixel_color_vec.iter() {
let write_res = (*pixel * self.pixel_samples_scale).write_color(&mut file);
match write_res {
Err(e) => println!("Error in writing result to file: {}", e),
_ => (), // The () is just the unit value, so nothing will happen
}
}

Expand Down
12 changes: 12 additions & 0 deletions lib/src/utilities/color.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::io::Write;
use std::iter::Sum;
use std::ops::Add;
use std::{
fs::File,
Expand Down Expand Up @@ -83,6 +84,17 @@ impl Add for Color {
}
}

impl Sum for Color {
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
// iter.fold() is a method in Rust that allows you to
// accumulate or reduce the elements of an iterator into a single value.
// init: The initial value of the accumulator, which is passed to
// the closure as the first argument in the first iteration.
// E.g.: Summing a list: numbers.iter().fold(0, |a, &b| a + b);
iter.fold(Color::default(), |a, b| a + b)
}
}

impl MulAssign for Color {
fn mul_assign(&mut self, rhs: Self) {
self.red *= rhs.red;
Expand Down
4 changes: 2 additions & 2 deletions lib/src/utilities/geometry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ use super::{
vector3::Vector3,
};

pub trait Hittable {
pub trait Hittable: Send + Sync {
fn hit(&self, ray: Ray, ray_interval: Interval) -> Option<HitRecord>;
}

impl<T> Hittable for T
where
T: AsRef<[Box<dyn Hittable>]>,
T: AsRef<[Box<dyn Hittable>]> + Send + Sync,
{
fn hit(&self, ray: Ray, ray_interval: Interval) -> Option<HitRecord> {
let t_min: f64 = ray_interval.min;
Expand Down
2 changes: 1 addition & 1 deletion lib/src/utilities/material.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ pub struct Scatter {
}

// https://github.com/ebkalderon/ray-tracing-in-one-weekend/commits/master/?before=afc5b8807ba4a342b09c83361968e7ddc284fc12+70
pub trait Material {
pub trait Material: Send + Sync {
fn scatter(&self, incoming_ray: Ray, record: &HitRecord) -> Option<Scatter>;
}

Expand Down
89 changes: 52 additions & 37 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ use lib::utilities::{
point::Point3,
vector3::Vector3,
};
use rayon::prelude::*;

const NUMBER_BALLS: i32 = 5;

const ASPECT_RATIO: f64 = 16.0 / 9.0;
const IMAGE_WIDTH: i32 = 640;
const SAMPLES_PER_PIXEL: i32 = 500;
const IMAGE_WIDTH: i32 = 800;
const SAMPLES_PER_PIXEL: i32 = 200;
const MAX_DEPTH: i32 = 50;
const VERTICAL_FOV: f64 = 40.0;

Expand All @@ -32,45 +33,59 @@ fn main() {
)));

// Scene - small balls (random)
for x_index in -NUMBER_BALLS..NUMBER_BALLS {
for y_index in -NUMBER_BALLS..NUMBER_BALLS {
let choose_material_random: f64 = rand::thread_rng().r#gen::<f64>();
let center = Point3::new(
(x_index as f64) + (0.9 * rand::thread_rng().r#gen::<f64>()),
0.2,
(y_index as f64) + (0.9 * rand::thread_rng().r#gen::<f64>()),
);
if choose_material_random < 0.6 {
// Lambertian
let material_lambertian = Box::new(Lambertian::new(Color::new(
rand::thread_rng().r#gen::<f64>(),
rand::thread_rng().r#gen::<f64>(),
rand::thread_rng().r#gen::<f64>(),
)));
world.push(Box::new(Sphere::new(center, 0.2, material_lambertian)));
} else if choose_material_random < 0.85 {
// Metal
let material_metal = Box::new(Metal::new(
Color::new(
rand::thread_rng().r#gen::<f64>(),
rand::thread_rng().r#gen::<f64>(),
rand::thread_rng().r#gen::<f64>(),
),
rand::thread_rng().r#gen::<f64>(),
));
world.push(Box::new(Sphere::new(center, 0.2, material_metal)));
} else {
// Glass
let material_glass = Box::new(Dielectric::new(1.33));
world.push(Box::new(Sphere::new(center, 0.2, material_glass)));
}
}
}
let spheres_scene: Vec<Box<dyn Hittable>> = (-NUMBER_BALLS..NUMBER_BALLS)
.into_par_iter()
.flat_map(|x_index| {
(-NUMBER_BALLS..NUMBER_BALLS)
.into_par_iter()
.map(|y_index| {
let choose_material_random: f64 = rand::thread_rng().r#gen::<f64>();
let center = Point3::new(
(x_index as f64) + (0.9 * rand::thread_rng().r#gen::<f64>()),
0.2,
(y_index as f64) + (0.9 * rand::thread_rng().r#gen::<f64>()),
);
if choose_material_random < 0.4 {
// Lambertian
let material_lambertian = Box::new(Lambertian::new(Color::new(
rand::thread_rng().r#gen::<f64>(),
rand::thread_rng().r#gen::<f64>(),
rand::thread_rng().r#gen::<f64>(),
)));
return Box::new(Sphere::new(center, 0.2, material_lambertian))
as Box<dyn Hittable>;
} else if choose_material_random < 0.8 {
// Metal
let material_metal = Box::new(Metal::new(
Color::new(
rand::thread_rng().r#gen::<f64>(),
rand::thread_rng().r#gen::<f64>(),
rand::thread_rng().r#gen::<f64>(),
),
rand::thread_rng().r#gen::<f64>(),
));
return Box::new(Sphere::new(center, 0.2, material_metal))
as Box<dyn Hittable>;
} else {
// Glass
let material_glass = Box::new(Dielectric::new(1.33));
return Box::new(Sphere::new(center, 0.2, material_glass))
as Box<dyn Hittable>;
}
})
.collect::<Vec<Box<dyn Hittable>>>()
})
.collect();

world.extend(spheres_scene);
// If you don't need to use spheres_scene after pushing its contents to world, you can move the entire vector.
// Explanation: world.extend(spheres_scene) moves the contents of spheres_scene into world. The extend method consumes the vector, avoiding the need for cloning or referencing, and effectively transfers ownership of each Box<dyn Hittable> to world.
// Use moving (via extend) if you want to transfer ownership and don't need to use spheres_scene afterward, which is more efficient because it avoids unnecessary duplication.

// Scene - big balls with Glass material
let material_glass = Box::new(Dielectric::new(1.0 / 1.55));
world.push(Box::new(Sphere::new(
Point3::new(-8.0, 1.0, 0.0),
Point3::new(8.0, 1.0, 0.0),
1.0,
material_glass,
)));
Expand Down

0 comments on commit 3ec5954

Please sign in to comment.