From 96e74b3ebfaa6c6179e05eb8d7801b30c834af52 Mon Sep 17 00:00:00 2001 From: Bill Thiede Date: Thu, 24 Oct 2019 14:27:40 -0700 Subject: [PATCH] rtiow: add greyscale images for debugging. --- rtiow/src/output.rs | 159 ++++++++++++++++++++++------------- rtiow/src/renderer.rs | 12 ++- rtiow/src/scenes/tutorial.rs | 4 +- 3 files changed, 112 insertions(+), 63 deletions(-) diff --git a/rtiow/src/output.rs b/rtiow/src/output.rs index 9994be3..9d29d5f 100644 --- a/rtiow/src/output.rs +++ b/rtiow/src/output.rs @@ -5,7 +5,7 @@ use std::sync::Arc; use std::sync::Mutex; use chrono::Local; -use image::RgbImage; +use image; use lazy_static::lazy_static; use log::info; @@ -17,13 +17,42 @@ pub const MAIN_IMAGE: &str = "@final"; // Red indicates recursion hit maximum depth splitting the pixel. // Green indicates no subdivision necessary pub const ADAPTIVE_DEPTH: &str = "adaptive_depth"; +// Grey scale showing rays cast per pixel. +pub const RAYS_PER_PIXEL: &str = "rays_per_pixel"; lazy_static! { static ref DEBUGGER: Arc> = Arc::new(Mutex::new(Debugger::new())); } +pub enum ImageType { + RGB01, + Grey01, + GreyNormalized, +} + +struct Image { + w: usize, + h: usize, + pix: Vec, +} + +impl Image { + fn new(w: usize, h: usize) -> Image { + Image { + w, + h, + pix: (0..w * h).map(|_| [0., 0., 0.].into()).collect(), + } + } + + fn put_pixel(&mut self, x: usize, y: usize, p: Vec3) { + let offset = x + y * self.w; + self.pix[offset] = p; + } +} + struct Debugger { - images: HashMap, + images: HashMap, } impl Debugger { @@ -34,94 +63,106 @@ impl Debugger { } } -pub fn register_image(name: String, dimensions: (u32, u32)) { +pub fn register_image(name: String, dimensions: (usize, usize), it: ImageType) { let mut debugger = DEBUGGER.lock().unwrap(); debugger .images - .insert(name, RgbImage::new(dimensions.0, dimensions.1)); + .insert(name, (it, Image::new(dimensions.0, dimensions.1))); } pub fn set_pixel(name: &str, x: usize, y: usize, pixel: Vec3) { let mut debugger = DEBUGGER.lock().unwrap(); - let img = debugger + let (_it, img) = debugger .images .get_mut(name) .expect(&format!("couldn't find image named '{}'", name)); - let y_inv = img.height() - y as u32 - 1; - img.put_pixel( - x as u32, - y_inv as u32, - image::Rgb([ - (pixel[0] * 255.).min(255.) as u8, - (pixel[1] * 255.).min(255.) as u8, - (pixel[2] * 255.).min(255.) as u8, - ]), - ); + let y_inv = img.h - y - 1; + img.put_pixel(x, y_inv, pixel); +} + +pub fn set_pixel_grey(name: &str, x: usize, y: usize, grey: f32) { + let mut debugger = DEBUGGER.lock().unwrap(); + let (_it, img) = debugger + .images + .get_mut(name) + .expect(&format!("couldn't find image named '{}'", name)); + let y_inv = img.h - y - 1; + img.put_pixel(x, y_inv, [grey, grey, grey].into()); +} + +trait ImageSaver { + fn save(&self, path: Q) -> std::io::Result<()> + where + Q: AsRef + Sized; } pub fn write_images>(output_dir: P) -> std::io::Result<()> { let output_dir: &Path = output_dir.as_ref(); let debugger = DEBUGGER.lock().unwrap(); let now = Local::now(); - /* - let style = r#" - - "#; - writeln!(f, "{}", style)?; - writeln!( - f, - "

Run @ {} ({})

