Implement importance sampling for materials

This commit is contained in:
Matthew Gordon 2020-09-12 11:53:01 -04:00
parent 7c82605d98
commit 45cf4fa487
5 changed files with 71 additions and 29 deletions

View File

@ -1,6 +1,7 @@
use crate::math::Vec3; use crate::math::Vec3;
use super::colour::{ColourRgbF, Photon, Spectrum}; use super::colour::{ColourRgbF, Photon, Spectrum};
use super::materials::MaterialSampleResult;
use super::raycasting::{IntersectionInfo, Ray}; use super::raycasting::{IntersectionInfo, Ray};
use super::sampler::Sampler; use super::sampler::Sampler;
use super::util::algebra_utils::try_change_of_basis_matrix; use super::util::algebra_utils::try_change_of_basis_matrix;
@ -59,7 +60,7 @@ impl Integrator for WhittedIntegrator {
.material .material
.sample(&(world_to_bsdf_space * info.retro), &photon)] .sample(&(world_to_bsdf_space * info.retro), &photon)]
.iter() .iter()
.map(|direction| { .map(|MaterialSampleResult { direction, pdf: _ }| {
let world_space_direction = bsdf_to_world_space * direction; let world_space_direction = bsdf_to_world_space * direction;
match sampler match sampler
.sample(&Ray::new(info.location, world_space_direction).bias(0.000_000_1)) .sample(&Ray::new(info.location, world_space_direction).bias(0.000_000_1))
@ -118,11 +119,14 @@ impl Integrator for SimpleRandomIntegrator {
.expect("Expected matrix to be invertable."); .expect("Expected matrix to be invertable.");
let world_space_w_i = info.retro; let world_space_w_i = info.retro;
let w_i = world_to_bsdf_space * world_space_w_i; let w_i = world_to_bsdf_space * world_space_w_i;
let w_o = info.material.sample(&w_i, &photon); let MaterialSampleResult {
direction: w_o,
pdf: w_o_pdf,
} = info.material.sample(&w_i, &photon);
let world_space_w_o = bsdf_to_world_space * w_o; let world_space_w_o = bsdf_to_world_space * w_o;
info.material.bsdf()( info.material.bsdf()(
&w_o.normalize(), &w_o,
&w_i.normalize(), &w_i,
&match sampler.sample(&Ray::new(info.location, world_space_w_o).bias(0.000_000_1)) { &match sampler.sample(&Ray::new(info.location, world_space_w_o).bias(0.000_000_1)) {
None => photon.set_intensity(test_lighting_environment( None => photon.set_intensity(test_lighting_environment(
&world_space_w_o, &world_space_w_o,
@ -132,6 +136,7 @@ impl Integrator for SimpleRandomIntegrator {
self.integrate(&sampler, &recursive_hit, &photon, recursion_limit - 1) self.integrate(&sampler, &recursive_hit, &photon, recursion_limit - 1)
} }
} }
.scale_intensity(w_o_pdf)
.scale_intensity(world_space_w_o.dot(&info.normal).abs()), .scale_intensity(world_space_w_o.dot(&info.normal).abs()),
) )
} }
@ -142,7 +147,7 @@ pub fn test_lighting_environment(w_o: &Vec3, wavelength: f64) -> f64 {
if w_o.dot(&sun_direction) >= 0.99 { if w_o.dot(&sun_direction) >= 0.99 {
300.0 300.0
} else { } else {
let sky_colour = ColourRgbF::new(w_o.y(), w_o.y(), 1.0) * 0.1; let sky_colour = ColourRgbF::new(w_o.y(), w_o.y(), 1.0);
Spectrum::reflection_from_linear_rgb(&sky_colour).intensity_at_wavelength(wavelength) Spectrum::reflection_from_linear_rgb(&sky_colour).intensity_at_wavelength(wavelength)
} }
} }

View File

@ -1,8 +1,12 @@
use crate::colour::{Photon, Spectrum}; use crate::colour::{Photon, Spectrum};
use crate::math::Vec3; use crate::math::Vec3;
use super::Material; use super::{Material, MaterialSampleResult};
use rand::distributions::Open01;
use rand::{thread_rng, Rng};
use std::f64::consts::PI;
use std::fmt::Debug; use std::fmt::Debug;
#[derive(Debug)] #[derive(Debug)]
@ -28,4 +32,29 @@ impl Material for LambertianMaterial {
result result
}) })
} }
fn sample(&self, _w_i: &Vec3, _photon: &Photon) -> MaterialSampleResult {
let mut rng = thread_rng();
let mut w_o = Vec3::new(
2.0 * rng.sample::<f64, _>(Open01) - 1.0,
2.0 * rng.sample::<f64, _>(Open01) - 1.0,
0.0,
);
while w_o.norm_squared() > 1.0 {
w_o = Vec3::new(
2.0 * rng.sample::<f64, _>(Open01) - 1.0,
2.0 * rng.sample::<f64, _>(Open01) - 1.0,
0.0,
);
}
w_o.coords[2] = (1.0 - w_o.x() * w_o.x() - w_o.y() * w_o.y())
.sqrt()
.max(0.0);
let cos_theta = w_o.dot(&Vec3::unit_z());
let sin_theta = (1.0 - cos_theta * cos_theta).sqrt();
MaterialSampleResult {
direction: w_o.normalize(),
pdf: (cos_theta * sin_theta) / PI,
}
}
} }

