Multiple importance sampling is now basically working.
Intersecting rectangular lights still isn't implemented, and there are likely bugs in the MIS implementation, but it's more-or-less working!
This commit is contained in:
parent
462977bd4d
commit
5a03a46ac7
|
@ -192,7 +192,8 @@ impl<'a> Surface for RectangleLight<'a> {
|
||||||
space: &[Matrix4x4],
|
space: &[Matrix4x4],
|
||||||
) {
|
) {
|
||||||
let _ = (accel_rays, wrays, isects, shader, space);
|
let _ = (accel_rays, wrays, isects, shader, space);
|
||||||
unimplemented!()
|
// TODO
|
||||||
|
// unimplemented!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,11 @@ use surface::{Surface, SurfaceIntersection, SurfaceIntersectionData};
|
||||||
|
|
||||||
use super::SurfaceLight;
|
use super::SurfaceLight;
|
||||||
|
|
||||||
|
// TODO: use proper error bounds for sample generation to avoid self-shadowing
|
||||||
|
// instead of these fudge factors.
|
||||||
|
const SAMPLE_RADIUS_EXPAND_FACTOR: f32 = 1.001;
|
||||||
|
const SAMPLE_RADIUS_SHRINK_FACTOR: f32 = 0.99;
|
||||||
|
|
||||||
// TODO: handle case where radius = 0.0.
|
// TODO: handle case where radius = 0.0.
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
@ -140,14 +145,23 @@ impl<'a> SurfaceLight for SphereLight<'a> {
|
||||||
);
|
);
|
||||||
|
|
||||||
// Calculate the final values and return everything.
|
// Calculate the final values and return everything.
|
||||||
let shadow_vec = ((x * sample.x()) + (y * sample.y()) + (z * sample.z())) *
|
let shadow_vec = {
|
||||||
space.inverse();
|
let sample_vec = (x * sample.x()) + (y * sample.y()) + (z * sample.z());
|
||||||
|
let sample_point = (arr + sample_vec).into_vector().normalized() * radius as f32;
|
||||||
|
let adjusted_sample_point = sample_point * SAMPLE_RADIUS_EXPAND_FACTOR;
|
||||||
|
(adjusted_sample_point.into_point() - arr) * space.inverse()
|
||||||
|
};
|
||||||
let pdf = uniform_sample_cone_pdf(cos_theta_max);
|
let pdf = uniform_sample_cone_pdf(cos_theta_max);
|
||||||
let spectral_sample = (col * surface_area_inv as f32).to_spectral_sample(wavelength);
|
let spectral_sample = (col * surface_area_inv as f32).to_spectral_sample(wavelength);
|
||||||
return (spectral_sample, shadow_vec, pdf as f32);
|
return (spectral_sample, shadow_vec, pdf as f32);
|
||||||
} else {
|
} else {
|
||||||
// If we're inside the sphere, there's light from every direction.
|
// If we're inside the sphere, there's light from every direction.
|
||||||
let shadow_vec = uniform_sample_sphere(u, v) * space.inverse();
|
let shadow_vec = {
|
||||||
|
let sample_vec = uniform_sample_sphere(u, v);
|
||||||
|
let sample_point = (arr + sample_vec).into_vector().normalized() * radius as f32;
|
||||||
|
let adjusted_sample_point = sample_point * SAMPLE_RADIUS_SHRINK_FACTOR;
|
||||||
|
(adjusted_sample_point.into_point() - arr) * space.inverse()
|
||||||
|
};
|
||||||
let pdf = 1.0 / (4.0 * PI_64);
|
let pdf = 1.0 / (4.0 * PI_64);
|
||||||
let spectral_sample = (col * surface_area_inv as f32).to_spectral_sample(wavelength);
|
let spectral_sample = (col * surface_area_inv as f32).to_spectral_sample(wavelength);
|
||||||
return (spectral_sample, shadow_vec, pdf as f32);
|
return (spectral_sample, shadow_vec, pdf as f32);
|
||||||
|
|
|
@ -44,6 +44,7 @@ mod image;
|
||||||
mod lerp;
|
mod lerp;
|
||||||
mod light;
|
mod light;
|
||||||
mod math;
|
mod math;
|
||||||
|
mod mis;
|
||||||
mod parse;
|
mod parse;
|
||||||
mod ray;
|
mod ray;
|
||||||
mod renderer;
|
mod renderer;
|
||||||
|
|
13
src/mis.rs
Normal file
13
src/mis.rs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
pub fn balance_heuristic(a: f32, b: f32) -> f32 {
|
||||||
|
let mis_fac = a / (a + b);
|
||||||
|
a / mis_fac
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn power_heuristic(a: f32, b: f32) -> f32 {
|
||||||
|
let a2 = a * a;
|
||||||
|
let b2 = b * b;
|
||||||
|
let mis_fac = a2 / (a2 + b2);
|
||||||
|
a / mis_fac
|
||||||
|
}
|
|
@ -19,6 +19,7 @@ use hash::hash_u32;
|
||||||
use hilbert;
|
use hilbert;
|
||||||
use image::Image;
|
use image::Image;
|
||||||
use math::{fast_logit, upper_power_of_two};
|
use math::{fast_logit, upper_power_of_two};
|
||||||
|
use mis::power_heuristic;
|
||||||
use ray::Ray;
|
use ray::Ray;
|
||||||
use scene::Scene;
|
use scene::Scene;
|
||||||
use surface;
|
use surface;
|
||||||
|
@ -372,8 +373,9 @@ pub struct LightPath {
|
||||||
wavelength: f32,
|
wavelength: f32,
|
||||||
|
|
||||||
next_bounce_ray: Option<Ray>,
|
next_bounce_ray: Option<Ray>,
|
||||||
next_attentuation_fac: Float4,
|
next_attenuation_fac: Float4,
|
||||||
|
|
||||||
|
closure_sample_pdf: f32,
|
||||||
light_attenuation: Float4,
|
light_attenuation: Float4,
|
||||||
pending_color_addition: Float4,
|
pending_color_addition: Float4,
|
||||||
color: Float4,
|
color: Float4,
|
||||||
|
@ -401,8 +403,9 @@ impl LightPath {
|
||||||
wavelength: wavelength,
|
wavelength: wavelength,
|
||||||
|
|
||||||
next_bounce_ray: None,
|
next_bounce_ray: None,
|
||||||
next_attentuation_fac: Float4::splat(1.0),
|
next_attenuation_fac: Float4::splat(1.0),
|
||||||
|
|
||||||
|
closure_sample_pdf: 1.0,
|
||||||
light_attenuation: Float4::splat(1.0),
|
light_attenuation: Float4::splat(1.0),
|
||||||
pending_color_addition: Float4::splat(0.0),
|
pending_color_addition: Float4::splat(0.0),
|
||||||
color: Float4::splat(0.0),
|
color: Float4::splat(0.0),
|
||||||
|
@ -449,10 +452,20 @@ impl LightPath {
|
||||||
// - Terminate the path.
|
// - Terminate the path.
|
||||||
use shading::surface_closure::SurfaceClosureUnion;
|
use shading::surface_closure::SurfaceClosureUnion;
|
||||||
if let &SurfaceClosureUnion::EmitClosure(ref clsr) = closure {
|
if let &SurfaceClosureUnion::EmitClosure(ref clsr) = closure {
|
||||||
self.color += clsr.emitted_color().e * self.light_attenuation;
|
if let LightPathEvent::CameraRay = self.event {
|
||||||
|
self.color += clsr.emitted_color().e;
|
||||||
|
} else {
|
||||||
|
let mis_pdf =
|
||||||
|
power_heuristic(self.closure_sample_pdf, idata.sample_pdf);
|
||||||
|
self.color += clsr.emitted_color().e * self.light_attenuation / mis_pdf;
|
||||||
|
};
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Roll the previous closure pdf into the attentuation
|
||||||
|
self.light_attenuation /= self.closure_sample_pdf;
|
||||||
|
|
||||||
// Prepare light ray
|
// Prepare light ray
|
||||||
let light_n = self.next_lds_samp();
|
let light_n = self.next_lds_samp();
|
||||||
let light_uvw = (
|
let light_uvw = (
|
||||||
|
@ -482,11 +495,20 @@ impl LightPath {
|
||||||
material.evaluate(ray.dir, shadow_vec, idata.nor, idata.nor_g);
|
material.evaluate(ray.dir, shadow_vec, idata.nor, idata.nor_g);
|
||||||
|
|
||||||
if attenuation.e.h_max() > 0.0 {
|
if attenuation.e.h_max() > 0.0 {
|
||||||
|
// Calculate MIS
|
||||||
|
let closure_pdf = material.sample_pdf(
|
||||||
|
ray.dir,
|
||||||
|
shadow_vec,
|
||||||
|
idata.nor,
|
||||||
|
idata.nor_g,
|
||||||
|
);
|
||||||
|
let light_mis_pdf = power_heuristic(light_pdf, closure_pdf);
|
||||||
|
|
||||||
// Calculate and store the light that will be contributed
|
// Calculate and store the light that will be contributed
|
||||||
// to the film plane if the light is not in shadow.
|
// to the film plane if the light is not in shadow.
|
||||||
self.pending_color_addition = light_color.e * attenuation.e *
|
self.pending_color_addition = light_color.e * attenuation.e *
|
||||||
self.light_attenuation /
|
self.light_attenuation /
|
||||||
(light_pdf * light_sel_pdf);
|
(light_mis_pdf * light_sel_pdf);
|
||||||
|
|
||||||
// Calculate the shadow ray for testing if the light is
|
// Calculate the shadow ray for testing if the light is
|
||||||
// in shadow or not.
|
// in shadow or not.
|
||||||
|
@ -536,7 +558,8 @@ impl LightPath {
|
||||||
if (pdf > 0.0) && (filter.e.h_max() > 0.0) {
|
if (pdf > 0.0) && (filter.e.h_max() > 0.0) {
|
||||||
// Account for the additional light attenuation from
|
// Account for the additional light attenuation from
|
||||||
// this bounce
|
// this bounce
|
||||||
self.next_attentuation_fac = filter.e / pdf;
|
self.next_attenuation_fac = filter.e;
|
||||||
|
self.closure_sample_pdf = pdf;
|
||||||
|
|
||||||
// Calculate the ray for this bounce
|
// Calculate the ray for this bounce
|
||||||
let offset_pos = robust_ray_origin(
|
let offset_pos = robust_ray_origin(
|
||||||
|
@ -564,7 +587,7 @@ impl LightPath {
|
||||||
} else if do_bounce {
|
} else if do_bounce {
|
||||||
*ray = self.next_bounce_ray.unwrap();
|
*ray = self.next_bounce_ray.unwrap();
|
||||||
self.event = LightPathEvent::BounceRay;
|
self.event = LightPathEvent::BounceRay;
|
||||||
self.light_attenuation *= self.next_attentuation_fac;
|
self.light_attenuation *= self.next_attenuation_fac;
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
|
@ -575,7 +598,8 @@ impl LightPath {
|
||||||
.world
|
.world
|
||||||
.background_color
|
.background_color
|
||||||
.to_spectral_sample(self.wavelength)
|
.to_spectral_sample(self.wavelength)
|
||||||
.e * self.light_attenuation;
|
.e * self.light_attenuation /
|
||||||
|
self.closure_sample_pdf;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -592,7 +616,7 @@ impl LightPath {
|
||||||
// Set up for the next bounce, if any
|
// Set up for the next bounce, if any
|
||||||
if let Some(ref nbr) = self.next_bounce_ray {
|
if let Some(ref nbr) = self.next_bounce_ray {
|
||||||
*ray = *nbr;
|
*ray = *nbr;
|
||||||
self.light_attenuation *= self.next_attentuation_fac;
|
self.light_attenuation *= self.next_attenuation_fac;
|
||||||
self.event = LightPathEvent::BounceRay;
|
self.event = LightPathEvent::BounceRay;
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -185,7 +185,7 @@ impl<'a> TracerInner<'a> {
|
||||||
) {
|
) {
|
||||||
match *obj {
|
match *obj {
|
||||||
Object::Surface(surface) => {
|
Object::Surface(surface) => {
|
||||||
let unassigned_shader = SimpleSurfaceShader::Lambert {
|
let unassigned_shader = SimpleSurfaceShader::Emit {
|
||||||
color: XYZ::from_tuple(rec709_to_xyz((1.0, 0.0, 1.0))),
|
color: XYZ::from_tuple(rec709_to_xyz((1.0, 0.0, 1.0))),
|
||||||
};
|
};
|
||||||
let shader = surface_shader.unwrap_or(&unassigned_shader);
|
let shader = surface_shader.unwrap_or(&unassigned_shader);
|
||||||
|
@ -199,8 +199,19 @@ impl<'a> TracerInner<'a> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Object::SurfaceLight(_) => {
|
Object::SurfaceLight(surface) => {
|
||||||
// TODO
|
// Lights don't use shaders
|
||||||
|
let bogus_shader = SimpleSurfaceShader::Emit {
|
||||||
|
color: XYZ::from_tuple(rec709_to_xyz((1.0, 0.0, 1.0))),
|
||||||
|
};
|
||||||
|
|
||||||
|
surface.intersect_rays(
|
||||||
|
rays,
|
||||||
|
wrays,
|
||||||
|
&mut self.isects,
|
||||||
|
&bogus_shader,
|
||||||
|
self.xform_stack.top(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user