From 18245b725c83c5eff92751d9d1bb679c13d14d5c Mon Sep 17 00:00:00 2001 From: Nathan Vegdahl Date: Thu, 9 Jun 2016 01:04:12 -0700 Subject: [PATCH] All final intersections are now done in world space. BVH traversal still happens in local space, but final actual surface intersection calculations are done in world space by transforming the triangle into world space. This is to improve numerical consistency between intersections. --- src/bbox.rs | 4 ++-- src/bvh.rs | 12 ++++++------ src/main.rs | 5 +++-- src/ray.rs | 35 +++++++++++++++++++++++++++-------- src/renderer.rs | 3 +-- src/surface/mod.rs | 10 +++++++--- src/surface/triangle_mesh.rs | 24 ++++++++++++++++-------- src/tracer.rs | 18 +++++++++++------- 8 files changed, 73 insertions(+), 38 deletions(-) diff --git a/src/bbox.rs b/src/bbox.rs index 88c3b6a..75ecea2 100644 --- a/src/bbox.rs +++ b/src/bbox.rs @@ -6,7 +6,7 @@ use std::iter::Iterator; use math::{Point, Matrix4x4}; use lerp::{lerp, lerp_slice, Lerp}; -use ray::Ray; +use ray::AccelRay; const BBOX_MAXT_ADJUST: f32 = 1.00000024; @@ -38,7 +38,7 @@ impl BBox { } // Returns whether the given ray intersects with the bbox. - pub fn intersect_ray(&self, ray: &Ray) -> bool { + pub fn intersect_accel_ray(&self, ray: &AccelRay) -> bool { // Calculate slab intersections let t1 = (self.min.co - ray.orig.co) * ray.dir_inv.co; let t2 = (self.max.co - ray.orig.co) * ray.dir_inv.co; diff --git a/src/bvh.rs b/src/bvh.rs index 4cc8287..6a6c32a 100644 --- a/src/bvh.rs +++ b/src/bvh.rs @@ -3,7 +3,7 @@ use lerp::lerp_slice; use bbox::BBox; use boundable::Boundable; -use ray::Ray; +use ray::{Ray, AccelRay}; use algorithm::partition; #[derive(Debug)] @@ -177,8 +177,8 @@ impl BVH { } - pub fn traverse(&self, rays: &mut [Ray], objects: &[T], mut obj_ray_test: F) - where F: FnMut(&T, &mut [Ray]) + pub fn traverse(&self, rays: &mut [AccelRay], objects: &[T], mut obj_ray_test: F) + where F: FnMut(&T, &mut [AccelRay]) { if self.nodes.len() == 0 { return; @@ -192,14 +192,14 @@ impl BVH { match self.nodes[i_stack[stack_ptr]] { BVHNode::Internal { bounds_range: br, second_child_index, split_axis } => { let part = partition(&mut rays[..ray_i_stack[stack_ptr]], |r| { - lerp_slice(&self.bounds[br.0..br.1], r.time).intersect_ray(r) + lerp_slice(&self.bounds[br.0..br.1], r.time).intersect_accel_ray(r) }); if part > 0 { i_stack[stack_ptr] += 1; i_stack[stack_ptr + 1] = second_child_index; ray_i_stack[stack_ptr] = part; ray_i_stack[stack_ptr + 1] = part; - if rays[0].dir[split_axis as usize].is_sign_positive() { + if rays[0].dir_inv[split_axis as usize].is_sign_positive() { i_stack.swap(stack_ptr, stack_ptr + 1); } stack_ptr += 1; @@ -210,7 +210,7 @@ impl BVH { BVHNode::Leaf { bounds_range: br, object_range } => { let part = partition(&mut rays[..ray_i_stack[stack_ptr]], |r| { - lerp_slice(&self.bounds[br.0..br.1], r.time).intersect_ray(r) + lerp_slice(&self.bounds[br.0..br.1], r.time).intersect_accel_ray(r) }); if part > 0 { for obj in &objects[object_range.0..object_range.1] { diff --git a/src/main.rs b/src/main.rs index 96918c2..5c692d7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -33,7 +33,7 @@ use std::fs::File; use docopt::Docopt; -use ray::Ray; +use ray::{Ray, AccelRay}; use parse::{parse_scene, DataTree}; // ---------------------------------------------------------------- @@ -91,7 +91,8 @@ fn main() { panic!() }; - println!("Ray size: {} bytes", mem::size_of::()); + println!("Ray size: {} bytes", mem::size_of::()); + println!("AccelRay size: {} bytes", mem::size_of::()); // Iterate through scenes and render them if let DataTree::Internal { ref children, .. } = dt { diff --git a/src/ray.rs b/src/ray.rs index 02bdb9f..108b142 100644 --- a/src/ray.rs +++ b/src/ray.rs @@ -9,10 +9,8 @@ use math::{Vector, Point, Matrix4x4}; pub struct Ray { pub orig: Point, pub dir: Vector, - pub dir_inv: Vector, pub max_t: f32, pub time: f32, - pub id: u32, pub flags: u32, } @@ -21,10 +19,8 @@ impl Ray { Ray { orig: orig, dir: dir, - dir_inv: Vector { co: Float4::new(1.0, 1.0, 1.0, 1.0) / dir.co }, max_t: std::f32::INFINITY, time: time, - id: 0, flags: 0, } } @@ -32,16 +28,39 @@ impl Ray { pub fn transform(&mut self, mat: &Matrix4x4) { self.orig = self.orig * *mat; self.dir = self.dir * *mat; - self.dir_inv = Vector { co: Float4::new(1.0, 1.0, 1.0, 1.0) / self.dir.co }; + } +} + + +#[derive(Debug, Copy, Clone)] +pub struct AccelRay { + pub orig: Point, + pub dir_inv: Vector, + pub max_t: f32, + pub time: f32, + pub flags: u32, + pub id: u32, +} + +impl AccelRay { + pub fn new(ray: &Ray, id: u32) -> AccelRay { + AccelRay { + orig: ray.orig, + dir_inv: Vector { co: Float4::new(1.0, 1.0, 1.0, 1.0) / ray.dir.co }, + max_t: ray.max_t, + time: ray.time, + flags: ray.flags, + id: id, + } } pub fn update_from_world_ray(&mut self, wr: &Ray) { self.orig = wr.orig; - self.dir = wr.dir; + self.dir_inv = Vector { co: Float4::new(1.0, 1.0, 1.0, 1.0) / wr.dir.co }; } pub fn update_from_xformed_world_ray(&mut self, wr: &Ray, mat: &Matrix4x4) { - self.update_from_world_ray(wr); - self.transform(mat); + self.orig = wr.orig * *mat; + self.dir_inv = Vector { co: Float4::new(1.0, 1.0, 1.0, 1.0) / (wr.dir * *mat).co }; } } diff --git a/src/renderer.rs b/src/renderer.rs index e9675d4..70eec64 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -102,7 +102,6 @@ impl Renderer { halton::sample(1, offset + si as u32), halton::sample(2, offset + si as u32)) }; - ray.id = rays.len() as u32; rays.push(ray); pixel_mapping.push((x, y)) } @@ -120,7 +119,7 @@ impl Renderer { if let &surface::SurfaceIntersection::Hit { t: _, pos: _, nor: _, - space: _, + local_space: _, uv } = isect { col.0 += uv.0 / self.spp as f32; diff --git a/src/surface/mod.rs b/src/surface/mod.rs index a0993fd..78c1596 100644 --- a/src/surface/mod.rs +++ b/src/surface/mod.rs @@ -4,7 +4,7 @@ use std::fmt::Debug; pub mod triangle_mesh; -use ray::Ray; +use ray::{Ray, AccelRay}; use math::{Point, Normal, Matrix4x4}; use boundable::Boundable; @@ -17,11 +17,15 @@ pub enum SurfaceIntersection { t: f32, pos: Point, nor: Normal, - space: Matrix4x4, + local_space: Matrix4x4, uv: (f32, f32), }, } pub trait Surface: Boundable + Debug + Sync { - fn intersect_rays(&self, rays: &mut [Ray], isects: &mut [SurfaceIntersection]); + fn intersect_rays(&self, + accel_rays: &mut [AccelRay], + wrays: &[Ray], + isects: &mut [SurfaceIntersection], + space: &[Matrix4x4]); } diff --git a/src/surface/triangle_mesh.rs b/src/surface/triangle_mesh.rs index 1fcbb18..a302b8b 100644 --- a/src/surface/triangle_mesh.rs +++ b/src/surface/triangle_mesh.rs @@ -1,8 +1,8 @@ #![allow(dead_code)] -use lerp::{lerp, lerp_slice_with}; +use lerp::{lerp, lerp_slice, lerp_slice_with}; use math::{Point, Normal, Matrix4x4}; -use ray::Ray; +use ray::{Ray, AccelRay}; use triangle; use bbox::BBox; use boundable::Boundable; @@ -59,22 +59,30 @@ impl Boundable for TriangleMesh { impl Surface for TriangleMesh { - fn intersect_rays(&self, rays: &mut [Ray], isects: &mut [SurfaceIntersection]) { - self.accel.traverse(&mut rays[..], &self.indices, |tri_i, rs| { + fn intersect_rays(&self, + accel_rays: &mut [AccelRay], + wrays: &[Ray], + isects: &mut [SurfaceIntersection], + space: &[Matrix4x4]) { + self.accel.traverse(&mut accel_rays[..], &self.indices, |tri_i, rs| { for r in rs { + let wr = &wrays[r.id as usize]; let tri = lerp_slice_with(&self.geo[*tri_i..(*tri_i + self.time_samples)], - r.time, + wr.time, |a, b, t| { (lerp(a.0, b.0, t), lerp(a.1, b.1, t), lerp(a.2, b.2, t)) }); - if let Some((t, tri_u, tri_v)) = triangle::intersect_ray(r, tri) { + let mat_space = lerp_slice(space, wr.time); + let mat_inv = mat_space.inverse(); + let tri = (tri.0 * mat_inv, tri.1 * mat_inv, tri.2 * mat_inv); + if let Some((t, tri_u, tri_v)) = triangle::intersect_ray(wr, tri) { if t < r.max_t { isects[r.id as usize] = SurfaceIntersection::Hit { t: t, - pos: r.orig + (r.dir * t), + pos: wr.orig + (wr.dir * t), nor: Normal::new(0.0, 0.0, 0.0), // TODO - space: Matrix4x4::new(), // TODO + local_space: mat_space, uv: (tri_u, tri_v), }; r.max_t = t; diff --git a/src/tracer.rs b/src/tracer.rs index 52a158a..b62edb5 100644 --- a/src/tracer.rs +++ b/src/tracer.rs @@ -4,12 +4,12 @@ use std::cell::UnsafeCell; use math::{Matrix4x4, multiply_matrix_slices}; use lerp::lerp_slice; use assembly::{Assembly, Object, InstanceType}; -use ray::Ray; +use ray::{Ray, AccelRay}; use surface::SurfaceIntersection; pub struct Tracer<'a> { root: &'a Assembly, - rays: UnsafeCell>, // Should only be used from trace(), not any other methods + rays: UnsafeCell>, // Should only be used from trace(), not any other methods xform_stack: TransformStack, isects: Vec, } @@ -30,7 +30,8 @@ impl<'a> Tracer<'a> { unsafe { (*rays_ptr).clear(); (*rays_ptr).reserve(wrays.len()); - (*rays_ptr).extend(wrays.iter()); + let mut ids = 0..(wrays.len() as u32); + (*rays_ptr).extend(wrays.iter().map(|wr| AccelRay::new(wr, ids.next().unwrap()))); } // Ready the isects @@ -55,8 +56,11 @@ impl<'a> Tracer<'a> { return &self.isects; } - fn trace_assembly<'b>(&'b mut self, assembly: &Assembly, wrays: &[Ray], rays: &mut [Ray]) { - assembly.object_accel.traverse(&mut rays[..], &assembly.instances[..], |inst, rs| { + fn trace_assembly<'b>(&'b mut self, + assembly: &Assembly, + wrays: &[Ray], + accel_rays: &mut [AccelRay]) { + assembly.object_accel.traverse(&mut accel_rays[..], &assembly.instances[..], |inst, rs| { // Transform rays if needed if let Some((xstart, xend)) = inst.transform_indices { // Push transforms to stack @@ -106,10 +110,10 @@ impl<'a> Tracer<'a> { }); } - fn trace_object<'b>(&'b mut self, obj: &Object, wrays: &[Ray], rays: &mut [Ray]) { + fn trace_object<'b>(&'b mut self, obj: &Object, wrays: &[Ray], rays: &mut [AccelRay]) { match obj { &Object::Surface(ref surface) => { - surface.intersect_rays(rays, &mut self.isects); + surface.intersect_rays(rays, wrays, &mut self.isects, self.xform_stack.top()); } } }