", - now.to_rfc2822(), - now.timestamp() - )?; - */ // Write out images in consistent order. let mut names = debugger.images.keys().collect::>(); names.sort(); let mut ratio = 1.; let mut size = (0, 0); for name in &names { - let img = debugger.images.get(*name).unwrap(); - if *name == MAIN_IMAGE { - let (w, h) = img.dimensions(); - ratio = w as f32 / h as f32; - size = img.dimensions(); - } + let (it, img) = debugger.images.get(*name).unwrap(); let filename = format!("{}.png", name); - /* - writeln!( - f, - r#"
{1} ({2})
"#, - filename, name, now.timestamp())?; - */ let path = output_dir.join(filename); info!("Saving {}", path.to_string_lossy()); - img.save(path)?; + match it { + ImageType::RGB01 => { + let mut out_img = image::RgbImage::new(img.w as u32, img.h as u32); + out_img + .enumerate_pixels_mut() + .enumerate() + .for_each(|(i, (_x, _y, p))| { + let pixel = img.pix[i]; + *p = image::Rgb([ + (pixel[0] * 255.).min(255.) as u8, + (pixel[1] * 255.).min(255.) as u8, + (pixel[2] * 255.).min(255.) as u8, + ]) + }); + if *name == MAIN_IMAGE { + ratio = img.w as f32 / img.h as f32; + size = (img.w, img.h); + } + out_img.save(path)?; + } + ImageType::Grey01 => { + let mut out_img = image::GrayImage::new(img.w as u32, img.h as u32); + out_img + .enumerate_pixels_mut() + .enumerate() + .for_each(|(i, (_x, _y, p))| { + let pixel = img.pix[i]; + *p = image::Luma([(pixel[0] * 255.).min(255.) as u8]) + }); + out_img.save(path)?; + } + ImageType::GreyNormalized => { + let mut out_img = image::GrayImage::new(img.w as u32, img.h as u32); + + let max_val = img.pix.iter().map(|v| v.x).fold(0., f32::max); + dbg!(&max_val); + out_img + .enumerate_pixels_mut() + .enumerate() + .for_each(|(i, (_x, _y, p))| { + let pixel = img.pix[i]; + *p = image::Luma([(pixel[0] / max_val * 255.).min(255.) as u8]) + }); + out_img.save(path)?; + } + }; } let f = File::create(output_dir.join("data.json"))?; #[derive(Serialize)] struct Data { timestamp: i64, ratio: f32, - size: (u32, u32), + size: (usize, usize), names: Vec, images: Vec, } diff --git a/rtiow/src/renderer.rs b/rtiow/src/renderer.rs index 24cb7c2..45ccb93 100644 --- a/rtiow/src/renderer.rs +++ b/rtiow/src/renderer.rs @@ -441,6 +441,7 @@ fn render_pixel(scene: &Scene, x: usize, y: usize) -> (Vec3, usize) { ([0., 0., 0.].into(), 0), |(p1, r1): (Vec3, usize), (p2, r2): (Vec3, usize)| ((p1 + p2), (r1 + r2)), ); + output::set_pixel_grey(output::RAYS_PER_PIXEL, x, y, rays as f32); (pixel / scene.subsamples as f32, rays) }; // Gamma correct, use gamma 2 correction, which is 1/gamma where gamma=2 which is 1/2 or @@ -575,14 +576,21 @@ pub fn render(scene: Scene, output_dir: &Path) -> std::result::Result<(), std::i info!("Rendering with {} subsamples", scene.subsamples); output::register_image( output::MAIN_IMAGE.to_string(), - (scene.width as u32, scene.height as u32), + (scene.width, scene.height), + output::ImageType::RGB01, ); if scene.adaptive_subsampling.is_some() { output::register_image( output::ADAPTIVE_DEPTH.to_string(), - (scene.width as u32, scene.height as u32), + (scene.width, scene.height), + output::ImageType::RGB01, ); } + output::register_image( + output::RAYS_PER_PIXEL.to_string(), + (scene.width, scene.height), + output::ImageType::GreyNormalized, + ); let pixel_total = scene.width * scene.height; let mut last_time = time::Instant::now(); diff --git a/rtiow/src/scenes/tutorial.rs b/rtiow/src/scenes/tutorial.rs index e2b15f2..7e1ab89 100644 --- a/rtiow/src/scenes/tutorial.rs +++ b/rtiow/src/scenes/tutorial.rs @@ -72,8 +72,8 @@ pub fn new(opt: &Opt) -> Scene { camera, world, subsamples: opt.subsamples, - // adaptive_subsampling: None, - adaptive_subsampling: Some(0.5), + adaptive_subsampling: None, + // adaptive_subsampling: Some(0.5), num_threads: opt.num_threads, width: opt.width, height: opt.height,