Fix color matching function

This commit is contained in:
hal8174 2025-05-25 19:55:38 +02:00
parent e3009563ba
commit 543bf7e1bf
Signed by: hal8174
SSH key fingerprint: SHA256:JwuqS+eVfISfKr+DkDQ6NWAbGd1jFAHkPpCM1yCnlTs
6 changed files with 241 additions and 73 deletions

View file

@ -5,7 +5,7 @@ pub trait Light<R: Rng>: Send + Sync + Debug {
fn emit(&self, w_in: Dir3, rng: &mut R) -> Color; fn emit(&self, w_in: Dir3, rng: &mut R) -> Color;
} }
#[derive(Debug)] #[derive(Clone, Debug)]
pub struct AreaLight { pub struct AreaLight {
pub(crate) color: Color, pub(crate) color: Color,
} }

View 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(())
}

View file

@ -2,7 +2,7 @@ use core::f32;
use ray_tracing_core::prelude::*; use ray_tracing_core::prelude::*;
#[derive(Debug)] #[derive(Debug, Clone, Copy)]
pub struct Iridescent { pub struct Iridescent {
d1: f32, d1: f32,
d2: f32, d2: f32,
@ -49,12 +49,12 @@ impl Iridescent {
let imag = (num_imag * denom_real - num_real * denom_imag) let imag = (num_imag * denom_real - num_real * denom_imag)
/ (denom_real * denom_real + denom_imag * denom_imag); / (denom_real * denom_real + denom_imag * denom_imag);
if !real.is_normal() || !imag.is_normal() { // if !real.is_normal() || !imag.is_normal() {
dbg!( // dbg!(
l, theta1, theta2, s_polaized, r12, phi, num_real, num_imag, denom_real, // l, theta1, theta2, s_polaized, r12, phi, num_real, num_imag, denom_real,
denom_imag, real, imag // denom_imag, real, imag
); // );
} // }
real * real + imag * imag real * real + imag * imag
} }
@ -87,34 +87,34 @@ impl Iridescent {
let local_c_squred = self.abs_C_squared(l, theta1, theta2, s_polaized); 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() { // if !local_c_squred.is_normal() || !local_cos_k_delta.is_normal() {
dbg!(( // dbg!((
l, // l,
theta1, // theta1,
theta2, // theta2,
local_cos_k_delta, // local_cos_k_delta,
local_c_squred, // local_c_squred,
s_polaized // s_polaized
)); // ));
} // }
if local_cos_k_delta.abs() < 1.0 { if local_cos_k_delta.abs() < 1.0 {
let k_delta = f32::acos(local_cos_k_delta); let k_delta = f32::acos(local_cos_k_delta);
let u = f32::sin(k_delta) / f32::sin(self.n * k_delta); let u = f32::sin(k_delta) / f32::sin(self.n * k_delta);
if !k_delta.is_normal() || !u.is_normal() { // if !k_delta.is_normal() || !u.is_normal() {
dbg!(( // dbg!((
l, // l,
theta1, // theta1,
theta2, // theta2,
local_cos_k_delta, // local_cos_k_delta,
local_c_squred, // local_c_squred,
s_polaized, // s_polaized,
k_delta, // k_delta,
u, // u,
)); // ));
} // }
local_c_squred / (local_c_squred + u * u) local_c_squred / (local_c_squred + u * u)
} else if local_cos_k_delta.abs() > 1.0 { } 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); let u = f32::sinh(imk_delta) / f32::sinh(self.n * imk_delta);
if !imk_delta.is_normal() || !u.is_normal() { // if !imk_delta.is_normal() || !u.is_normal() {
dbg!(( // dbg!((
l, // l,
theta1, // theta1,
theta2, // theta2,
local_cos_k_delta, // local_cos_k_delta,
local_c_squred, // local_c_squred,
s_polaized, // s_polaized,
imk_delta, // imk_delta,
u // u
)); // ));
} // }
local_c_squred / (local_c_squred + u * u) local_c_squred / (local_c_squred + u * u)
} else { } else {
let u = 1.0 / self.n; let u = 1.0 / self.n;
if !u.is_normal() { // if !u.is_normal() {
dbg!(( // dbg!((
l, // l,
theta1, // theta1,
theta2, // theta2,
local_cos_k_delta, // local_cos_k_delta,
local_c_squred, // local_c_squred,
s_polaized, // s_polaized,
u // u
)); // ));
} // }
local_c_squred / (local_c_squred + u * 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)) 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 range = 400.0..800.0;
let size = range.end - range.start; // let size = range.end - range.start;
let l = rng.gen_range(range); let l = rng.gen_range(range);
let r = self.averaged_reflectance(l, theta1); let r = self.averaged_reflectance(l, theta1);
if !r.is_normal() { // if !r.is_normal() {
dbg!((theta1, l, r)); // dbg!((theta1, l, r));
} // }
let color = Self::color_matching_function(l); let color = Self::color_matching_function(l);
if !color.r().is_normal() || !color.g().is_normal() || !color.b().is_normal() { // if !color.r().is_normal() || !color.g().is_normal() || !color.b().is_normal() {
dbg!(color); // dbg!(color);
} // }
color * r color * r * (1.0 / 0.27)
} }
} }

View file

@ -1,26 +1,32 @@
use ray_tracing_core::light::AreaLight;
use ray_tracing_core::prelude::*; use ray_tracing_core::prelude::*;
use ray_tracing_core::scene::{Intersection, Scene}; use ray_tracing_core::scene::{Intersection, Scene};
use ray_tracing_material::default::DefaultMaterial; use ray_tracing_material::default::DefaultMaterial;
pub struct BasicScene { #[derive(Clone)]
pub struct BasicScene<M> {
pub(crate) spheres: Vec<(Pos3, Float)>, pub(crate) spheres: Vec<(Pos3, Float)>,
area_light: AreaLight,
material: M,
} }
impl BasicScene { impl<M> BasicScene<M> {
pub fn new() -> Self { pub fn new(material: M) -> Self {
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 { 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>> { fn intersect(&self, ray: Ray, min: Float, max: Float) -> Option<Intersection<'_, R>> {
let mut intersection: Option<Intersection<'_, R>> = None; let mut intersection: Option<Intersection<'_, R>> = None;
@ -35,7 +41,7 @@ impl<R: Rng> Scene<R> for BasicScene {
let int = Intersection::new( let int = Intersection::new(
d, d,
((ray.start() + d * ray.dir()) - c).normalize(), ((ray.start() + d * ray.dir()) - c).normalize(),
Some(&DefaultMaterial {}), Some(&self.material),
None, None,
0.0, 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( fn sample_light(

View file

@ -28,7 +28,7 @@ pub fn example_scenes<R: Rng + Debug + 'static>() -> HashMap<&'static str, Box<d
"stanford_dragon_microfacet", "stanford_dragon_microfacet",
Box::new(stanford_dragon::StanfordDragon::new(material)), 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( map.insert(
"stanford_dragon_iridescent", "stanford_dragon_iridescent",
Box::new(stanford_dragon::StanfordDragon::new(material)), 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()), Box::new(stanford_dragon_as::StanfordDragon::new()),
); );
map.insert("mis_test", Box::new(mis_test::MISTest::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 map
} }
pub mod basic_cornell; pub mod basic_cornell;
pub mod cornell2; pub mod cornell2;
pub mod mis_test; pub mod mis_test;
pub mod sphere;
pub mod stanford_dragon; pub mod stanford_dragon;
pub mod stanford_dragon_as; pub mod stanford_dragon_as;

View 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)
}
}