diff options
Diffstat (limited to 'src/main.rs')
-rw-r--r-- | src/main.rs | 115 |
1 files changed, 115 insertions, 0 deletions
diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..7ec82e6 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,115 @@ +mod camera; +mod hittable; +mod material; +mod ray; +mod util; +mod vec3; +mod image; +mod texture; +mod scenes; + +use std::{sync::{Arc, mpsc}, thread}; + +use camera::Camera; +use hittable::Hittable; +use image::Image; +use ray::Ray; +use vec3::{Vec3, Color}; +use scenes::get_scene; + +struct PixelUpdate { + color: Color, + x: usize, + y: usize, +} + +fn ray_color(ray: &Ray, background: &Color, world: &dyn Hittable, depth: u32) -> Color { + if depth <= 0 { + return Color { + x: 0.0, + y: 0.0, + z: 0.0, + }; + } + match world.hit(ray, 0.001, f64::INFINITY) { + None => background.clone(), + Some(rec) => { + let mut scattered = Ray::new(); + let mut attenuation = Color::new(); + if let Some(material) = &rec.material { + let emitted = material.emitted(rec.u, rec.v, &rec.p); + if !material.scatter(&ray, &rec, &mut attenuation, &mut scattered) { + emitted + } else { + emitted + attenuation * ray_color(&scattered, background, world, depth - 1) + } + } else { + Color { x: 0.0, y: 0.0, z: 0.0 } + } + }, + } +} + +fn render(image_width: u32, image_height: u32, samples_per_pixel: u32, max_depth: u32, world: Arc<dyn Hittable>, background: Color, camera: Arc<Camera>, tx: mpsc::Sender<PixelUpdate>) { + for j in (0..image_height).rev() { + for i in 0..image_width { + for _ in 0..samples_per_pixel { + let u = ((i as f64) + rand::random::<f64>()) / ((image_width - 1) as f64); + let v = ((j as f64) + rand::random::<f64>()) / ((image_height - 1) as f64); + let ray = camera.get_ray(u, v); + + tx.send(PixelUpdate { color: ray_color(&ray, &background, world.as_ref(), max_depth), x: i as usize, y: j as usize}).unwrap(); + } + } + } +} + +fn main() { + // Image + const ASPECT_RATIO: f64 = 16.0 / 9.0; + //const ASPECT_RATIO: f64 = 1.0; + const IMAGE_WIDTH: u32 = 600; + const IMAGE_HEIGHT: u32 = (IMAGE_WIDTH as f64 / ASPECT_RATIO) as u32; + const SAMPLES_PER_PIXEL: u32 = 30; + const MAX_DEPTH: u32 = 50; + const THREAD_COUNT: u32 = 8; + const TIME_START: f64 = 0.0; + const TIME_END: f64 = 1.0; + // World + let (world, lookfrom, lookat, vfov, aperture, background) = get_scene(std::env::args().nth(1).unwrap_or("0".to_string()).trim().parse().unwrap_or(0)); + + // Camera + let vup = Vec3 { x: 0.0, y: 1.0, z: 0.0 }; + let dist_to_focus = 10.0; + let cam = Arc::new(Camera::new(lookfrom, lookat, vup, vfov, ASPECT_RATIO, aperture, dist_to_focus, TIME_START, TIME_END)); + // Render + let mut final_image = Image::new(IMAGE_WIDTH as usize, IMAGE_HEIGHT as usize); + let (tx, rx) = mpsc::channel::<PixelUpdate>(); + for _ in 0..THREAD_COUNT { + let sender = tx.clone(); + let world_ref = world.clone(); + let camera_ref = cam.clone(); + let background_clone = background.clone(); + thread::spawn( || { + render(IMAGE_WIDTH, IMAGE_HEIGHT, SAMPLES_PER_PIXEL / THREAD_COUNT, MAX_DEPTH, world_ref, background_clone, camera_ref, sender); + }); + } + let expected_updates: u64 = (SAMPLES_PER_PIXEL / THREAD_COUNT) as u64 * THREAD_COUNT as u64 * IMAGE_HEIGHT as u64 * IMAGE_WIDTH as u64; + let print_frequency: u64 = (SAMPLES_PER_PIXEL / THREAD_COUNT) as u64 * THREAD_COUNT as u64 * IMAGE_WIDTH as u64; + let mut update_count: u64 = 0; + loop { + if let Ok(update) = rx.try_recv() { + update_count += 1; + final_image.add_sample(update.x, update.y, update.color); + if update_count % print_frequency == 0 { + eprint!("\rCurrent completion: {:.2}%", (update_count as f64 / expected_updates as f64) * 100.0) + } + } else { + if Arc::strong_count(&world) == 1 { + break + } + } + } + final_image.write(&mut std::io::stdout()); + eprintln!("\nDone."); +} |