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.
This commit is contained in:
Nathan Vegdahl 2016-06-09 01:04:12 -07:00
parent e2954ba5b0
commit 18245b725c
8 changed files with 73 additions and 38 deletions

View File

@ -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;

View File

@ -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<T, F>(&self, rays: &mut [Ray], objects: &[T], mut obj_ray_test: F)
where F: FnMut(&T, &mut [Ray])
pub fn traverse<T, F>(&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] {

View File

@ -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::<Ray>());
println!("Ray size: {} bytes", mem::size_of::<Ray>());
println!("AccelRay size: {} bytes", mem::size_of::<AccelRay>());
// Iterate through scenes and render them
if let DataTree::Internal { ref children, .. } = dt {

View File

@ -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 };
}
}

View File

@ -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;

View File

@ -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]);
}

View File

@ -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;

View File

@ -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<Vec<Ray>>, // Should only be used from trace(), not any other methods
rays: UnsafeCell<Vec<AccelRay>>, // Should only be used from trace(), not any other methods
xform_stack: TransformStack,
isects: Vec<SurfaceIntersection>,
}
@ -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());
}
}
}