From d43d60bb857a1f1f96557587ad8851bcd7fd2aa8 Mon Sep 17 00:00:00 2001 From: hal8174 Date: Sun, 5 Jan 2025 23:52:41 +0100 Subject: [PATCH] Add initial MIS implementation --- ray-tracing-core/src/material.rs | 5 + ray-tracing-core/src/scene.rs | 7 ++ ray-tracing-material/src/microfacet.rs | 6 + ray-tracing-material/src/mirror.rs | 6 + ray-tracing-renderer/src/lib.rs | 1 + ray-tracing-renderer/src/mis.rs | 109 ++++++++++++++++++ .../src/acceleration_structure/mod.rs | 7 ++ ray-tracing-scene/src/basic_scene.rs | 1 + ray-tracing-scene/src/triangle_bvh.rs | 7 +- ray-tracing-tev/src/main.rs | 15 ++- 10 files changed, 161 insertions(+), 3 deletions(-) create mode 100644 ray-tracing-renderer/src/mis.rs diff --git a/ray-tracing-core/src/material.rs b/ray-tracing-core/src/material.rs index ae20c8c..51f354d 100644 --- a/ray-tracing-core/src/material.rs +++ b/ray-tracing-core/src/material.rs @@ -10,6 +10,11 @@ pub trait Material: Send + Sync + Debug { SampleResult::new(w_out, self.eval(w_in, w_out, rng) * FloatConsts::PI) } + + fn pdf(&self, w_in: Dir3, w_out: Dir3) -> Float { + let _ = w_out; + FloatConsts::FRAC_1_PI * Float::max(w_in.y(), 0.0) + } } #[derive(Debug)] diff --git a/ray-tracing-core/src/scene.rs b/ray-tracing-core/src/scene.rs index 61fa0d1..661d949 100644 --- a/ray-tracing-core/src/scene.rs +++ b/ray-tracing-core/src/scene.rs @@ -18,6 +18,7 @@ pub struct Intersection<'sc, R: Rng> { normal: Dir3, material: Option<&'sc dyn Material>, light: Option<&'sc dyn Light>, + light_pdf: Float, } impl<'sc, R: Rng> Intersection<'sc, R> { @@ -26,12 +27,14 @@ impl<'sc, R: Rng> Intersection<'sc, R> { normal: Dir3, material: Option<&'sc dyn Material>, light: Option<&'sc dyn Light>, + light_pdf: Float, ) -> Self { Self { t, normal, material, light, + light_pdf, } } @@ -51,6 +54,10 @@ impl<'sc, R: Rng> Intersection<'sc, R> { self.light } + pub fn light_pdf(&self) -> Float { + self.light_pdf + } + pub fn tangent_frame(&self) -> Frame { Frame::from_normal(self.normal) } diff --git a/ray-tracing-material/src/microfacet.rs b/ray-tracing-material/src/microfacet.rs index 8e2e20f..147faed 100644 --- a/ray-tracing-material/src/microfacet.rs +++ b/ray-tracing-material/src/microfacet.rs @@ -57,6 +57,12 @@ impl Material for Mi ray_tracing_core::material::SampleResult::new(w_out, Color::black()) } } + + fn pdf(&self, w_in: Dir3, w_out: Dir3) -> Float { + let w_h = (w_in + w_out).normalize(); + + self.dist.d(w_h) + } } #[derive(Debug)] diff --git a/ray-tracing-material/src/mirror.rs b/ray-tracing-material/src/mirror.rs index 546921b..3852c43 100644 --- a/ray-tracing-material/src/mirror.rs +++ b/ray-tracing-material/src/mirror.rs @@ -21,4 +21,10 @@ impl Material for Mirror { ray_tracing_core::material::SampleResult::new(w_out, self.color) } + + fn pdf(&self, w_in: Dir3, w_out: Dir3) -> Float { + let _ = w_out; + let _ = w_in; + 0.0 + } } diff --git a/ray-tracing-renderer/src/lib.rs b/ray-tracing-renderer/src/lib.rs index 83db56a..1db16c8 100644 --- a/ray-tracing-renderer/src/lib.rs +++ b/ray-tracing-renderer/src/lib.rs @@ -1,4 +1,5 @@ pub mod depth_renderer; +pub mod mis; pub mod next_event_estimation; pub mod path_tracer; pub mod path_tracer_importance; diff --git a/ray-tracing-renderer/src/mis.rs b/ray-tracing-renderer/src/mis.rs new file mode 100644 index 0000000..673c325 --- /dev/null +++ b/ray-tracing-renderer/src/mis.rs @@ -0,0 +1,109 @@ +use ray_tracing_core::{camera::Camera, prelude::*, renderer::ClassicalRenderer, scene::Scene}; + +pub struct MIS { + width: u32, + height: u32, +} + +impl MIS { + pub fn new(width: u32, height: u32) -> Self { + Self { width, height } + } +} + +impl ClassicalRenderer for MIS +where + S: Scene, + C: Camera, + R: Rng, +{ + fn render_pixel(&self, scene: &S, camera: &C, x: u32, y: u32, rng: &mut R) -> Color { + let mut sum = Color::black(); + let mut alpha = Color::white(); + + let mut r = camera.forward(x, y, rng); + + let mut count = 0; + let mut last_bsdf_pdf = 0.0; + + while let Some(i) = scene.intersect(r, 0.001, Float::INFINITY) { + let frame = i.tangent_frame(); + + let intersect_pos = r.at(i.t()); + + let w_in = frame.to_frame(-r.dir()); + + if let Some(light) = i.light() { + if count == 0 { + sum += alpha * light.emit(w_in, rng); + } + } + + let w_out = if let Some(material) = i.material() { + let sample_result = material.sample(w_in, rng); + if let Some(light) = i.light() { + let dist = i.t() / r.dir().length(); + + let path_pdf = last_bsdf_pdf; + let nee_pdf = i.light_pdf() * dist * dist / w_in.y(); + + let b = path_pdf / (path_pdf + nee_pdf); + sum += b * alpha * light.emit(w_in, rng); + } + + if let Some(l) = scene.sample_light(w_in, &i, rng) { + let light_frame = l.tangent_frame(); + + let light_dir = l.pos() - intersect_pos; + + let light_ray = Ray::new(intersect_pos, light_dir.normalize(), r.time()); + + if scene + .intersect(light_ray, 0.001, light_dir.length() - 0.001) + .is_none() + { + let g = (Dir3::dot(i.normal(), light_dir.normalize()) + * Dir3::dot(l.normal(), -light_dir.normalize())) + / light_dir.length_squared(); + + // calculate MIS wheight + let path_pdf = material.pdf(w_in, frame.to_frame(light_dir)); + let nee_pdf = l.pdf() * light_dir.length_squared() / Float::abs(w_in.y()); + + let b = nee_pdf / (path_pdf + nee_pdf); + + sum += b + * alpha + * g + * material.eval(w_in, frame.to_frame(light_dir.normalize()), rng) + * l.light() + .emit(light_frame.to_frame(-light_dir.normalize()), rng) + / l.pdf(); + } + } + + last_bsdf_pdf = material.pdf(w_in, sample_result.w_out()); + alpha *= sample_result.color(); + sample_result.w_out() + } else { + return sum; + }; + + r = Ray::new(r.at(i.t()), frame.to_world(w_out), r.time()); + count += 1; + if count > 4 { + break; + } + } + + sum + } + + fn width(&self) -> u32 { + self.width + } + + fn height(&self) -> u32 { + self.height + } +} diff --git a/ray-tracing-scene/src/acceleration_structure/mod.rs b/ray-tracing-scene/src/acceleration_structure/mod.rs index a953375..4bf44b2 100644 --- a/ray-tracing-scene/src/acceleration_structure/mod.rs +++ b/ray-tracing-scene/src/acceleration_structure/mod.rs @@ -83,11 +83,18 @@ impl, R: Rng> Scene for AccelerationStructureSc let material = &self.materials[i as usize]; + let light_pdf = if let Some(l) = &material.light { + 0.0 + } else { + 0.0 + }; + Some(Intersection::new( t, n, material.material.as_deref(), material.light.as_deref(), + light_pdf, )) } diff --git a/ray-tracing-scene/src/basic_scene.rs b/ray-tracing-scene/src/basic_scene.rs index 12926d4..f2ee4f6 100644 --- a/ray-tracing-scene/src/basic_scene.rs +++ b/ray-tracing-scene/src/basic_scene.rs @@ -37,6 +37,7 @@ impl Scene for BasicScene { ((ray.start() + d * ray.dir()) - c).normalize(), Some(&DefaultMaterial {}), None, + 0.0, ); if d >= min && d <= max { if let Some(i) = intersection.as_ref() { diff --git a/ray-tracing-scene/src/triangle_bvh.rs b/ray-tracing-scene/src/triangle_bvh.rs index 3a77caa..0e6e922 100644 --- a/ray-tracing-scene/src/triangle_bvh.rs +++ b/ray-tracing-scene/src/triangle_bvh.rs @@ -329,13 +329,16 @@ impl Scene for TriangleBVH { let triangle = self.triangles[i as usize]; let material = &self.materials[triangle.material as usize]; - let n = triangle_normal(self.get_vertices_from_index(i)).normalize(); + let n = triangle_normal(self.get_vertices_from_index(i)); + + let area = n.length() * 0.5; Some(Intersection::new( t, - n, + n.normalize(), material.material.as_deref(), material.light.as_deref(), + 1.0 / ((self.lights.len() as Float) * area), )) } diff --git a/ray-tracing-tev/src/main.rs b/ray-tracing-tev/src/main.rs index 7ea3205..63fe4dc 100644 --- a/ray-tracing-tev/src/main.rs +++ b/ray-tracing-tev/src/main.rs @@ -7,7 +7,7 @@ use ray_tracing_core::{ scene::Scene, }; use ray_tracing_renderer::{ - depth_renderer::DepthRenderer, next_event_estimation::NextEventEstimation, + depth_renderer::DepthRenderer, mis::MIS, next_event_estimation::NextEventEstimation, path_tracer::PathTracer, path_tracer_importance::PathTracerImportance, }; use ray_tracing_scene::examples::example_scenes; @@ -42,6 +42,7 @@ fn render_image< ) -> Result<(), TevError> { let mut data = vec![0.0; (renderer.width() * renderer.height() * 3) as usize]; + let start = std::time::Instant::now(); data.par_chunks_mut(3).enumerate().for_each(|(i, c)| { let x = (i % renderer.width() as usize) as u32; let y = (i / renderer.width() as usize) as u32; @@ -55,6 +56,7 @@ fn render_image< c[2] += r.b(); } }); + println!("Rendered \"{}\" in {:?}", name.as_ref(), start.elapsed()); let channel_names = &["r", "g", "b"]; @@ -152,5 +154,16 @@ fn main() { &mut client, ) .unwrap(); + + let r = MIS::new(args.width, args.height); + render_image( + format!("{scene} - mis"), + &r, + &s, + &c, + args.samples_per_pixel, + &mut client, + ) + .unwrap(); } }