Improved material visualizer
This commit is contained in:
parent
7e88ebbfc1
commit
ab68f307f1
2 changed files with 93 additions and 74 deletions
|
|
@ -3,10 +3,10 @@ use ray_tracing_material::{diffuse::DiffuseMaterial, mirror::Mirror};
|
||||||
use ray_tracing_material_validator::generate_chart;
|
use ray_tracing_material_validator::generate_chart;
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
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)?;
|
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)?;
|
generate_chart("diffuse.png", &m, 100)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
use std::ops::RangeBounds;
|
use plotters::{coord::Shift, prelude::*};
|
||||||
|
use rand::{rngs::SmallRng, SeedableRng};
|
||||||
use plotters::{coord::Shift, prelude::*, style::RelativeSize};
|
use ray_tracing_core::{color::Color, prelude::*};
|
||||||
use rand::{rngs::SmallRng, Rng, SeedableRng};
|
|
||||||
use ray_tracing_core::prelude::*;
|
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
|
|
||||||
pub fn generate_chart<M: Material<SmallRng>>(
|
pub fn generate_chart<M: Material<SmallRng>>(
|
||||||
|
|
@ -10,9 +8,15 @@ pub fn generate_chart<M: Material<SmallRng>>(
|
||||||
m: &M,
|
m: &M,
|
||||||
samples_per_pixel: usize,
|
samples_per_pixel: usize,
|
||||||
) -> Result<(), Box<dyn std::error::Error>> {
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
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()?;
|
root.present()?;
|
||||||
|
|
||||||
|
|
@ -27,47 +31,12 @@ fn plot_material_sample<DB: DrawingBackend, M: Material<SmallRng>>(
|
||||||
where
|
where
|
||||||
DB::ErrorType: 'static,
|
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 width = width / 3 - 70;
|
||||||
|
let height = height / 2 - 97;
|
||||||
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 histogram = create_historgram(
|
let histogram = create_historgram(
|
||||||
m,
|
m,
|
||||||
|
|
@ -75,19 +44,75 @@ where
|
||||||
height,
|
height,
|
||||||
samples_per_pixel * width * height,
|
samples_per_pixel * width * height,
|
||||||
Dir3::new(1.0, 1.0, 0.0).normalize(),
|
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() {
|
upper_chart
|
||||||
let color = map.get_color_normalized(f, 0.0, max);
|
.configure_mesh()
|
||||||
if dir.y() < 0.0 {
|
.disable_x_mesh()
|
||||||
lower_area.draw_pixel((dir.x() as f64, dir.z() as f64), &color)?;
|
.disable_y_mesh()
|
||||||
} else {
|
.draw()?;
|
||||||
upper_area.draw_pixel((dir.x() as f64, dir.z() as f64), &color)?;
|
|
||||||
|
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 {
|
struct Histogram {
|
||||||
data: Vec<f32>,
|
data: Vec<Color>,
|
||||||
inserted: usize,
|
inserted: usize,
|
||||||
width: usize,
|
width: usize,
|
||||||
height: usize,
|
height: usize,
|
||||||
|
|
@ -105,7 +130,7 @@ struct Histogram {
|
||||||
impl Histogram {
|
impl Histogram {
|
||||||
fn new(width: usize, height: usize) -> Self {
|
fn new(width: usize, height: usize) -> Self {
|
||||||
Self {
|
Self {
|
||||||
data: vec![0.0; 2 * width * height],
|
data: vec![Color::black(); 2 * width * height],
|
||||||
inserted: 0,
|
inserted: 0,
|
||||||
width,
|
width,
|
||||||
height,
|
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);
|
let index = self.discretize(pos);
|
||||||
self.data[index] += value;
|
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;
|
self.inserted += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -134,7 +161,7 @@ impl Histogram {
|
||||||
self.max / (self.inserted as f32)
|
self.max / (self.inserted as f32)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn iter(&self) -> impl Iterator<Item = (Dir3, f32)> + use<'_> {
|
fn iter(&self) -> impl Iterator<Item = (Dir3, Color)> + use<'_> {
|
||||||
let m = 1f32 / self.inserted as f32;
|
let m = 1f32 / self.inserted as f32;
|
||||||
let width = self.width;
|
let width = self.width;
|
||||||
let height = self.height;
|
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<I>(&mut self, par_iter: I)
|
fn par_extend<I>(&mut self, par_iter: I)
|
||||||
where
|
where
|
||||||
I: IntoParallelIterator<Item = (Dir3, f32)>,
|
I: IntoParallelIterator<Item = (Dir3, Color)>,
|
||||||
{
|
{
|
||||||
let (tx, rx) = std::sync::mpsc::sync_channel(100);
|
let (tx, rx) = std::sync::mpsc::sync_channel(100);
|
||||||
|
|
||||||
|
|
@ -196,7 +223,6 @@ fn create_historgram<M: Material<SmallRng>>(
|
||||||
height: usize,
|
height: usize,
|
||||||
executions: usize,
|
executions: usize,
|
||||||
w_in: Dir3,
|
w_in: Dir3,
|
||||||
channel: usize,
|
|
||||||
) -> Histogram {
|
) -> Histogram {
|
||||||
let mut h = Histogram::new(width, height);
|
let mut h = Histogram::new(width, height);
|
||||||
h.par_extend(
|
h.par_extend(
|
||||||
|
|
@ -205,14 +231,7 @@ fn create_historgram<M: Material<SmallRng>>(
|
||||||
.map_init(SmallRng::from_entropy, |rng, _| {
|
.map_init(SmallRng::from_entropy, |rng, _| {
|
||||||
let sample = m.sample(w_in, rng);
|
let sample = m.sample(w_in, rng);
|
||||||
|
|
||||||
let f = match channel {
|
(sample.w_out(), sample.color())
|
||||||
0 => sample.color().r(),
|
|
||||||
1 => sample.color().g(),
|
|
||||||
2 => sample.color().b(),
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
(sample.w_out(), f)
|
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue