Implemented ray intersection for RectangleLights.

This means that RectangleLights now work with MIS.  Yay!
This commit is contained in:
Nathan Vegdahl 2017-08-17 13:46:38 -07:00
parent 072d366892
commit 81c8da8113
2 changed files with 107 additions and 37 deletions

View File

@ -4,11 +4,12 @@ use bbox::BBox;
use boundable::Boundable;
use color::{XYZ, SpectralSample, Color};
use lerp::lerp_slice;
use math::{Vector, Normal, Point, Matrix4x4};
use math::{Vector, Normal, Point, Matrix4x4, cross};
use ray::{Ray, AccelRay};
use sampling::{spherical_triangle_solid_angle, uniform_sample_spherical_triangle};
use shading::surface_closure::{SurfaceClosureUnion, EmitClosure};
use shading::SurfaceShader;
use surface::{Surface, SurfaceIntersection};
use surface::{Surface, SurfaceIntersection, SurfaceIntersectionData, triangle};
use super::SurfaceLight;
@ -42,41 +43,43 @@ impl<'a> RectangleLight<'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);
// 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 dim = lerp_slice(self.dimensions, time);
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, 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 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);
// 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)
// }
1.0 / (area_1 + area_2)
}
// fn outgoing(
// &self,
@ -114,7 +117,6 @@ impl<'a> SurfaceLight for RectangleLight<'a> {
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);
// Get the four corners of the rectangle, transformed into world space
@ -196,9 +198,77 @@ impl<'a> Surface for RectangleLight<'a> {
shader: &SurfaceShader,
space: &[Matrix4x4],
) {
let _ = (accel_rays, wrays, isects, shader, space);
// TODO
// unimplemented!()
let _ = shader; // Silence 'unused' warning
for r in accel_rays.iter_mut() {
let wr = &wrays[r.id as usize];
// Calculate time interpolated values
let dim = lerp_slice(self.dimensions, r.time);
let xform = lerp_slice(space, r.time);
let space_inv = xform.inverse();
// Get the four corners of the rectangle, transformed into world space
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;
// Test against two triangles that make up the light
for tri in &[(p1, p2, p3), (p3, p4, p1)] {
if let Some((t, b0, b1, b2)) = triangle::intersect_ray(wr, *tri) {
if t < r.max_t {
if r.is_occlusion() {
isects[r.id as usize] = SurfaceIntersection::Occlude;
r.mark_done();
} else {
let (pos, pos_err) = triangle::surface_point(*tri, (b0, b1, b2));
let normal = cross(tri.0 - tri.1, tri.0 - tri.2).into_normal();
let intersection_data = SurfaceIntersectionData {
incoming: wr.dir,
t: t,
pos: pos,
pos_err: pos_err,
nor: normal,
nor_g: normal,
uv: (0.0, 0.0), // TODO
local_space: xform,
sample_pdf: self.sample_pdf(
&xform,
wr.orig,
wr.dir,
0.0,
0.0,
wr.wavelength,
r.time,
),
};
let closure = {
let inv_surface_area = (1.0 / (dim.0 as f64 * dim.1 as f64)) as f32;
let color = lerp_slice(self.colors, r.time).to_spectral_sample(
wr.wavelength,
) * inv_surface_area;
SurfaceClosureUnion::EmitClosure(EmitClosure::new(color))
};
// Fill in intersection
isects[r.id as usize] = SurfaceIntersection::Hit {
intersection_data: intersection_data,
closure: closure,
};
// Set ray's max t
r.max_t = t;
}
break;
}
}
}
}
}
}

View File

@ -1,6 +1,6 @@
#![allow(dead_code)]
mod triangle;
pub mod triangle;
pub mod triangle_mesh;
use std::fmt::Debug;