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;
}
#[derive(Debug)]
#[derive(Clone, Debug)]
pub struct AreaLight {
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::*;
#[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)
}
}

View file

@ -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(

View file

@ -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;

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