From 81c8da8113f66ba580283946d12ed769d55946d5 Mon Sep 17 00:00:00 2001 From: Nathan Vegdahl Date: Thu, 17 Aug 2017 13:46:38 -0700 Subject: [PATCH] Implemented ray intersection for RectangleLights. This means that RectangleLights now work with MIS. Yay! --- src/light/rectangle_light.rs | 142 ++++++++++++++++++++++++++--------- src/surface/mod.rs | 2 +- 2 files changed, 107 insertions(+), 37 deletions(-) diff --git a/src/light/rectangle_light.rs b/src/light/rectangle_light.rs index b7f43f7..29c7707 100644 --- a/src/light/rectangle_light.rs +++ b/src/light/rectangle_light.rs @@ -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; + } + } + } + } } } diff --git a/src/surface/mod.rs b/src/surface/mod.rs index 076175d..5389618 100644 --- a/src/surface/mod.rs +++ b/src/surface/mod.rs @@ -1,6 +1,6 @@ #![allow(dead_code)] -mod triangle; +pub mod triangle; pub mod triangle_mesh; use std::fmt::Debug;