WIP: multiple importance sampling.

Reorganized light and surface traits so that light sources are
surfaces as well, which will let them slide easily into
intersection tests with the rest of the scene geometry.
This commit is contained in:
Nathan Vegdahl 2017-08-16 18:17:50 -07:00
parent 5c91aca002
commit 462977bd4d
9 changed files with 190 additions and 242 deletions

View File

@ -31,10 +31,36 @@ impl<'a> DistantDiskLight<'a> {
colors: arena.copy_slice(&colors), colors: arena.copy_slice(&colors),
} }
} }
// fn sample_pdf(&self, sample_dir: Vector, wavelength: f32, time: f32) -> f32 {
// // We're not using these, silence warnings
// let _ = (sample_dir, wavelength);
// let radius: f64 = lerp_slice(self.radii, time) as f64;
// uniform_sample_cone_pdf(radius.cos()) as f32
// }
// fn outgoing(&self, dir: Vector, wavelength: f32, time: f32) -> SpectralSample {
// // We're not using this, silence warning
// let _ = dir;
// let radius = lerp_slice(self.radii, time) as f64;
// let col = lerp_slice(self.colors, time);
// let solid_angle = 2.0 * PI_64 * (1.0 - radius.cos());
// (col / solid_angle as f32).to_spectral_sample(wavelength)
// }
} }
impl<'a> WorldLightSource for DistantDiskLight<'a> { impl<'a> WorldLightSource for DistantDiskLight<'a> {
fn sample(&self, u: f32, v: f32, wavelength: f32, time: f32) -> (SpectralSample, Vector, f32) { fn sample_from_point(
&self,
u: f32,
v: f32,
wavelength: f32,
time: f32,
) -> (SpectralSample, Vector, f32) {
// Calculate time interpolated values // Calculate time interpolated values
let radius: f64 = lerp_slice(self.radii, time) as f64; let radius: f64 = lerp_slice(self.radii, time) as f64;
let direction = lerp_slice(self.directions, time); let direction = lerp_slice(self.directions, time);
@ -56,26 +82,6 @@ impl<'a> WorldLightSource for DistantDiskLight<'a> {
(spectral_sample, shadow_vec, pdf as f32) (spectral_sample, shadow_vec, pdf as f32)
} }
fn sample_pdf(&self, sample_dir: Vector, wavelength: f32, time: f32) -> f32 {
// We're not using these, silence warnings
let _ = (sample_dir, wavelength);
let radius: f64 = lerp_slice(self.radii, time) as f64;
uniform_sample_cone_pdf(radius.cos()) as f32
}
fn outgoing(&self, dir: Vector, wavelength: f32, time: f32) -> SpectralSample {
// We're not using this, silence warning
let _ = dir;
let radius = lerp_slice(self.radii, time) as f64;
let col = lerp_slice(self.colors, time);
let solid_angle = 2.0 * PI_64 * (1.0 - radius.cos());
(col / solid_angle as f32).to_spectral_sample(wavelength)
}
fn is_delta(&self) -> bool { fn is_delta(&self) -> bool {
false false
} }

View File

@ -4,11 +4,9 @@ mod sphere_light;
use std::fmt::Debug; use std::fmt::Debug;
use boundable::Boundable;
use color::SpectralSample; use color::SpectralSample;
use math::{Vector, Point, Matrix4x4}; use math::{Vector, Point, Matrix4x4};
use ray::{Ray, AccelRay}; use surface::Surface;
use surface::SurfaceIntersection;
pub use self::distant_disk_light::DistantDiskLight; pub use self::distant_disk_light::DistantDiskLight;
pub use self::rectangle_light::RectangleLight; pub use self::rectangle_light::RectangleLight;
@ -16,19 +14,19 @@ pub use self::sphere_light::SphereLight;
/// A finite light source that can be bounded in space. /// A finite light source that can be bounded in space.
pub trait LightSource: Boundable + Debug + Sync { pub trait SurfaceLight: Surface {
/// Samples the light source for a given point to be illuminated. /// Samples the surface given a point to be illuminated.
/// ///
/// - space: The world-to-object space transform of the light. /// - `space`: The world-to-object space transform of the light.
/// - arr: The point to be illuminated (in world space). /// - `arr`: The point to be illuminated (in world space).
/// - u: Random parameter U. /// - `u`: Random parameter U.
/// - v: Random parameter V. /// - `v`: Random parameter V.
/// - wavelength: The wavelength of light to sample at. /// - `wavelength`: The wavelength of light to sample at.
/// - time: The time to sample at. /// - `time`: The time to sample at.
/// ///
/// Returns: The light arriving at the point arr, the vector to use for /// Returns: The light arriving at the point arr, the vector to use for
/// shadow testing, and the pdf of the sample. /// shadow testing, and the pdf of the sample.
fn sample( fn sample_from_point(
&self, &self,
space: &Matrix4x4, space: &Matrix4x4,
arr: Point, arr: Point,
@ -39,53 +37,6 @@ pub trait LightSource: Boundable + Debug + Sync {
) -> (SpectralSample, Vector, f32); ) -> (SpectralSample, Vector, f32);
/// Calculates the pdf of sampling the given
/// `sample_dir`/`sample_u`/`sample_v` from the given point `arr`. This is used
/// primarily to calculate probabilities for multiple importance sampling.
///
/// NOTE: this function CAN assume that sample_dir, sample_u, and sample_v
/// are a valid sample for the light source (i.e. hits/lies on the light
/// source). No guarantees are made about the correctness of the return
/// value if they are not valid.
///
/// TODO: this probably shouldn't be part of the public interface. In the
/// rest of the renderer, the PDF is always calculated by the `sample` and
/// and `intersect_rays` methods.
fn sample_pdf(
&self,
space: &Matrix4x4,
arr: Point,
sample_dir: Vector,
sample_u: f32,
sample_v: f32,
wavelength: f32,
time: f32,
) -> f32;
/// Returns the color emitted in the given direction from the
/// given parameters on the light.
///
/// - dir: The direction of the outgoing light.
/// - u: Random parameter U.
/// - v: Random parameter V.
/// - wavelength: The hero wavelength of light to sample at.
/// - time: The time to sample at.
///
/// TODO: this probably shouldn't be part of the public interface. In the
/// rest of the renderer, this is handled by the `sample` and
/// `intersect_rays` methods.
fn outgoing(
&self,
space: &Matrix4x4,
dir: Vector,
u: f32,
v: f32,
wavelength: f32,
time: f32,
) -> SpectralSample;
/// Returns whether the light has a delta distribution. /// Returns whether the light has a delta distribution.
/// ///
/// If a light has no chance of a ray hitting it through random process /// If a light has no chance of a ray hitting it through random process
@ -94,18 +45,12 @@ pub trait LightSource: Boundable + Debug + Sync {
fn is_delta(&self) -> bool; fn is_delta(&self) -> bool;
/// Returns an approximation of the total energy emitted by the light /// Returns an approximation of the total energy emitted by the surface.
/// source. Note that this does not need to be exact: it is used for ///
/// importance sampling. /// Note: this does not need to be exact, but it does need to be non-zero
/// for any surface that does emit light. This is used for importance
/// sampling.
fn approximate_energy(&self) -> f32; fn approximate_energy(&self) -> f32;
fn intersect_rays(
&self,
accel_rays: &mut [AccelRay],
wrays: &[Ray],
isects: &mut [SurfaceIntersection],
space: &[Matrix4x4],
);
} }
@ -121,25 +66,13 @@ pub trait WorldLightSource: Debug + Sync {
/// ///
/// Returns: The light arriving from the shadow-testing direction, the /// Returns: The light arriving from the shadow-testing direction, the
/// vector to use for shadow testing, and the pdf of the sample. /// vector to use for shadow testing, and the pdf of the sample.
fn sample(&self, u: f32, v: f32, wavelength: f32, time: f32) -> (SpectralSample, Vector, f32); fn sample_from_point(
&self,
u: f32,
/// Calculates the pdf of sampling the given sample_dir. This is used v: f32,
/// primarily to calculate probabilities for multiple importance sampling. wavelength: f32,
/// time: f32,
/// NOTE: this function CAN assume that sample_dir is a valid sample for ) -> (SpectralSample, Vector, f32);
/// the light source (i.e. hits/lies on the light source). No guarantees
/// are made about the correctness of the return value if it isn't valid.
fn sample_pdf(&self, sample_dir: Vector, wavelength: f32, time: f32) -> f32;
/// Returns the color emitted in the given direction from the
/// given parameters on the light.
///
/// - dir: The direction of the outgoing light.
/// - wavelength: The hero wavelength of light to sample at.
/// - time: The time to sample at.
fn outgoing(&self, dir: Vector, wavelength: f32, time: f32) -> SpectralSample;
/// Returns whether the light has a delta distribution. /// Returns whether the light has a delta distribution.
@ -151,7 +84,10 @@ pub trait WorldLightSource: Debug + Sync {
/// Returns an approximation of the total energy emitted by the light /// Returns an approximation of the total energy emitted by the light
/// source. Note that this does not need to be exact: it is used for /// source.
/// importance sampling. ///
/// Note: this does not need to be exact, but it does need to be non-zero
/// for any light that emits any light. This is used for importance
/// sampling.
fn approximate_energy(&self) -> f32; fn approximate_energy(&self) -> f32;
} }

View File

@ -7,9 +7,10 @@ use lerp::lerp_slice;
use math::{Vector, Point, Matrix4x4}; use math::{Vector, Point, Matrix4x4};
use ray::{Ray, AccelRay}; use ray::{Ray, AccelRay};
use sampling::{spherical_triangle_solid_angle, uniform_sample_spherical_triangle}; use sampling::{spherical_triangle_solid_angle, uniform_sample_spherical_triangle};
use surface::SurfaceIntersection; use shading::SurfaceShader;
use surface::{Surface, SurfaceIntersection};
use super::LightSource; use super::SurfaceLight;
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
@ -40,10 +41,67 @@ impl<'a> RectangleLight<'a> {
bounds_: arena.copy_slice(&bbs), bounds_: arena.copy_slice(&bbs),
} }
} }
// fn sample_pdf(
// &self,
// space: &Matrix4x4,
// arr: Point,
// sample_dir: Vector,
// sample_u: f32,
// sample_v: f32,
// wavelength: f32,
// time: f32,
// ) -> f32 {
// // We're not using these, silence warnings
// let _ = (sample_dir, sample_u, sample_v, wavelength);
// let dim = lerp_slice(self.dimensions, time);
// // Get the four corners of the rectangle, transformed into world space
// let space_inv = space.inverse();
// let p1 = Point::new(dim.0 * 0.5, dim.1 * 0.5, 0.0) * space_inv;
// let p2 = Point::new(dim.0 * -0.5, dim.1 * 0.5, 0.0) * space_inv;
// let p3 = Point::new(dim.0 * -0.5, dim.1 * -0.5, 0.0) * space_inv;
// let p4 = Point::new(dim.0 * 0.5, dim.1 * -0.5, 0.0) * space_inv;
// // Get the four corners of the rectangle, projected on to the unit
// // sphere centered around arr.
// let sp1 = (p1 - arr).normalized();
// let sp2 = (p2 - arr).normalized();
// let sp3 = (p3 - arr).normalized();
// let sp4 = (p4 - arr).normalized();
// // Get the solid angles of the rectangle split into two triangles
// let area_1 = spherical_triangle_solid_angle(sp2, sp1, sp3);
// let area_2 = spherical_triangle_solid_angle(sp4, sp1, sp3);
// 1.0 / (area_1 + area_2)
// }
// fn outgoing(
// &self,
// space: &Matrix4x4,
// dir: Vector,
// u: f32,
// v: f32,
// wavelength: f32,
// time: f32,
// ) -> SpectralSample {
// // We're not using these, silence warnings
// let _ = (space, dir, u, v);
// let dim = lerp_slice(self.dimensions, time);
// let col = lerp_slice(self.colors, time);
// // TODO: Is this right? Do we need to get the surface area post-transform?
// let surface_area_inv: f64 = 1.0 / (dim.0 as f64 * dim.1 as f64);
// (col * surface_area_inv as f32 * 0.5).to_spectral_sample(wavelength)
// }
} }
impl<'a> LightSource for RectangleLight<'a> { impl<'a> SurfaceLight for RectangleLight<'a> {
fn sample( fn sample_from_point(
&self, &self,
space: &Matrix4x4, space: &Matrix4x4,
arr: Point, arr: Point,
@ -110,63 +168,6 @@ impl<'a> LightSource for RectangleLight<'a> {
(spectral_sample, shadow_vec, pdf as f32) (spectral_sample, shadow_vec, pdf as f32)
} }
fn sample_pdf(
&self,
space: &Matrix4x4,
arr: Point,
sample_dir: Vector,
sample_u: f32,
sample_v: f32,
wavelength: f32,
time: f32,
) -> f32 {
// We're not using these, silence warnings
let _ = (sample_dir, sample_u, sample_v, wavelength);
let dim = lerp_slice(self.dimensions, time);
// Get the four corners of the rectangle, transformed into world space
let space_inv = space.inverse();
let p1 = Point::new(dim.0 * 0.5, dim.1 * 0.5, 0.0) * space_inv;
let p2 = Point::new(dim.0 * -0.5, dim.1 * 0.5, 0.0) * space_inv;
let p3 = Point::new(dim.0 * -0.5, dim.1 * -0.5, 0.0) * space_inv;
let p4 = Point::new(dim.0 * 0.5, dim.1 * -0.5, 0.0) * space_inv;
// Get the four corners of the rectangle, projected on to the unit
// sphere centered around arr.
let sp1 = (p1 - arr).normalized();
let sp2 = (p2 - arr).normalized();
let sp3 = (p3 - arr).normalized();
let sp4 = (p4 - arr).normalized();
// Get the solid angles of the rectangle split into two triangles
let area_1 = spherical_triangle_solid_angle(sp2, sp1, sp3);
let area_2 = spherical_triangle_solid_angle(sp4, sp1, sp3);
1.0 / (area_1 + area_2)
}
fn outgoing(
&self,
space: &Matrix4x4,
dir: Vector,
u: f32,
v: f32,
wavelength: f32,
time: f32,
) -> SpectralSample {
// We're not using these, silence warnings
let _ = (space, dir, u, v);
let dim = lerp_slice(self.dimensions, time);
let col = lerp_slice(self.colors, time);
// TODO: Is this right? Do we need to get the surface area post-transform?
let surface_area_inv: f64 = 1.0 / (dim.0 as f64 * dim.1 as f64);
(col * surface_area_inv as f32 * 0.5).to_spectral_sample(wavelength)
}
fn is_delta(&self) -> bool { fn is_delta(&self) -> bool {
false false
} }
@ -178,14 +179,19 @@ impl<'a> LightSource for RectangleLight<'a> {
) / self.colors.len() as f32; ) / self.colors.len() as f32;
color.y color.y
} }
}
impl<'a> Surface for RectangleLight<'a> {
fn intersect_rays( fn intersect_rays(
&self, &self,
accel_rays: &mut [AccelRay], accel_rays: &mut [AccelRay],
wrays: &[Ray], wrays: &[Ray],
isects: &mut [SurfaceIntersection], isects: &mut [SurfaceIntersection],
shader: &SurfaceShader,
space: &[Matrix4x4], space: &[Matrix4x4],
) { ) {
let _ = (accel_rays, wrays, isects, shader, space);
unimplemented!() unimplemented!()
} }
} }

