Fix color matching function
This commit is contained in:
parent
e3009563ba
commit
543bf7e1bf
6 changed files with 241 additions and 73 deletions
|
|
@ -5,7 +5,7 @@ pub trait Light<R: Rng>: Send + Sync + Debug {
|
|||
fn emit(&self, w_in: Dir3, rng: &mut R) -> Color;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct AreaLight {
|
||||
pub(crate) color: Color,
|
||||
}
|
||||
|
|
|
|||
102
ray-tracing-material-visualizer/src/bin/iridescent.rs
Normal file
102
ray-tracing-material-visualizer/src/bin/iridescent.rs
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
use core::f32;
|
||||
|
||||
use plotters::{prelude::*, style::full_palette::GREEN};
|
||||
use rand::{rngs::SmallRng, Rng, SeedableRng};
|
||||
use ray_tracing_material::iridescent::Iridescent;
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let root = BitMapBackend::new("iridescent_sweep.png", (800, 600)).into_drawing_area();
|
||||
|
||||
root.fill(&WHITE)?;
|
||||
let mut chart = ChartBuilder::on(&root)
|
||||
.margin(5)
|
||||
.x_label_area_size(30)
|
||||
.y_label_area_size(30)
|
||||
.build_cartesian_2d(0f32..f32::consts::FRAC_PI_2, 0f32..1f32)?;
|
||||
|
||||
chart.configure_mesh().draw()?;
|
||||
|
||||
let (pixel_range_x, pixel_range_y) = chart.plotting_area().get_pixel_range();
|
||||
let buckets = pixel_range_x.end - pixel_range_x.start;
|
||||
let height_per_pixel = 1.0 / (pixel_range_y.end - pixel_range_y.start) as f32;
|
||||
let samples = 10000;
|
||||
|
||||
let m = Iridescent::new(0.5 * 244.0, 0.5 * 244.0, 1.0, 1.5, 20);
|
||||
|
||||
let mut data = Vec::new();
|
||||
|
||||
let mut rng = SmallRng::seed_from_u64(0);
|
||||
|
||||
let plotting_area = chart.plotting_area();
|
||||
for i in 0..buckets {
|
||||
let mut s = ray_tracing_core::color::Color::black();
|
||||
let mut s_wavelength = ray_tracing_core::color::Color::black();
|
||||
|
||||
for _ in 0..samples {
|
||||
let theta1 = rng.gen_range(
|
||||
(f32::consts::FRAC_PI_2 * (i as f32 / buckets as f32))
|
||||
..=(f32::consts::FRAC_PI_2 * ((i + 1) as f32 / buckets as f32)),
|
||||
);
|
||||
s += m.monte_carlo_reflectance(theta1, &mut rng);
|
||||
s_wavelength += ray_tracing_core::color::Color::new(
|
||||
m.averaged_reflectance(595.0, theta1),
|
||||
m.averaged_reflectance(556.0, theta1),
|
||||
m.averaged_reflectance(442.0, theta1),
|
||||
);
|
||||
}
|
||||
|
||||
let c = s / samples as f32;
|
||||
|
||||
// data.push(c);
|
||||
data.push(s_wavelength / samples as f32);
|
||||
// dbg!(c);
|
||||
for j in 0..10 {
|
||||
plotting_area
|
||||
.draw_pixel(
|
||||
(
|
||||
f32::consts::FRAC_PI_2 * (i as f32 / buckets as f32),
|
||||
1.0 - (height_per_pixel * (j as f32)),
|
||||
),
|
||||
&RGBColor(
|
||||
(c.r() * 256.0) as u8,
|
||||
(c.g() * 256.0) as u8,
|
||||
(c.b() * 256.0) as u8,
|
||||
),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
chart.draw_series(LineSeries::new(
|
||||
data.iter().enumerate().map(|(i, c)| {
|
||||
(
|
||||
(f32::consts::FRAC_PI_2 * (i as f32 / buckets as f32)),
|
||||
c.r(),
|
||||
)
|
||||
}),
|
||||
&RED,
|
||||
))?;
|
||||
|
||||
chart.draw_series(LineSeries::new(
|
||||
data.iter().enumerate().map(|(i, c)| {
|
||||
(
|
||||
(f32::consts::FRAC_PI_2 * (i as f32 / buckets as f32)),
|
||||
c.g(),
|
||||
)
|
||||
}),
|
||||
&GREEN,
|
||||
))?;
|
||||
|
||||
chart.draw_series(LineSeries::new(
|
||||
data.iter().enumerate().map(|(i, c)| {
|
||||
(
|
||||
(f32::consts::FRAC_PI_2 * (i as f32 / buckets as f32)),
|
||||
c.b(),
|
||||
)
|
||||
}),
|
||||
&BLUE,
|
||||
))?;
|
||||
|
||||
root.present()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@ use core::f32;
|
|||
|
||||
use ray_tracing_core::prelude::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Iridescent {
|
||||
d1: f32,
|
||||
d2: f32,
|
||||
|
|
@ -49,12 +49,12 @@ impl Iridescent {
|
|||
let imag = (num_imag * denom_real - num_real * denom_imag)
|
||||
/ (denom_real * denom_real + denom_imag * denom_imag);
|
||||
|
||||
if !real.is_normal() || !imag.is_normal() {
|
||||
dbg!(
|
||||
l, theta1, theta2, s_polaized, r12, phi, num_real, num_imag, denom_real,
|
||||
denom_imag, real, imag
|
||||
);
|
||||
}
|
||||
// if !real.is_normal() || !imag.is_normal() {
|
||||
// dbg!(
|
||||
// l, theta1, theta2, s_polaized, r12, phi, num_real, num_imag, denom_real,
|
||||
// denom_imag, real, imag
|
||||
// );
|
||||
// }
|
||||
|
||||
real * real + imag * imag
|
||||
}
|
||||
|
|
@ -87,34 +87,34 @@ impl Iridescent {
|
|||
|
||||
let local_c_squred = self.abs_C_squared(l, theta1, theta2, s_polaized);
|
||||
|
||||
if !local_c_squred.is_normal() || !local_cos_k_delta.is_normal() {
|
||||
dbg!((
|
||||
l,
|
||||
theta1,
|
||||
theta2,
|
||||
local_cos_k_delta,
|
||||
local_c_squred,
|
||||
s_polaized
|
||||
));
|
||||
}
|
||||
// if !local_c_squred.is_normal() || !local_cos_k_delta.is_normal() {
|
||||
// dbg!((
|
||||
// l,
|
||||
// theta1,
|
||||
// theta2,
|
||||
// local_cos_k_delta,
|
||||
// local_c_squred,
|
||||
// s_polaized
|
||||
// ));
|
||||
// }
|
||||
|
||||
if local_cos_k_delta.abs() < 1.0 {
|
||||
let k_delta = f32::acos(local_cos_k_delta);
|
||||
|
||||
let u = f32::sin(k_delta) / f32::sin(self.n * k_delta);
|
||||
|
||||
if !k_delta.is_normal() || !u.is_normal() {
|
||||
dbg!((
|
||||
l,
|
||||
theta1,
|
||||
theta2,
|
||||
local_cos_k_delta,
|
||||
local_c_squred,
|
||||
s_polaized,
|
||||
k_delta,
|
||||
u,
|
||||
));
|
||||
}
|
||||
// if !k_delta.is_normal() || !u.is_normal() {
|
||||
// dbg!((
|
||||
// l,
|
||||
// theta1,
|
||||
// theta2,
|
||||
// local_cos_k_delta,
|
||||
// local_c_squred,
|
||||
// s_polaized,
|
||||
// k_delta,
|
||||
// u,
|
||||
// ));
|
||||
// }
|
||||
|
||||
local_c_squred / (local_c_squred + u * u)
|
||||
} else if local_cos_k_delta.abs() > 1.0 {
|
||||
|
|
@ -124,40 +124,40 @@ impl Iridescent {
|
|||
|
||||
let u = f32::sinh(imk_delta) / f32::sinh(self.n * imk_delta);
|
||||
|
||||
if !imk_delta.is_normal() || !u.is_normal() {
|
||||
dbg!((
|
||||
l,
|
||||
theta1,
|
||||
theta2,
|
||||
local_cos_k_delta,
|
||||
local_c_squred,
|
||||
s_polaized,
|
||||
imk_delta,
|
||||
u
|
||||
));
|
||||
}
|
||||
// if !imk_delta.is_normal() || !u.is_normal() {
|
||||
// dbg!((
|
||||
// l,
|
||||
// theta1,
|
||||
// theta2,
|
||||
// local_cos_k_delta,
|
||||
// local_c_squred,
|
||||
// s_polaized,
|
||||
// imk_delta,
|
||||
// u
|
||||
// ));
|
||||
// }
|
||||
|
||||
local_c_squred / (local_c_squred + u * u)
|
||||
} else {
|
||||
let u = 1.0 / self.n;
|
||||
|
||||
if !u.is_normal() {
|
||||
dbg!((
|
||||
l,
|
||||
theta1,
|
||||
theta2,
|
||||
local_cos_k_delta,
|
||||
local_c_squred,
|
||||
s_polaized,
|
||||
u
|
||||
));
|
||||
}
|
||||
// if !u.is_normal() {
|
||||
// dbg!((
|
||||
// l,
|
||||
// theta1,
|
||||
// theta2,
|
||||
// local_cos_k_delta,
|
||||
// local_c_squred,
|
||||
// s_polaized,
|
||||
// u
|
||||
// ));
|
||||
// }
|
||||
|
||||
local_c_squred / (local_c_squred + u * u)
|
||||
}
|
||||
}
|
||||
|
||||
fn averaged_reflectance(&self, l: f32, theta1: f32) -> f32 {
|
||||
pub fn averaged_reflectance(&self, l: f32, theta1: f32) -> f32 {
|
||||
0.5 * (self.reflectance(l, theta1, true) + self.reflectance(l, theta1, false))
|
||||
}
|
||||
|
||||
|
|
@ -183,25 +183,25 @@ impl Iridescent {
|
|||
)
|
||||
}
|
||||
|
||||
fn monte_carlo_reflectance<R: Rng>(&self, theta1: f32, rng: &mut R) -> Color {
|
||||
pub fn monte_carlo_reflectance<R: Rng>(&self, theta1: f32, rng: &mut R) -> Color {
|
||||
let range = 400.0..800.0;
|
||||
let size = range.end - range.start;
|
||||
// let size = range.end - range.start;
|
||||
|
||||
let l = rng.gen_range(range);
|
||||
|
||||
let r = self.averaged_reflectance(l, theta1);
|
||||
|
||||
if !r.is_normal() {
|
||||
dbg!((theta1, l, r));
|
||||
}
|
||||
// if !r.is_normal() {
|
||||
// dbg!((theta1, l, r));
|
||||
// }
|
||||
|
||||
let color = Self::color_matching_function(l);
|
||||
|
||||
if !color.r().is_normal() || !color.g().is_normal() || !color.b().is_normal() {
|
||||
dbg!(color);
|
||||
}
|
||||
// if !color.r().is_normal() || !color.g().is_normal() || !color.b().is_normal() {
|
||||
// dbg!(color);
|
||||
// }
|
||||
|
||||
color * r
|
||||
color * r * (1.0 / 0.27)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,26 +1,32 @@
|
|||
use ray_tracing_core::light::AreaLight;
|
||||
use ray_tracing_core::prelude::*;
|
||||
use ray_tracing_core::scene::{Intersection, Scene};
|
||||
use ray_tracing_material::default::DefaultMaterial;
|
||||
|
||||
pub struct BasicScene {
|
||||
#[derive(Clone)]
|
||||
pub struct BasicScene<M> {
|
||||
pub(crate) spheres: Vec<(Pos3, Float)>,
|
||||
area_light: AreaLight,
|
||||
material: M,
|
||||
}
|
||||
|
||||
impl BasicScene {
|
||||
pub fn new() -> Self {
|
||||
impl<M> BasicScene<M> {
|
||||
pub fn new(material: M) -> Self {
|
||||
Self {
|
||||
spheres: vec![(Pos3::zero(), 1.0), (Pos3::new(0.0, 0.0, 2.5), 2.0)],
|
||||
spheres: vec![(Pos3::zero(), 1.0), (Pos3::new(0.0, -4.5, 0.0), 4.0)],
|
||||
area_light: AreaLight::new(Color::white()),
|
||||
material,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for BasicScene {
|
||||
impl Default for BasicScene<DefaultMaterial> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
Self::new(DefaultMaterial {})
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Rng> Scene<R> for BasicScene {
|
||||
impl<R: Rng, M: Material<R>> Scene<R> for BasicScene<M> {
|
||||
fn intersect(&self, ray: Ray, min: Float, max: Float) -> Option<Intersection<'_, R>> {
|
||||
let mut intersection: Option<Intersection<'_, R>> = None;
|
||||
|
||||
|
|
@ -35,7 +41,7 @@ impl<R: Rng> Scene<R> for BasicScene {
|
|||
let int = Intersection::new(
|
||||
d,
|
||||
((ray.start() + d * ray.dir()) - c).normalize(),
|
||||
Some(&DefaultMaterial {}),
|
||||
Some(&self.material),
|
||||
None,
|
||||
0.0,
|
||||
);
|
||||
|
|
@ -51,7 +57,13 @@ impl<R: Rng> Scene<R> for BasicScene {
|
|||
}
|
||||
}
|
||||
|
||||
intersection
|
||||
Some(intersection.unwrap_or(Intersection::new(
|
||||
Float::INFINITY,
|
||||
-ray.dir(),
|
||||
None,
|
||||
Some(&self.area_light),
|
||||
0.0,
|
||||
)))
|
||||
}
|
||||
|
||||
fn sample_light(
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ pub fn example_scenes<R: Rng + Debug + 'static>() -> HashMap<&'static str, Box<d
|
|||
"stanford_dragon_microfacet",
|
||||
Box::new(stanford_dragon::StanfordDragon::new(material)),
|
||||
);
|
||||
let material = Iridescent::new(100.0, 900.0, 1.0, 1.5, 20);
|
||||
let material = Iridescent::new(250.0, 250.0, 1.0, 1.5, 20);
|
||||
map.insert(
|
||||
"stanford_dragon_iridescent",
|
||||
Box::new(stanford_dragon::StanfordDragon::new(material)),
|
||||
|
|
@ -43,11 +43,15 @@ pub fn example_scenes<R: Rng + Debug + 'static>() -> HashMap<&'static str, Box<d
|
|||
Box::new(stanford_dragon_as::StanfordDragon::new()),
|
||||
);
|
||||
map.insert("mis_test", Box::new(mis_test::MISTest::new()));
|
||||
|
||||
let material = Iridescent::new(250.0, 250.0, 1.0, 1.3, 20);
|
||||
map.insert("sphere", Box::new(sphere::SphereScene::new(material)));
|
||||
map
|
||||
}
|
||||
|
||||
pub mod basic_cornell;
|
||||
pub mod cornell2;
|
||||
pub mod mis_test;
|
||||
pub mod sphere;
|
||||
pub mod stanford_dragon;
|
||||
pub mod stanford_dragon_as;
|
||||
|
|
|
|||
50
ray-tracing-scene/src/examples/sphere.rs
Normal file
50
ray-tracing-scene/src/examples/sphere.rs
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
use std::{
|
||||
cell::{Cell, OnceCell},
|
||||
marker::PhantomData,
|
||||
};
|
||||
|
||||
use ray_tracing_core::prelude::*;
|
||||
|
||||
use crate::basic_scene::BasicScene;
|
||||
|
||||
use super::ExampleScene;
|
||||
|
||||
pub struct SphereScene<M> {
|
||||
scene: OnceCell<BasicScene<M>>,
|
||||
material: Cell<Option<M>>,
|
||||
}
|
||||
|
||||
impl<M> SphereScene<M> {
|
||||
pub fn new(material: M) -> Self {
|
||||
Self {
|
||||
scene: OnceCell::new(),
|
||||
material: Cell::new(Some(material)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Rng + 'static, M: Material<R> + Clone + 'static> ExampleScene<R> for SphereScene<M> {
|
||||
fn get_scene(&self) -> Box<dyn ray_tracing_core::scene::Scene<R> + Sync> {
|
||||
let s = self
|
||||
.scene
|
||||
.get_or_init(|| BasicScene::new(self.material.take().unwrap()));
|
||||
|
||||
Box::new(s.clone())
|
||||
}
|
||||
|
||||
fn get_camera_pos(&self) -> Pos3 {
|
||||
Pos3::new(5.0, 1.0, 0.0)
|
||||
}
|
||||
|
||||
fn get_camera_look_at(&self) -> Pos3 {
|
||||
Pos3::new(0.0, 0.0, 0.0)
|
||||
}
|
||||
|
||||
fn get_camera_up(&self) -> Dir3 {
|
||||
Dir3::up()
|
||||
}
|
||||
|
||||
fn get_horizontal_fov(&self) -> Float {
|
||||
Float::to_radians(90.0)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue