diff --git a/ray-tracing-material-visualizer/src/bin/plot.rs b/ray-tracing-material-visualizer/src/bin/plot.rs index 1f2b5ee..ba20d6e 100644 --- a/ray-tracing-material-visualizer/src/bin/plot.rs +++ b/ray-tracing-material-visualizer/src/bin/plot.rs @@ -3,10 +3,10 @@ use ray_tracing_material::{diffuse::DiffuseMaterial, mirror::Mirror}; use ray_tracing_material_validator::generate_chart; fn main() -> Result<(), Box> { - let m = Mirror::new(Color::new(1.0, 1.0, 1.0)); + let m = Mirror::new(Color::new(1.0, 1.0, 0.5)); generate_chart("mirror.png", &m, 2)?; - let m = DiffuseMaterial::new(Color::new(1.0, 1.0, 1.0)); + let m = DiffuseMaterial::new(Color::new(1.0, 1.0, 0.5)); generate_chart("diffuse.png", &m, 100)?; Ok(()) diff --git a/ray-tracing-material-visualizer/src/lib.rs b/ray-tracing-material-visualizer/src/lib.rs index d6d8d94..095ab6d 100644 --- a/ray-tracing-material-visualizer/src/lib.rs +++ b/ray-tracing-material-visualizer/src/lib.rs @@ -1,8 +1,6 @@ -use std::ops::RangeBounds; - -use plotters::{coord::Shift, prelude::*, style::RelativeSize}; -use rand::{rngs::SmallRng, Rng, SeedableRng}; -use ray_tracing_core::prelude::*; +use plotters::{coord::Shift, prelude::*}; +use rand::{rngs::SmallRng, SeedableRng}; +use ray_tracing_core::{color::Color, prelude::*}; use rayon::prelude::*; pub fn generate_chart>( @@ -10,9 +8,15 @@ pub fn generate_chart>( m: &M, samples_per_pixel: usize, ) -> Result<(), Box> { - let root = BitMapBackend::new(&path, (800, 800)).into_drawing_area(); + let inner_size = 400; + let width = (inner_size + 70) * 3 + 34; + let height = (inner_size + 97) * 2; + let root = BitMapBackend::new(&path, (width, height)).into_drawing_area(); - plot_material_sample(&root, m, samples_per_pixel)?; + root.fill(&WHITE)?; + + let area = root.titled("Sampled", ("sans-serif", 30))?; + plot_material_sample(&area, m, samples_per_pixel)?; root.present()?; @@ -27,47 +31,12 @@ fn plot_material_sample>( where DB::ErrorType: 'static, { - root.fill(&WHITE)?; + let (xrange, yrange) = root.get_pixel_range(); + let width = (xrange.end - xrange.start) as usize; + let height = (yrange.end - yrange.start) as usize; - let (upper, lower) = root.split_horizontally(RelativeSize::Width(0.5)); - - let mut upper_chart = ChartBuilder::on(&upper) - .margin(5) - .x_label_area_size(30) - .top_x_label_area_size(30) - .y_label_area_size(30) - .right_y_label_area_size(30) - .caption("Upper Hemisphere", ("sans-serif", 20)) - .build_cartesian_2d(-1.0..1.0, -1.0..1.0)?; - - upper_chart - .configure_mesh() - .disable_x_mesh() - .disable_y_mesh() - .draw()?; - - let upper_area = upper_chart.plotting_area(); - - let mut lower_chart = ChartBuilder::on(&lower) - .margin(5) - .x_label_area_size(30) - .top_x_label_area_size(30) - .y_label_area_size(30) - .right_y_label_area_size(30) - .caption("Lower Hemisphere", ("sans-serif", 20)) - .build_cartesian_2d(-1.0..1.0, -1.0..1.0)?; - - lower_chart - .configure_mesh() - .disable_x_mesh() - .disable_y_mesh() - .draw()?; - - let lower_area = lower_chart.plotting_area(); - - let (xrange, yrange) = upper_area.get_pixel_range(); - let width = (xrange.end - xrange.start) as usize - 1; - let height = (yrange.end - yrange.start) as usize - 1; + let width = width / 3 - 70; + let height = height / 2 - 97; let histogram = create_historgram( m, @@ -75,19 +44,75 @@ where height, samples_per_pixel * width * height, Dir3::new(1.0, 1.0, 0.0).normalize(), - 0, ); - let map = ViridisRGB; + let areas = root.split_evenly((2, 3)); + for (channel, upper, lower) in (0..3).map(|i| (i, &areas[i], &areas[i + 3])) { + let channel_name = match channel { + 0 => "red", + 1 => "green", + 2 => "blue", + _ => unreachable!(), + }; - let max = histogram.max(); + let mut upper_chart = ChartBuilder::on(upper) + .margin(5) + .x_label_area_size(30) + .top_x_label_area_size(30) + .y_label_area_size(30) + .right_y_label_area_size(30) + .caption( + format!("Upper Hemisphere {channel_name}"), + ("sans-serif", 20), + ) + .build_cartesian_2d(-1.0..1.0, -1.0..1.0)?; - for (dir, f) in histogram.iter() { - let color = map.get_color_normalized(f, 0.0, max); - if dir.y() < 0.0 { - lower_area.draw_pixel((dir.x() as f64, dir.z() as f64), &color)?; - } else { - upper_area.draw_pixel((dir.x() as f64, dir.z() as f64), &color)?; + upper_chart + .configure_mesh() + .disable_x_mesh() + .disable_y_mesh() + .draw()?; + + let upper_area = upper_chart.plotting_area(); + + let mut lower_chart = ChartBuilder::on(lower) + .margin(5) + .x_label_area_size(30) + .top_x_label_area_size(30) + .y_label_area_size(30) + .right_y_label_area_size(30) + .caption( + format!("Lower Hemisphere {channel_name}"), + ("sans-serif", 20), + ) + .build_cartesian_2d(-1.0..1.0, -1.0..1.0)?; + + lower_chart + .configure_mesh() + .disable_x_mesh() + .disable_y_mesh() + .draw()?; + + let lower_area = lower_chart.plotting_area(); + + let map = ViridisRGB; + + let max = histogram.max(); + + for (dir, f) in histogram.iter() { + let f = match channel { + 0 => f.r(), + 1 => f.g(), + 2 => f.b(), + _ => unreachable!(), + }; + + let color = map.get_color_normalized(f, 0.0, max); + if dir.y() < 0.0 { + lower_area.draw_pixel((dir.x() as f64, dir.z() as f64), &color)?; + } else { + upper_area.draw_pixel((dir.x() as f64, dir.z() as f64), &color)?; + } } } @@ -95,7 +120,7 @@ where } struct Histogram { - data: Vec, + data: Vec, inserted: usize, width: usize, height: usize, @@ -105,7 +130,7 @@ struct Histogram { impl Histogram { fn new(width: usize, height: usize) -> Self { Self { - data: vec![0.0; 2 * width * height], + data: vec![Color::black(); 2 * width * height], inserted: 0, width, height, @@ -123,10 +148,12 @@ impl Histogram { } } - fn insert(&mut self, pos: Dir3, value: f32) { + fn insert(&mut self, pos: Dir3, value: Color) { let index = self.discretize(pos); self.data[index] += value; - self.max = f32::max(self.max, self.data[index]); + self.max = f32::max(self.max, self.data[index].r()); + self.max = f32::max(self.max, self.data[index].g()); + self.max = f32::max(self.max, self.data[index].b()); self.inserted += 1; } @@ -134,7 +161,7 @@ impl Histogram { self.max / (self.inserted as f32) } - fn iter(&self) -> impl Iterator + use<'_> { + fn iter(&self) -> impl Iterator + use<'_> { let m = 1f32 / self.inserted as f32; let width = self.width; let height = self.height; @@ -166,10 +193,10 @@ impl Histogram { } } -impl ParallelExtend<(Dir3, f32)> for Histogram { +impl ParallelExtend<(Dir3, Color)> for Histogram { fn par_extend(&mut self, par_iter: I) where - I: IntoParallelIterator, + I: IntoParallelIterator, { let (tx, rx) = std::sync::mpsc::sync_channel(100); @@ -196,7 +223,6 @@ fn create_historgram>( height: usize, executions: usize, w_in: Dir3, - channel: usize, ) -> Histogram { let mut h = Histogram::new(width, height); h.par_extend( @@ -205,14 +231,7 @@ fn create_historgram>( .map_init(SmallRng::from_entropy, |rng, _| { let sample = m.sample(w_in, rng); - let f = match channel { - 0 => sample.color().r(), - 1 => sample.color().g(), - 2 => sample.color().b(), - _ => unreachable!(), - }; - - (sample.w_out(), f) + (sample.w_out(), sample.color()) }), );