Add initial MIS implementation

This commit is contained in:
hal8174 2025-01-05 23:52:41 +01:00
parent ba2d853319
commit d43d60bb85
10 changed files with 161 additions and 3 deletions

View file

@ -10,6 +10,11 @@ pub trait Material<R: Rng>: 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)]

View file

@ -18,6 +18,7 @@ pub struct Intersection<'sc, R: Rng> {
normal: Dir3,
material: Option<&'sc dyn Material<R>>,
light: Option<&'sc dyn Light<R>>,
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<R>>,
light: Option<&'sc dyn Light<R>>,
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)
}

View file

@ -57,6 +57,12 @@ impl<R: Rng, D: MicrofacetDistribution + Debug + Sync + Send> Material<R> 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)]

View file

@ -21,4 +21,10 @@ impl<R: Rng> Material<R> 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
}
}

View file

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

View file

@ -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<S, C, R> ClassicalRenderer<R, S, C> for MIS
where
S: Scene<R>,
C: Camera<R>,
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
}
}

View file

@ -83,11 +83,18 @@ impl<A: AccelerationStructure<u32>, R: Rng> Scene<R> 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,
))
}

View file

@ -37,6 +37,7 @@ impl<R: Rng> Scene<R> 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() {

View file

@ -329,13 +329,16 @@ impl<R: Rng> Scene<R> for TriangleBVH<R> {
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),
))
}

View file

@ -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();
}
}