multithreading && code cleanup

This commit is contained in:
2026-02-13 05:06:02 +01:00
parent 7ec01f634a
commit e84404a6de
9 changed files with 109 additions and 81 deletions

View File

@@ -6,4 +6,5 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
rand = "*"
rand = "0.7.0"
rayon = "1.8"

File diff suppressed because one or more lines are too long

View File

@@ -1,8 +1,6 @@
use std::io::Write;
use crate::{vec3::*, rtweekend::clamp};
fn get_scaled_color_components(c: Color, samples_per_pixel: i32) -> (u8, u8, u8) {
pub fn get_scaled_color_components(c: Color, samples_per_pixel: i32) -> (u8, u8, u8) {
let r = c.x();
let g = c.y();
let b = c.z();
@@ -18,16 +16,4 @@ fn get_scaled_color_components(c: Color, samples_per_pixel: i32) -> (u8, u8, u8)
(255.0 * clamp(g, 0.0, 1.0)) as u8,
(255.0 * clamp(b, 0.0, 1.0)) as u8,
)
}
pub fn write_color<T: Write>(writer: &mut T, pixel_color: Color, samples_per_pixel: i32) {
let (r, g, b) = get_scaled_color_components(pixel_color, samples_per_pixel);
write!(writer, "{r} {g} {b} ").expect("Can write pixel data");
}
pub fn write_color_bin<T: Write>(writer: &mut T, pixel_color: Color, samples_per_pixel: i32) {
let (r, g, b) = get_scaled_color_components(pixel_color, samples_per_pixel);
let binary_data = [r, g, b];
writer.write(&binary_data).expect("Can write binary pixel data!");
}

View File