View File

@ -9,10 +9,11 @@ use lerp::lerp_slice;
use math::{Vector, Point, Matrix4x4, dot, coordinate_system_from_vector}; use math::{Vector, Point, Matrix4x4, dot, coordinate_system_from_vector};
use ray::{Ray, AccelRay}; use ray::{Ray, AccelRay};
use sampling::{uniform_sample_cone, uniform_sample_cone_pdf, uniform_sample_sphere}; use sampling::{uniform_sample_cone, uniform_sample_cone_pdf, uniform_sample_sphere};
use surface::{SurfaceIntersection, SurfaceIntersectionData};
use shading::surface_closure::{SurfaceClosureUnion, EmitClosure}; use shading::surface_closure::{SurfaceClosureUnion, EmitClosure};
use shading::SurfaceShader;
use surface::{Surface, SurfaceIntersection, SurfaceIntersectionData};
use super::LightSource; use super::SurfaceLight;
// TODO: handle case where radius = 0.0. // TODO: handle case where radius = 0.0.
@ -40,10 +41,45 @@ impl<'a> SphereLight<'a> {
bounds_: arena.copy_slice(&bbs), bounds_: arena.copy_slice(&bbs),
} }
} }
// TODO: this is only used from within `intersect_rays`, and could be done
// more efficiently by inlining it there.
fn sample_pdf(
&self,
space: &Matrix4x4,
arr: Point,
sample_dir: Vector,
sample_u: f32,
sample_v: f32,
wavelength: f32,
time: f32,
) -> f32 {
// We're not using these, silence warnings
let _ = (sample_dir, sample_u, sample_v, wavelength);
let arr = arr * *space;
let pos = Point::new(0.0, 0.0, 0.0);
let radius: f64 = lerp_slice(self.radii, time) as f64;
let d2: f64 = (pos - arr).length2() as f64; // Distance from center of sphere squared
let d: f64 = d2.sqrt(); // Distance from center of sphere
if d > radius {
// Calculate the portion of the sphere visible from the point
let sin_theta_max2: f64 = ((radius * radius) / d2).min(1.0);
let cos_theta_max2: f64 = 1.0 - sin_theta_max2;
let cos_theta_max: f64 = cos_theta_max2.sqrt();
uniform_sample_cone_pdf(cos_theta_max) as f32
} else {
(1.0 / (4.0 * PI_64)) as f32
}
}
} }
impl<'a> LightSource for SphereLight<'a> {
fn sample( impl<'a> SurfaceLight for SphereLight<'a> {
fn sample_from_point(
&self, &self,
space: &Matrix4x4, space: &Matrix4x4,
arr: Point, arr: Point,
@ -61,7 +97,6 @@ impl<'a> LightSource for SphereLight<'a> {
let col = lerp_slice(self.colors, time); let col = lerp_slice(self.colors, time);
let surface_area_inv: f64 = 1.0 / (4.0 * PI_64 * radius * radius); let surface_area_inv: f64 = 1.0 / (4.0 * PI_64 * radius * radius);
// Create a coordinate system from the vector between the // Create a coordinate system from the vector between the
// point and the center of the light // point and the center of the light
let z = pos - arr; let z = pos - arr;
@ -119,57 +154,6 @@ impl<'a> LightSource for SphereLight<'a> {
} }
} }
fn sample_pdf(
&self,
space: &Matrix4x4,
arr: Point,
sample_dir: Vector,
sample_u: f32,
sample_v: f32,
wavelength: f32,
time: f32,
) -> f32 {
// We're not using these, silence warnings
let _ = (sample_dir, sample_u, sample_v, wavelength);
let arr = arr * *space;
let pos = Point::new(0.0, 0.0, 0.0);
let radius: f64 = lerp_slice(self.radii, time) as f64;
let d2: f64 = (pos - arr).length2() as f64; // Distance from center of sphere squared
let d: f64 = d2.sqrt(); // Distance from center of sphere
if d > radius {
// Calculate the portion of the sphere visible from the point
let sin_theta_max2: f64 = ((radius * radius) / d2).min(1.0);
let cos_theta_max2: f64 = 1.0 - sin_theta_max2;
let cos_theta_max: f64 = cos_theta_max2.sqrt();
uniform_sample_cone_pdf(cos_theta_max) as f32
} else {
(1.0 / (4.0 * PI_64)) as f32
}
}
fn outgoing(
&self,
space: &Matrix4x4,
dir: Vector,
u: f32,
v: f32,
wavelength: f32,
time: f32,
) -> SpectralSample {
// We're not using these, silence warnings
let _ = (space, dir, u, v);
// TODO: use transform space correctly
let radius = lerp_slice(self.radii, time) as f64;
let col = lerp_slice(self.colors, time);
let surface_area = 4.0 * PI_64 * radius * radius;
(col / surface_area as f32).to_spectral_sample(wavelength)
}
fn is_delta(&self) -> bool { fn is_delta(&self) -> bool {
false false
} }
@ -181,14 +165,20 @@ impl<'a> LightSource for SphereLight<'a> {
) / self.colors.len() as f32; ) / self.colors.len() as f32;
color.y color.y
} }
}
impl<'a> Surface for SphereLight<'a> {
fn intersect_rays( fn intersect_rays(
&self, &self,
accel_rays: &mut [AccelRay], accel_rays: &mut [AccelRay],
wrays: &[Ray], wrays: &[Ray],
isects: &mut [SurfaceIntersection], isects: &mut [SurfaceIntersection],
shader: &SurfaceShader,
space: &[Matrix4x4], space: &[Matrix4x4],
) { ) {
let _ = shader; // Silence 'unused' warning
for r in accel_rays.iter_mut() { for r in accel_rays.iter_mut() {
let wr = &wrays[r.id as usize]; let wr = &wrays[r.id as usize];
@ -316,6 +306,7 @@ impl<'a> LightSource for SphereLight<'a> {
} }
} }
impl<'a> Boundable for SphereLight<'a> { impl<'a> Boundable for SphereLight<'a> {
fn bounds(&self) -> &[BBox] { fn bounds(&self) -> &[BBox] {
self.bounds_ self.bounds_

View File

@ -119,7 +119,7 @@ pub fn parse_assembly<'a>(
if let DataTree::Internal { ident: Some(ident), .. } = *child { if let DataTree::Internal { ident: Some(ident), .. } = *child {
builder.add_object( builder.add_object(
ident, ident,
Object::Light(arena.alloc(parse_sphere_light(arena, child)?)), Object::SurfaceLight(arena.alloc(parse_sphere_light(arena, child)?)),
); );
} else { } else {
// No ident // No ident
@ -132,7 +132,9 @@ pub fn parse_assembly<'a>(
if let DataTree::Internal { ident: Some(ident), .. } = *child { if let DataTree::Internal { ident: Some(ident), .. } = *child {
builder.add_object( builder.add_object(
ident, ident,
Object::Light(arena.alloc(parse_rectangle_light(arena, child)?)), Object::SurfaceLight(
arena.alloc(parse_rectangle_light(arena, child)?),
),
); );
} else { } else {
// No ident // No ident

View File

@ -8,7 +8,7 @@ use bbox::{BBox, transform_bbox_slice_from};
use boundable::Boundable; use boundable::Boundable;
use color::SpectralSample; use color::SpectralSample;
use lerp::lerp_slice; use lerp::lerp_slice;
use light::LightSource; use light::SurfaceLight;
use math::{Matrix4x4, Vector}; use math::{Matrix4x4, Vector};
use surface::{Surface, SurfaceIntersection}; use surface::{Surface, SurfaceIntersection};
use shading::SurfaceShader; use shading::SurfaceShader;
@ -75,7 +75,7 @@ impl<'a> Assembly<'a> {
InstanceType::Object => { InstanceType::Object => {
match self.objects[inst.data_index] { match self.objects[inst.data_index] {
Object::Light(light) => { Object::SurfaceLight(light) => {
// Get the world-to-object space transform of the light // Get the world-to-object space transform of the light
let xform = if let Some((a, b)) = inst.transform_indices { let xform = if let Some((a, b)) = inst.transform_indices {
let pxforms = xform_stack.top(); let pxforms = xform_stack.top();
@ -95,8 +95,14 @@ impl<'a> Assembly<'a> {
}; };
// Sample the light // Sample the light
let (color, shadow_vec, pdf) = let (color, shadow_vec, pdf) = light.sample_from_point(
light.sample(&xform, idata.pos, uvw.0, uvw.1, wavelength, time); &xform,
idata.pos,
uvw.0,
uvw.1,
wavelength,
time,
);
return Some((color, shadow_vec, pdf, sel_pdf)); return Some((color, shadow_vec, pdf, sel_pdf));
} }
@ -303,7 +309,7 @@ impl<'a> AssemblyBuilder<'a> {
.iter() .iter()
.filter(|inst| match inst.instance_type { .filter(|inst| match inst.instance_type {
InstanceType::Object => { InstanceType::Object => {
if let Object::Light(_) = self.objects[inst.data_index] { if let Object::SurfaceLight(_) = self.objects[inst.data_index] {
true true
} else { } else {
false false
@ -324,7 +330,7 @@ impl<'a> AssemblyBuilder<'a> {
let bounds = &bbs[bis[inst.id]..bis[inst.id + 1]]; let bounds = &bbs[bis[inst.id]..bis[inst.id + 1]];
let energy = match inst.instance_type { let energy = match inst.instance_type {
InstanceType::Object => { InstanceType::Object => {
if let Object::Light(light) = self.objects[inst.data_index] { if let Object::SurfaceLight(light) = self.objects[inst.data_index] {
light.approximate_energy() light.approximate_energy()
} else { } else {
0.0 0.0
@ -370,7 +376,7 @@ impl<'a> AssemblyBuilder<'a> {
let obj = &self.objects[inst.data_index]; let obj = &self.objects[inst.data_index];
match *obj { match *obj {
Object::Surface(s) => bbs.extend(s.bounds()), Object::Surface(s) => bbs.extend(s.bounds()),
Object::Light(l) => bbs.extend(l.bounds()), Object::SurfaceLight(l) => bbs.extend(l.bounds()),
} }
} }
@ -404,7 +410,7 @@ impl<'a> AssemblyBuilder<'a> {
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub enum Object<'a> { pub enum Object<'a> {
Surface(&'a Surface), Surface(&'a Surface),
Light(&'a LightSource), SurfaceLight(&'a SurfaceLight),
} }

View File

@ -60,7 +60,8 @@ impl<'a> Scene<'a> {
// World lights // World lights
let n = n / wl_prob; let n = n / wl_prob;
let (i, p) = weighted_choice(self.world.lights, n, |l| l.approximate_energy()); let (i, p) = weighted_choice(self.world.lights, n, |l| l.approximate_energy());
let (ss, sv, pdf) = self.world.lights[i].sample(uvw.0, uvw.1, wavelength, time); let (ss, sv, pdf) =
self.world.lights[i].sample_from_point(uvw.0, uvw.1, wavelength, time);
return Some((ss, sv, pdf, p * wl_prob, true)); return Some((ss, sv, pdf, p * wl_prob, true));
} else { } else {
// Local lights // Local lights

View File

@ -8,8 +8,8 @@ use std::fmt::Debug;
use boundable::Boundable; use boundable::Boundable;
use math::{Point, Vector, Normal, Matrix4x4}; use math::{Point, Vector, Normal, Matrix4x4};
use ray::{Ray, AccelRay}; use ray::{Ray, AccelRay};
use shading::SurfaceShader;
use shading::surface_closure::SurfaceClosureUnion; use shading::surface_closure::SurfaceClosureUnion;
use shading::SurfaceShader;
pub trait Surface: Boundable + Debug + Sync { pub trait Surface: Boundable + Debug + Sync {

View File

@ -199,7 +199,7 @@ impl<'a> TracerInner<'a> {
); );
} }
Object::Light(_) => { Object::SurfaceLight(_) => {
// TODO // TODO
} }
} }