View File

@ -4,6 +4,7 @@ use super::colour::Photon;
use rand::distributions::{Open01, OpenClosed01}; use rand::distributions::{Open01, OpenClosed01};
use rand::{thread_rng, Rng}; use rand::{thread_rng, Rng};
use std::f64::consts::PI;
use std::fmt::Debug; use std::fmt::Debug;
pub mod lambertian_material; pub mod lambertian_material;
@ -18,10 +19,15 @@ pub use reflective_material::ReflectiveMaterial;
pub mod smooth_transparent_dialectric; pub mod smooth_transparent_dialectric;
pub use smooth_transparent_dialectric::SmoothTransparentDialectric; pub use smooth_transparent_dialectric::SmoothTransparentDialectric;
pub struct MaterialSampleResult {
pub direction: Vec3,
pub pdf: f64,
}
pub trait Material: Debug + Sync + Send { pub trait Material: Debug + Sync + Send {
fn bsdf<'a>(&'a self) -> Box<dyn Fn(&Vec3, &Vec3, &Photon) -> Photon + 'a>; fn bsdf<'a>(&'a self) -> Box<dyn Fn(&Vec3, &Vec3, &Photon) -> Photon + 'a>;
fn sample(&self, _w_i: &Vec3, _photon: &Photon) -> Vec3 { fn sample(&self, _w_i: &Vec3, _photon: &Photon) -> MaterialSampleResult {
let mut rng = thread_rng(); let mut rng = thread_rng();
let mut w_o = Vec3::new( let mut w_o = Vec3::new(
2.0 * rng.sample::<f64, _>(Open01) - 1.0, 2.0 * rng.sample::<f64, _>(Open01) - 1.0,
@ -35,10 +41,9 @@ pub trait Material: Debug + Sync + Send {
rng.sample::<f64, _>(OpenClosed01), rng.sample::<f64, _>(OpenClosed01),
); );
} }
w_o MaterialSampleResult {
} direction: w_o.normalize(),
pdf: 1.0 / (2.0 * PI),
fn pdf(&self, _w_i: &Vec3, _w_o: &Vec3) -> f64 { }
1.0
} }
} }

View File

@ -3,7 +3,7 @@ use crate::math::Vec3;
use std::fmt::Debug; use std::fmt::Debug;
use super::Material; use super::{Material, MaterialSampleResult};
#[derive(Debug)] #[derive(Debug)]
pub struct ReflectiveMaterial { pub struct ReflectiveMaterial {
@ -39,7 +39,10 @@ impl Material for ReflectiveMaterial {
}) })
} }
fn sample(&self, w_o: &Vec3, _photon: &Photon) -> Vec3 { fn sample(&self, w_o: &Vec3, _photon: &Photon) -> MaterialSampleResult {
Vec3::new(-w_o.x(), -w_o.y(), w_o.z()) MaterialSampleResult {
direction: Vec3::new(-w_o.x(), -w_o.y(), w_o.z()),
pdf: 1.0,
}
} }
} }

View File

@ -1,5 +1,5 @@
use crate::colour::{Photon, Spectrum}; use crate::colour::{Photon, Spectrum};
use crate::materials::Material; use crate::materials::{Material, MaterialSampleResult};
use crate::math::Vec3; use crate::math::Vec3;
use rand::random; use rand::random;
@ -83,17 +83,12 @@ impl Material for SmoothTransparentDialectric {
} else if (*w_o - fresnel.transmission_direction).norm_squared() < 0.0000000001 { } else if (*w_o - fresnel.transmission_direction).norm_squared() < 0.0000000001 {
photon_in.scale_intensity(fresnel.transmission_strength) photon_in.scale_intensity(fresnel.transmission_strength)
} else { } else {
/*dbg!(
w_o,
fresnel.reflection_direction,
fresnel.transmission_direction
);*/
photon_in.set_intensity(0.0) photon_in.set_intensity(0.0)
} }
}) })
} }
fn sample(&self, w_i: &Vec3, photon: &Photon) -> Vec3 { fn sample(&self, w_i: &Vec3, photon: &Photon) -> MaterialSampleResult {
let (eta1, eta2) = if w_i.z() >= 0.0 { let (eta1, eta2) = if w_i.z() >= 0.0 {
(1.0, self.eta.intensity_at_wavelength(photon.wavelength)) (1.0, self.eta.intensity_at_wavelength(photon.wavelength))
} else { } else {
@ -101,15 +96,20 @@ impl Material for SmoothTransparentDialectric {
}; };
let fresnel = fresnel(w_i, eta1, eta2); let fresnel = fresnel(w_i, eta1, eta2);
if fresnel.transmission_strength <= 0.0000000001 { if fresnel.transmission_strength <= 0.0000000001 {
fresnel.reflection_direction MaterialSampleResult {
direction: fresnel.reflection_direction,
pdf: 0.5,
}
} else if fresnel.reflection_strength <= 0.0000000001 || random() { } else if fresnel.reflection_strength <= 0.0000000001 || random() {
fresnel.transmission_direction MaterialSampleResult {
direction: fresnel.transmission_direction,
pdf: 0.5,
}
} else { } else {
fresnel.reflection_direction MaterialSampleResult {
direction: fresnel.reflection_direction,
pdf: 0.5,
} }
} }
fn pdf(&self, _w_i: &Vec3, _w_o: &Vec3) -> f64 {
0.0
} }
} }