From 745b7d2602fcb9a196b7350ff7893018a0a737e8 Mon Sep 17 00:00:00 2001 From: hal8174 Date: Fri, 27 Dec 2024 19:11:02 +0100 Subject: [PATCH] Add Oren-Nayar and fix bugs --- ray-tracing-egui/src/main.rs | 2 +- ray-tracing-material-visualizer/src/main.rs | 39 +++++++++++- ray-tracing-material/src/diffuse.rs | 8 ++- ray-tracing-material/src/lib.rs | 1 + ray-tracing-material/src/microfacet.rs | 61 +++++++++++++------ ray-tracing-material/src/oren_nayar.rs | 39 ++++++++++++ .../src/examples/stanford_dragon.rs | 15 ++++- 7 files changed, 137 insertions(+), 28 deletions(-) create mode 100644 ray-tracing-material/src/oren_nayar.rs diff --git a/ray-tracing-egui/src/main.rs b/ray-tracing-egui/src/main.rs index 8532c66..af5f5e0 100644 --- a/ray-tracing-egui/src/main.rs +++ b/ray-tracing-egui/src/main.rs @@ -1,4 +1,4 @@ -use egui::{ahash::HashMap, Widget}; +use egui::Widget; use egui_winit_vulkano::{Gui, GuiConfig}; use ray_tracing_core::prelude::*; use ray_tracing_scene::examples::example_scenes; diff --git a/ray-tracing-material-visualizer/src/main.rs b/ray-tracing-material-visualizer/src/main.rs index 37a46a5..ee5c6b7 100644 --- a/ray-tracing-material-visualizer/src/main.rs +++ b/ray-tracing-material-visualizer/src/main.rs @@ -6,7 +6,12 @@ use plotters::{ }; use rand::{rngs::SmallRng, SeedableRng}; use ray_tracing_core::prelude::*; -use ray_tracing_material::{diffuse::DiffuseMaterial, mirror::Mirror}; +use ray_tracing_material::{ + diffuse::DiffuseMaterial, + microfacet::{BeckmannDistribution, Microfacet}, + mirror::Mirror, + oren_nayar::OrenNayar, +}; use rayon::iter::{IntoParallelIterator, ParallelExtend, ParallelIterator}; fn main() -> Result<(), Box> { @@ -20,6 +25,12 @@ fn main() -> Result<(), Box> { let m = DiffuseMaterial::new(color); generate_chart("diffuse.png", &m, 100, w_in)?; + let m = Microfacet::new(BeckmannDistribution::new(0.5), color); + generate_chart("microfacet.png", &m, 100, w_in)?; + + let m = OrenNayar::new(0.5, color); + generate_chart("oren-nayar.png", &m, 100, w_in)?; + Ok(()) } @@ -42,7 +53,6 @@ fn generate_chart>( let sample_histogram = create_historgram_sampled(m, 400, 400, executions, w_in); let max = Float::max(eval_histogram.max(), sample_histogram.max()); - dbg!(max, eval_histogram.max(), sample_histogram.max()); let area = areas[0].titled("Evaled", ("sans-serif", 30))?; plot_material(&area, eval_histogram, max)?; @@ -132,6 +142,7 @@ where _ => unreachable!(), }; + assert!(f >= 0.0 && f <= max && f.is_finite(), "{f}"); let color = plotters::prelude::ViridisRGB::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)?; @@ -263,6 +274,18 @@ fn create_historgram_sampled>( .map_init(SmallRng::from_entropy, |rng, _| { let sample = m.sample(w_in, rng); + assert!( + sample.color().r() >= 0.0 + && sample.color().r().is_finite() + && sample.color().g() >= 0.0 + && sample.color().g().is_finite() + && sample.color().b() >= 0.0 + && sample.color().b().is_finite(), + "w_in: {w_in:?}; w_out: {:?}; material: {m:?}; color: {:?}", + sample.w_out(), + sample.color() + ); + (sample.w_out(), sample.color()) }), ); @@ -285,7 +308,17 @@ fn create_histogram_evaled>( .map_init(SmallRng::from_entropy, |rng, _| { let w_out = Dir3::sample_uniform_sphere(rng); - let color = m.eval(w_in, w_out, rng) * w_out.y() * 4.0 * FloatConsts::PI; + let color = m.eval(w_in, w_out, rng) * w_out.y().abs() * 4.0 * FloatConsts::PI; + + assert!( + color.r() >= 0.0 + && color.r().is_finite() + && color.g() >= 0.0 + && color.g().is_finite() + && color.b() >= 0.0 + && color.b().is_finite(), + "w_in: {w_in:?}; w_out: {w_out:?}; material: {m:?}; color: {color:?}" + ); (w_out, color) }), diff --git a/ray-tracing-material/src/diffuse.rs b/ray-tracing-material/src/diffuse.rs index 32df758..5cd575f 100644 --- a/ray-tracing-material/src/diffuse.rs +++ b/ray-tracing-material/src/diffuse.rs @@ -14,7 +14,11 @@ impl DiffuseMaterial { } impl Material for DiffuseMaterial { - fn eval(&self, _w_in: Dir3, _w_out: Dir3, _rng: &mut R) -> Color { - self.albedo + fn eval(&self, _w_in: Dir3, w_out: Dir3, _rng: &mut R) -> Color { + if w_out.y() >= 0.0 { + self.albedo + } else { + Color::black() + } } } diff --git a/ray-tracing-material/src/lib.rs b/ray-tracing-material/src/lib.rs index d2e46c4..4a11b33 100644 --- a/ray-tracing-material/src/lib.rs +++ b/ray-tracing-material/src/lib.rs @@ -2,3 +2,4 @@ pub mod default; pub mod diffuse; pub mod microfacet; pub mod mirror; +pub mod oren_nayar; diff --git a/ray-tracing-material/src/microfacet.rs b/ray-tracing-material/src/microfacet.rs index c6ded78..e656bf1 100644 --- a/ray-tracing-material/src/microfacet.rs +++ b/ray-tracing-material/src/microfacet.rs @@ -2,34 +2,57 @@ use ray_tracing_core::{material::Material, prelude::*}; use std::fmt::Debug; #[derive(Debug)] -struct Microfacet { +pub struct Microfacet { dist: D, color: Color, } -fn fresnel(w_in: Dir3, n: Dir3, nu_rel: Float) -> Float { - todo!() -} - -impl Material for Microfacet { - fn eval( - &self, - w_in: ray_tracing_core::prelude::Dir3, - w_out: ray_tracing_core::prelude::Dir3, - _rng: &mut R, - ) -> ray_tracing_core::prelude::Color { - let w_h = (w_in + w_out).normalize(); - - let g = self.dist.g1(w_in, w_h) * self.dist.g1(w_out, w_h); - - self.color * fresnel(w_in, w_h, 1.0) * g * self.dist.d(w_h) / (4.0 * w_in.y() * w_out.y()) +impl Microfacet { + pub fn new(dist: D, color: Color) -> Self { + Self { dist, color } } } -struct BeckmannDistribution { +fn fresnel_real(cos_theta_in: Float, nu1: Float, nu2: Float) -> Float { + let nu_rel = nu1 / nu2; + let cos_theta_tr = Float::sqrt(1.0 - nu_rel * nu_rel * (1.0 - cos_theta_in * cos_theta_in)); + + let rs = ((nu1 * cos_theta_in - nu2 * cos_theta_tr) + / (nu1 * cos_theta_in + nu2 * cos_theta_tr)) + .powi(2); + let rp = ((nu1 * cos_theta_tr - nu2 * cos_theta_in) + / (nu1 * cos_theta_tr + nu2 * cos_theta_in)) + .powi(2); + + 0.5 * (rs + rp) +} + +impl Material for Microfacet { + fn eval(&self, w_in: Dir3, w_out: Dir3, _rng: &mut R) -> ray_tracing_core::prelude::Color { + if w_out.y() > 0.0 { + let w_h = (w_in + w_out).normalize(); + + let g = self.dist.g1(w_in, w_h) * self.dist.g1(w_out, w_h); + + self.color * fresnel_real(Dir3::dot(w_in, w_h), 1.0, 1.3) * g * self.dist.d(w_h) + / (4.0 * w_in.y() * w_out.y()).max(0.0) + } else { + Color::black() + } + } +} + +#[derive(Debug)] +pub struct BeckmannDistribution { alpha: Float, } +impl BeckmannDistribution { + pub fn new(alpha: Float) -> Self { + Self { alpha } + } +} + impl MicrofacetDistribution for BeckmannDistribution { fn g1(&self, w: Dir3, w_h: Dir3) -> Float { if w.y() > 0.0 && Dir3::dot(w, w_h) > 0.0 { @@ -55,7 +78,7 @@ impl MicrofacetDistribution for BeckmannDistribution { } } -trait MicrofacetDistribution { +pub trait MicrofacetDistribution { fn g1(&self, w: Dir3, w_h: Dir3) -> Float; fn d(&self, w_h: Dir3) -> Float; diff --git a/ray-tracing-material/src/oren_nayar.rs b/ray-tracing-material/src/oren_nayar.rs new file mode 100644 index 0000000..5935fb9 --- /dev/null +++ b/ray-tracing-material/src/oren_nayar.rs @@ -0,0 +1,39 @@ +use ray_tracing_core::prelude::*; + +#[derive(Debug)] +pub struct OrenNayar { + rho: Float, + color: Color, +} + +impl OrenNayar { + pub fn new(rho: Float, color: Color) -> Self { + Self { rho, color } + } +} + +impl Material for OrenNayar { + fn eval(&self, w_in: Dir3, w_out: Dir3, _rng: &mut R) -> Color { + if w_out.y() > 0.0 { + let cos_alpha = Float::min(w_in.y(), w_out.y()); + let cos_beta = Float::max(w_in.y(), w_out.y()); + + let a = 1.0 - (self.rho * self.rho) / (2.0 * self.rho * self.rho + 0.33); + let b = 0.45 * self.rho * self.rho / (self.rho * self.rho + 0.09); + + let sin_alpha = Float::sqrt(1.0 - cos_alpha * cos_alpha); + let tan_beta = Float::sqrt(1.0 - cos_beta * cos_beta) / cos_beta; + + let w_in_tick = w_in - Dir3::up() * w_in.y(); + let w_out_tick = w_out - Dir3::up() * w_out.y(); + + let cos_phi = Dir3::dot(w_in_tick, w_out_tick); + + self.color + * FloatConsts::FRAC_1_PI + * (a + b * Float::max(0.0, cos_phi) * sin_alpha * tan_beta) + } else { + Color::black() + } + } +} diff --git a/ray-tracing-scene/src/examples/stanford_dragon.rs b/ray-tracing-scene/src/examples/stanford_dragon.rs index fe7f41d..76f8c1b 100644 --- a/ray-tracing-scene/src/examples/stanford_dragon.rs +++ b/ray-tracing-scene/src/examples/stanford_dragon.rs @@ -1,6 +1,10 @@ use rand::Rng; use ray_tracing_core::{light::AreaLight, prelude::*, scene::Scene}; -use ray_tracing_material::{diffuse::DiffuseMaterial, mirror::Mirror}; +use ray_tracing_material::{ + diffuse::DiffuseMaterial, + microfacet::{BeckmannDistribution, Microfacet}, + oren_nayar::OrenNayar, +}; use std::fmt::Debug; use crate::{ @@ -12,6 +16,11 @@ use super::ExampleScene; pub fn scene() -> ExampleScene { let f = || { + let color = Color::new(0.2, 0.2, 0.9); + // let m = DiffuseMaterial::new(color); + // let m = Microfacet::new(BeckmannDistribution::new(0.5), color); + let m = OrenNayar::new(0.5, color); + let obj = ObjData::new("ray-tracing-scene/obj/stanford_dragon.obj").unwrap(); let mut triangles = obj @@ -20,7 +29,7 @@ pub fn scene() -> ExampleScene { .collect::>(); let materials = vec![ - BVHMaterial::new_material(DiffuseMaterial::new(Color::new(0.2, 0.2, 0.9))), + BVHMaterial::new_material(m), BVHMaterial::new_light(AreaLight::new(Color::white() * 30.0)), ]; @@ -50,7 +59,7 @@ pub fn scene() -> ExampleScene { ExampleScene { scene: f, - camera_pos: Pos3::new(0.0, 0.0, -400.0), + camera_pos: Pos3::new(-150.0, 100.0, 250.0), camera_look_at: Pos3::new(0.0, 0.0, 0.0), camera_up: Dir3::up(), horizontal_fov: 90.0_f32.to_radians(),