@@ -26,6 +26,6 @@ impl HitRecord<'_> {
}
}
pub trait Hittable {
fn hit(&mut self, r: &Ray, t_min: f64, t_max: f64) -> Option<HitRecord>;
pub trait Hittable: Send + Sync {
fn hit(&self, r: &Ray, t_min: f64, t_max: f64) -> Option<HitRecord<'_>>;
}

View File

@@ -19,11 +19,11 @@ impl HittableList {
}
impl Hittable for HittableList {
fn hit(&mut self, r: &crate::ray::Ray, t_min: f64, t_max: f64) -> Option<HitRecord> {
fn hit(&self, r: &crate::ray::Ray, t_min: f64, t_max: f64) -> Option<HitRecord<'_>> {
let mut temp_rec = None;
let mut closest_so_far = t_max;
for object in &mut self.objects {
for object in &self.objects {
if let Some(rec) = object.hit(r, t_min, closest_so_far) {
closest_so_far = rec.t;
temp_rec = Some(rec);

View File

@@ -1,37 +1,51 @@
pub mod vec3;
pub mod camera;
pub mod color;
pub mod ray;
pub mod sphere;
pub mod hittable;
pub mod hittable_list;
pub mod rtweekend;
pub mod camera;
pub mod material;
pub mod ray;
pub mod rtweekend;
pub mod sphere;
pub mod vec3;
use std::{io::{BufWriter, Write, stdout}, fs::File, time::Instant};
use rayon::prelude::*;
use std::{
io::{stdout, Write},
sync::atomic::{AtomicUsize, Ordering},
};
use crate::{
material::*,
camera::*,
color::*,
sphere::*,
rtweekend::*,
hittable_list::*,
vec3::*,
ray::*,
hittable::*,
camera::*, material::{Lambertian, Dielectric},
hittable_list::*,
material::*,
material::{Dielectric, Lambertian},
ray::*,
rtweekend::*,
sphere::*,
vec3::*,
};
fn random_scene() -> HittableList {
let mut world = HittableList::new_empty();
static GROUND_MATERIAL: Lambertian = Lambertian::new(Color::new(0.5, 0.5, 0.5));
world.add(Box::new(Sphere::new(Point3::new(0.0, -1000.0, 0.0), 1000.0, &GROUND_MATERIAL)));
world.add(Box::new(Sphere::new(
Point3::new(0.0, -1000.0, 0.0),
1000.0,
&GROUND_MATERIAL,
)));
for a in 0..12 {
for b in 0..12 {
let choose_mat = random_f64();
let center = Point3::new(a as f64 + 0.9*random_f64(), 0.2, b as f64 + 0.9*random_f64());
let center = Point3::new(
a as f64 + 0.9 * random_f64(),
0.2,
b as f64 + 0.9 * random_f64(),
);
if (center - Point3::new(4.0, 0.2, 0.0)).length() > 0.9 {
if choose_mat < 0.8 {
@@ -50,18 +64,30 @@ fn random_scene() -> HittableList {
}
static MATERIAL1: Dielectric = Dielectric::new(1.5);
world.add(Box::new(Sphere::new(Point3::new(0.0, 1.0, 0.0), 1.0, &MATERIAL1)));
world.add(Box::new(Sphere::new(
Point3::new(0.0, 1.0, 0.0),
1.0,
&MATERIAL1,
)));
static MATERIAL2: Lambertian = Lambertian::new(Color::new(0.4, 0.2, 0.1));
world.add(Box::new(Sphere::new(Point3::new(-4.0, 1.0, 0.0), 1.0, &MATERIAL2)));
world.add(Box::new(Sphere::new(
Point3::new(-4.0, 1.0, 0.0),
1.0,
&MATERIAL2,
)));
static MATERIAL3: Metal = Metal::new(Color::new(0.7, 0.6, 0.5), 0.0);
world.add(Box::new(Sphere::new(Point3::new(4.0, 1.0, 0.0), 1.0, &MATERIAL3)));
world.add(Box::new(Sphere::new(
Point3::new(4.0, 1.0, 0.0),
1.0,
&MATERIAL3,
)));
world
}
fn ray_color(r: Ray, world: &mut dyn Hittable, depth: i32) -> Color {
fn ray_color(r: Ray, world: &dyn Hittable, depth: i32) -> Color {
// Limit the bounces
if depth <= 0 {
return Color::new(0.0, 0.0, 0.0);
@@ -71,8 +97,11 @@ fn ray_color(r: Ray, world: &mut dyn Hittable, depth: i32) -> Color {
let mut scattered = Ray::new_empty();
let mut attenuation = Color::new_empty();
if rec.mat_ptr.scatter(r, rec, &mut attenuation, &mut scattered) {
return attenuation * ray_color(scattered, world, depth - 1);
if rec
.mat_ptr
.scatter(r, rec, &mut attenuation, &mut scattered)
{
return attenuation * ray_color(scattered, world, depth - 1);
}
return Color::new_empty();
}
@@ -86,19 +115,19 @@ fn ray_color(r: Ray, world: &mut dyn Hittable, depth: i32) -> Color {
fn main() {
// Image
let aspect_ratio: f64 = 1.0 / 1.0;
let image_width: i32 = 1000;
let image_height: i32 = (image_width as f64 / aspect_ratio as f64) as i32;
let samples_per_pixel: i32 = 200;
let image_height: i32 = 1000;
let image_width: i32 = (image_height as f64 / aspect_ratio as f64) as i32;
let samples_per_pixel: i32 = 50;
let max_depth: i32 = 4;
// World
let mut world = random_scene();
let world = random_scene();
// Camera
let lookfrom = Point3::new(-13.0, 2.0, 3.0);
let lookat = Point3::new(0.0, 0.0, 0.0);
let vup = Vec3::new(0.0, 1.0, 0.0);
let dist_to_focus = 10.0;//(lookfrom-lookat).length_squared();
let dist_to_focus = 10.0; //(lookfrom-lookat).length_squared();
let cam = Camera::new(
lookfrom,
@@ -107,37 +136,51 @@ fn main() {
30.0,
aspect_ratio,
0.0,
dist_to_focus
dist_to_focus,
);
// Render
//let mut rng = rand::thread_rng();
let now = Instant::now();
let mut file = BufWriter::new(File::create("img.ppm").expect("File creation failed"));
rayon::ThreadPoolBuilder::new()
.num_threads(11)
.build_global()
.unwrap();
write!(file, "P6\n{image_width} {image_height}\n255\n").expect("Error while writing Magic Byte");
let mut buffer = vec![0u8; (image_width * image_height * 3) as usize];
for j in (0..image_width).rev() {
for i in 0..image_height {
let mut pixel_color = Color::new(0.0, 0.0, 0.0);
for _ in 0..samples_per_pixel {
let u = (i as f64 + random_f64()) / (image_width - 1) as f64;
let v = (j as f64 + random_f64()) / (image_height - 1) as f64;
let r: Ray = cam.get_ray(u, v);
pixel_color += ray_color(r, &mut world, max_depth)
let rows_completed = AtomicUsize::new(0);
let total_rows = image_height as usize;
buffer
.par_chunks_mut((image_width * 3) as usize)
.enumerate()
.for_each(|(index, row_slice)| {
let j = image_height - 1 - index as i32;
for i in 0..image_width {
let mut pixel_color = Color::new(0.0, 0.0, 0.0);
for _ in 0..samples_per_pixel {
let u = (i as f64 + random_f64()) / (image_width - 1) as f64;
let v = (j as f64 + random_f64()) / (image_height - 1) as f64;
let r = cam.get_ray(u, v);
pixel_color += ray_color(r, &world, max_depth);
}
let (ir, ig, ib) = get_scaled_color_components(pixel_color, samples_per_pixel);
row_slice[(i as usize) * 3] = ir;
row_slice[(i as usize) * 3 + 1] = ig;
row_slice[(i as usize) * 3 + 2] = ib;
}
write_color_bin(&mut file, pixel_color, samples_per_pixel);
}
print!(
"[ {:.2} % ]\r",
(1.0 - ((j + 1) as f64 / image_width as f64)) * 100.0
);
stdout().flush().expect("Can flush stdout");
}
println!("Took {:.6}ms", now.elapsed().as_millis());
println!("Took {:.6}s", now.elapsed().as_secs_f64());
println!("Took {:.6}m", now.elapsed().as_secs_f64() / 60.0);
println!("Took {:.6}h", now.elapsed().as_secs_f64() / 3600.0);
}
// progress
let completed = rows_completed.fetch_add(1, Ordering::Relaxed) + 1;
if completed % 10 == 0 || completed == total_rows {
print!(
"\r[ {:.2} % ] Rendering...",
(completed as f64 / total_rows as f64) * 100.0
);
let _ = stdout().flush();
}
});
}

View File

@@ -16,7 +16,7 @@ use crate::vec3::{Color, random_unit_vector, reflect, refract};
*/
pub trait Material {
pub trait Material: Send + Sync {
fn scatter(&self, r_in: Ray, rec: HitRecord, attenuation: &mut Color, scattered: &mut Ray) -> bool;
}

View File

@@ -16,7 +16,7 @@ pub fn random_f64() -> f64 {
// PLEASE CHANGE
pub fn random_range_f64(min: f64, max: f64) -> f64 {
rand::thread_rng().gen_range(min..=max)
rand::thread_rng().gen_range(min, max)
}
pub fn clamp(x: f64, min: f64, max: f64) -> f64 {

View File

@@ -15,7 +15,7 @@ impl Sphere<'_> {
}
impl Hittable for Sphere<'_> {
fn hit(&mut self, r: &crate::ray::Ray, t_min: f64, t_max: f64) -> Option<HitRecord> {
fn hit(&self, r: &crate::ray::Ray, t_min: f64, t_max: f64) -> Option<HitRecord<'_>> {
let oc = r.origin() - self.center;
let a = r.direction().length_squared();
let half_b = oc.dot(&r.direction());