rtiow: add greyscale images for debugging.
This commit is contained in:
parent
ea31b570db
commit
96e74b3ebf
@ -5,7 +5,7 @@ use std::sync::Arc;
|
|||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
|
||||||
use chrono::Local;
|
use chrono::Local;
|
||||||
use image::RgbImage;
|
use image;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use log::info;
|
use log::info;
|
||||||
|
|
||||||
@ -17,13 +17,42 @@ pub const MAIN_IMAGE: &str = "@final";
|
|||||||
// Red indicates recursion hit maximum depth splitting the pixel.
|
// Red indicates recursion hit maximum depth splitting the pixel.
|
||||||
// Green indicates no subdivision necessary
|
// Green indicates no subdivision necessary
|
||||||
pub const ADAPTIVE_DEPTH: &str = "adaptive_depth";
|
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! {
|
lazy_static! {
|
||||||
static ref DEBUGGER: Arc<Mutex<Debugger>> = Arc::new(Mutex::new(Debugger::new()));
|
static ref DEBUGGER: Arc<Mutex<Debugger>> = Arc::new(Mutex::new(Debugger::new()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum ImageType {
|
||||||
|
RGB01,
|
||||||
|
Grey01,
|
||||||
|
GreyNormalized,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Image {
|
||||||
|
w: usize,
|
||||||
|
h: usize,
|
||||||
|
pix: Vec<Vec3>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
struct Debugger {
|
||||||
images: HashMap<String, RgbImage>,
|
images: HashMap<String, (ImageType, Image)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debugger {
|
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();
|
let mut debugger = DEBUGGER.lock().unwrap();
|
||||||
debugger
|
debugger
|
||||||
.images
|
.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) {
|
pub fn set_pixel(name: &str, x: usize, y: usize, pixel: Vec3) {
|
||||||
let mut debugger = DEBUGGER.lock().unwrap();
|
let mut debugger = DEBUGGER.lock().unwrap();
|
||||||
let img = debugger
|
let (_it, img) = debugger
|
||||||
.images
|
.images
|
||||||
.get_mut(name)
|
.get_mut(name)
|
||||||
.expect(&format!("couldn't find image named '{}'", name));
|
.expect(&format!("couldn't find image named '{}'", name));
|
||||||
let y_inv = img.height() - y as u32 - 1;
|
let y_inv = img.h - y - 1;
|
||||||
img.put_pixel(
|
img.put_pixel(x, y_inv, pixel);
|
||||||
x as u32,
|
}
|
||||||
y_inv as u32,
|
|
||||||
image::Rgb([
|
pub fn set_pixel_grey(name: &str, x: usize, y: usize, grey: f32) {
|
||||||
(pixel[0] * 255.).min(255.) as u8,
|
let mut debugger = DEBUGGER.lock().unwrap();
|
||||||
(pixel[1] * 255.).min(255.) as u8,
|
let (_it, img) = debugger
|
||||||
(pixel[2] * 255.).min(255.) as u8,
|
.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<Q>(&self, path: Q) -> std::io::Result<()>
|
||||||
|
where
|
||||||
|
Q: AsRef<Path> + Sized;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_images<P: AsRef<Path>>(output_dir: P) -> std::io::Result<()> {
|
pub fn write_images<P: AsRef<Path>>(output_dir: P) -> std::io::Result<()> {
|
||||||
let output_dir: &Path = output_dir.as_ref();
|
let output_dir: &Path = output_dir.as_ref();
|
||||||
let debugger = DEBUGGER.lock().unwrap();
|
let debugger = DEBUGGER.lock().unwrap();
|
||||||
let now = Local::now();
|
let now = Local::now();
|
||||||
/*
|
|
||||||
let style = r#"
|
|
||||||
<style>
|
|
||||||
.frame {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
figure {
|
|
||||||
margin: 0.25em;
|
|
||||||
}
|
|
||||||
figcaption {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
img {
|
|
||||||
max-width: 97%;
|
|
||||||
height: auto;
|
|
||||||
margin: 0;
|
|
||||||
border: 1px solid #999;
|
|
||||||
padding: 5px;
|
|
||||||
background: #fff;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
"#;
|
|
||||||
writeln!(f, "{}", style)?;
|
|
||||||
writeln!(
|
|
||||||
f,
|
|
||||||
"<h1>Run @ {} ({})</h1>",
|
|
||||||
now.to_rfc2822(),
|
|
||||||
now.timestamp()
|
|
||||||
)?;
|
|
||||||
*/
|
|
||||||
// Write out images in consistent order.
|
// Write out images in consistent order.
|
||||||
let mut names = debugger.images.keys().collect::<Vec<_>>();
|
let mut names = debugger.images.keys().collect::<Vec<_>>();
|
||||||
names.sort();
|
names.sort();
|
||||||
let mut ratio = 1.;
|
let mut ratio = 1.;
|
||||||
let mut size = (0, 0);
|
let mut size = (0, 0);
|
||||||
for name in &names {
|
for name in &names {
|
||||||
let img = debugger.images.get(*name).unwrap();
|
let (it, 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 filename = format!("{}.png", name);
|
let filename = format!("{}.png", name);
|
||||||
/*
|
|
||||||
writeln!(
|
|
||||||
f,
|
|
||||||
r#"<div class="frame"><figure><a href="{0}?t={2}"><img src="{0}?t={2}"></a><figcaption>{1} ({2})</figcaption></figure></div>"#,
|
|
||||||
filename, name, now.timestamp())?;
|
|
||||||
*/
|
|
||||||
let path = output_dir.join(filename);
|
let path = output_dir.join(filename);
|
||||||
info!("Saving {}", path.to_string_lossy());
|
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"))?;
|
let f = File::create(output_dir.join("data.json"))?;
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
struct Data {
|
struct Data {
|
||||||
timestamp: i64,
|
timestamp: i64,
|
||||||
ratio: f32,
|
ratio: f32,
|
||||||
size: (u32, u32),
|
size: (usize, usize),
|
||||||
names: Vec<String>,
|
names: Vec<String>,
|
||||||
images: Vec<String>,
|
images: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -441,6 +441,7 @@ fn render_pixel(scene: &Scene, x: usize, y: usize) -> (Vec3, usize) {
|
|||||||
([0., 0., 0.].into(), 0),
|
([0., 0., 0.].into(), 0),
|
||||||
|(p1, r1): (Vec3, usize), (p2, r2): (Vec3, usize)| ((p1 + p2), (r1 + r2)),
|
|(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)
|
(pixel / scene.subsamples as f32, rays)
|
||||||
};
|
};
|
||||||
// Gamma correct, use gamma 2 correction, which is 1/gamma where gamma=2 which is 1/2 or
|
// 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);
|
info!("Rendering with {} subsamples", scene.subsamples);
|
||||||
output::register_image(
|
output::register_image(
|
||||||
output::MAIN_IMAGE.to_string(),
|
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() {
|
if scene.adaptive_subsampling.is_some() {
|
||||||
output::register_image(
|
output::register_image(
|
||||||
output::ADAPTIVE_DEPTH.to_string(),
|
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 pixel_total = scene.width * scene.height;
|
||||||
let mut last_time = time::Instant::now();
|
let mut last_time = time::Instant::now();
|
||||||
|
|||||||
@ -72,8 +72,8 @@ pub fn new(opt: &Opt) -> Scene {
|
|||||||
camera,
|
camera,
|
||||||
world,
|
world,
|
||||||
subsamples: opt.subsamples,
|
subsamples: opt.subsamples,
|
||||||
// adaptive_subsampling: None,
|
adaptive_subsampling: None,
|
||||||
adaptive_subsampling: Some(0.5),
|
// adaptive_subsampling: Some(0.5),
|
||||||
num_threads: opt.num_threads,
|
num_threads: opt.num_threads,
|
||||||
width: opt.width,
|
width: opt.width,
|
||||||
height: opt.height,
|
height: opt.height,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user