LightTree now works with lights in hierarchical instancing.
This commit is contained in:
parent
97b5ef77f8
commit
c71b00ca31
|
@ -9,6 +9,7 @@ use light_accel::{LightAccel, LightTree};
|
||||||
use light::LightSource;
|
use light::LightSource;
|
||||||
use math::{Matrix4x4, Vector};
|
use math::{Matrix4x4, Vector};
|
||||||
use surface::{Surface, SurfaceIntersection};
|
use surface::{Surface, SurfaceIntersection};
|
||||||
|
use transform_stack::TransformStack;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -32,30 +33,56 @@ pub struct Assembly {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Assembly {
|
impl Assembly {
|
||||||
// Returns (light_color, shadow_vector, selection_pdf)
|
// Returns (light_color, shadow_vector, pdf, selection_pdf)
|
||||||
pub fn sample_lights(&self,
|
pub fn sample_lights(&self,
|
||||||
|
xform_stack: &mut TransformStack,
|
||||||
n: f32,
|
n: f32,
|
||||||
uvw: (f32, f32, f32),
|
uvw: (f32, f32, f32),
|
||||||
wavelength: f32,
|
wavelength: f32,
|
||||||
time: f32,
|
time: f32,
|
||||||
intr: &SurfaceIntersection)
|
intr: &SurfaceIntersection)
|
||||||
-> Option<(SpectralSample, Vector, f32)> {
|
-> Option<(SpectralSample, Vector, f32, f32)> {
|
||||||
if let &SurfaceIntersection::Hit { pos, incoming, nor, closure, .. } = intr {
|
if let &SurfaceIntersection::Hit { pos, incoming, nor, closure, .. } = intr {
|
||||||
if let Some((light_i, sel_pdf, _)) = self.light_accel
|
let sel_xform = if xform_stack.top().len() > 0 {
|
||||||
.select(incoming, pos, nor, closure.as_surface_closure(), time, n) {
|
lerp_slice(xform_stack.top(), time)
|
||||||
|
} else {
|
||||||
|
Matrix4x4::new()
|
||||||
|
};
|
||||||
|
if let Some((light_i, sel_pdf, whittled_n)) = self.light_accel
|
||||||
|
.select(incoming * sel_xform,
|
||||||
|
pos * sel_xform,
|
||||||
|
nor * sel_xform,
|
||||||
|
closure.as_surface_closure(),
|
||||||
|
time,
|
||||||
|
n) {
|
||||||
let inst = self.light_instances[light_i];
|
let inst = self.light_instances[light_i];
|
||||||
match inst.instance_type {
|
match inst.instance_type {
|
||||||
|
|
||||||
InstanceType::Object => {
|
InstanceType::Object => {
|
||||||
match &self.objects[inst.data_index] {
|
match &self.objects[inst.data_index] {
|
||||||
&Object::Light(ref light) => {
|
&Object::Light(ref 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 {
|
||||||
lerp_slice(&self.xforms[a..b], time)
|
let pxforms = xform_stack.top();
|
||||||
|
let xform = lerp_slice(&self.xforms[a..b], time);
|
||||||
|
if pxforms.len() > 0 {
|
||||||
|
lerp_slice(pxforms, time) * xform
|
||||||
|
} else {
|
||||||
|
xform
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Matrix4x4::new()
|
let pxforms = xform_stack.top();
|
||||||
|
if pxforms.len() > 0 {
|
||||||
|
lerp_slice(pxforms, time)
|
||||||
|
} else {
|
||||||
|
Matrix4x4::new()
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Sample the light
|
||||||
let (color, shadow_vec, pdf) =
|
let (color, shadow_vec, pdf) =
|
||||||
light.sample(&xform, pos, uvw.0, uvw.1, wavelength, time);
|
light.sample(&xform, pos, uvw.0, uvw.1, wavelength, time);
|
||||||
return Some((color, shadow_vec, pdf * sel_pdf));
|
return Some((color, shadow_vec, pdf, sel_pdf));
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
|
@ -63,9 +90,23 @@ impl Assembly {
|
||||||
}
|
}
|
||||||
|
|
||||||
InstanceType::Assembly => {
|
InstanceType::Assembly => {
|
||||||
// TODO: recursive light selection inside assemblies
|
// Push the world-to-object space transforms of the assembly onto
|
||||||
unimplemented!()
|
// the transform stack.
|
||||||
|
if let Some((a, b)) = inst.transform_indices {
|
||||||
|
xform_stack.push(&self.xforms[a..b]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sample sub-assembly lights
|
||||||
|
let sample = self.assemblies[inst.data_index]
|
||||||
|
.sample_lights(xform_stack, whittled_n, uvw, wavelength, time, intr);
|
||||||
|
|
||||||
|
// Pop the assembly's transforms off the transform stack.
|
||||||
|
if let Some(_) = inst.transform_indices {
|
||||||
|
xform_stack.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return sample
|
||||||
|
return sample.map(|(ss, v, pdf, spdf)| (ss, v, pdf, spdf * sel_pdf));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -190,8 +231,7 @@ impl AssemblyBuilder {
|
||||||
self.objects.shrink_to_fit();
|
self.objects.shrink_to_fit();
|
||||||
self.assemblies.shrink_to_fit();
|
self.assemblies.shrink_to_fit();
|
||||||
|
|
||||||
// Calculate instance bounds, used for building object accel and
|
// Calculate instance bounds, used for building object accel and light accel.
|
||||||
// (TODO) light accel.
|
|
||||||
let (bis, bbs) = self.instance_bounds();
|
let (bis, bbs) = self.instance_bounds();
|
||||||
|
|
||||||
// Build object accel
|
// Build object accel
|
||||||
|
@ -199,8 +239,8 @@ impl AssemblyBuilder {
|
||||||
1,
|
1,
|
||||||
|inst| &bbs[bis[inst.id]..bis[inst.id + 1]]);
|
|inst| &bbs[bis[inst.id]..bis[inst.id + 1]]);
|
||||||
|
|
||||||
// Get list of instances that are for light sources.
|
// Get list of instances that are for light sources or assemblies that contain light
|
||||||
// TODO: include assemblies that themselves contain light sources.
|
// sources.
|
||||||
let mut light_instances: Vec<_> = self.instances
|
let mut light_instances: Vec<_> = self.instances
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|inst| {
|
.filter(|inst| {
|
||||||
|
@ -213,7 +253,9 @@ impl AssemblyBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => false,
|
InstanceType::Assembly => {
|
||||||
|
self.assemblies[inst.data_index].light_accel.approximate_energy() > 0.0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.map(|&a| a)
|
.map(|&a| a)
|
||||||
|
@ -231,8 +273,9 @@ impl AssemblyBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: handle assemblies.
|
InstanceType::Assembly => {
|
||||||
_ => 0.0,
|
self.assemblies[inst.data_index].light_accel.approximate_energy()
|
||||||
|
}
|
||||||
};
|
};
|
||||||
(bounds, energy)
|
(bounds, energy)
|
||||||
});
|
});
|
||||||
|
|
56
src/bvh.rs
56
src/bvh.rs
|
@ -1,15 +1,12 @@
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
use std;
|
use algorithm::{partition, merge_slices_append};
|
||||||
use std::cmp::Ordering;
|
|
||||||
|
|
||||||
use algorithm::{partition, quick_select, merge_slices_append};
|
|
||||||
use bbox::BBox;
|
use bbox::BBox;
|
||||||
use boundable::Boundable;
|
use boundable::Boundable;
|
||||||
use lerp::lerp_slice;
|
use lerp::lerp_slice;
|
||||||
use math::log2_64;
|
use math::log2_64;
|
||||||
use ray::AccelRay;
|
use ray::AccelRay;
|
||||||
use sah::sah_split;
|
use objects_split::{sah_split, median_split};
|
||||||
|
|
||||||
|
|
||||||
const BVH_MAX_DEPTH: usize = 64;
|
const BVH_MAX_DEPTH: usize = 64;
|
||||||
|
@ -125,15 +122,6 @@ impl BVH {
|
||||||
split_axis: 0,
|
split_axis: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Get combined object bounds
|
|
||||||
let bounds = {
|
|
||||||
let mut bb = BBox::new();
|
|
||||||
for obj in &objects[..] {
|
|
||||||
bb |= lerp_slice(bounder(obj), 0.5);
|
|
||||||
}
|
|
||||||
bb
|
|
||||||
};
|
|
||||||
|
|
||||||
// Partition objects.
|
// Partition objects.
|
||||||
// If we're too near the max depth, we do balanced building to
|
// If we're too near the max depth, we do balanced building to
|
||||||
// avoid exceeding max depth.
|
// avoid exceeding max depth.
|
||||||
|
@ -144,45 +132,7 @@ impl BVH {
|
||||||
sah_split(objects, &bounder)
|
sah_split(objects, &bounder)
|
||||||
} else {
|
} else {
|
||||||
// Balanced splitting, when we don't have room to play
|
// Balanced splitting, when we don't have room to play
|
||||||
let split_axis = {
|
median_split(objects, &bounder)
|
||||||
let mut axis = 0;
|
|
||||||
let mut largest = std::f32::NEG_INFINITY;
|
|
||||||
for i in 0..3 {
|
|
||||||
let extent = bounds.max.get_n(i) - bounds.min.get_n(i);
|
|
||||||
if extent > largest {
|
|
||||||
largest = extent;
|
|
||||||
axis = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
axis
|
|
||||||
};
|
|
||||||
|
|
||||||
let place = {
|
|
||||||
let place = objects.len() / 2;
|
|
||||||
if place > 0 {
|
|
||||||
place
|
|
||||||
} else {
|
|
||||||
1
|
|
||||||
}
|
|
||||||
};
|
|
||||||
quick_select(objects, place, |a, b| {
|
|
||||||
let tb_a = lerp_slice(bounder(a), 0.5);
|
|
||||||
let tb_b = lerp_slice(bounder(b), 0.5);
|
|
||||||
let centroid_a = (tb_a.min.get_n(split_axis) + tb_a.max.get_n(split_axis)) *
|
|
||||||
0.5;
|
|
||||||
let centroid_b = (tb_b.min.get_n(split_axis) + tb_b.max.get_n(split_axis)) *
|
|
||||||
0.5;
|
|
||||||
|
|
||||||
if centroid_a < centroid_b {
|
|
||||||
Ordering::Less
|
|
||||||
} else if centroid_a == centroid_b {
|
|
||||||
Ordering::Equal
|
|
||||||
} else {
|
|
||||||
Ordering::Greater
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
(place, split_axis)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create child nodes
|
// Create child nodes
|
||||||
|
|
|
@ -44,8 +44,10 @@ impl LightSource for SphereLight {
|
||||||
wavelength: f32,
|
wavelength: f32,
|
||||||
time: f32)
|
time: f32)
|
||||||
-> (SpectralSample, Vector, f32) {
|
-> (SpectralSample, Vector, f32) {
|
||||||
// TODO: use transform space correctly
|
// TODO: track fp error due to transforms
|
||||||
let pos = Point::new(0.0, 0.0, 0.0) * space.inverse();
|
let arr = arr * *space;
|
||||||
|
let pos = Point::new(0.0, 0.0, 0.0);
|
||||||
|
|
||||||
// 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 col = lerp_slice(&self.colors, time);
|
let col = lerp_slice(&self.colors, time);
|
||||||
|
@ -93,13 +95,14 @@ impl LightSource for SphereLight {
|
||||||
(d - (cos_a * radius)) as f32);
|
(d - (cos_a * radius)) as f32);
|
||||||
|
|
||||||
// Calculate the final values and return everything.
|
// Calculate the final values and return everything.
|
||||||
let shadow_vec = (x * sample.x()) + (y * sample.y()) + (z * sample.z());
|
let shadow_vec = ((x * sample.x()) + (y * sample.y()) + (z * sample.z())) *
|
||||||
|
space.inverse();
|
||||||
let pdf = uniform_sample_cone_pdf(cos_theta_max);
|
let pdf = uniform_sample_cone_pdf(cos_theta_max);
|
||||||
let spectral_sample = (col * surface_area_inv as f32).to_spectral_sample(wavelength);
|
let spectral_sample = (col * surface_area_inv as f32).to_spectral_sample(wavelength);
|
||||||
return (spectral_sample, shadow_vec, pdf as f32);
|
return (spectral_sample, shadow_vec, pdf as f32);
|
||||||
} else {
|
} else {
|
||||||
// If we're inside the sphere, there's light from every direction.
|
// If we're inside the sphere, there's light from every direction.
|
||||||
let shadow_vec = uniform_sample_sphere(u, v);
|
let shadow_vec = uniform_sample_sphere(u, v) * space.inverse();
|
||||||
let pdf = 1.0 / (4.0 * PI_64);
|
let pdf = 1.0 / (4.0 * PI_64);
|
||||||
let spectral_sample = (col * surface_area_inv as f32).to_spectral_sample(wavelength);
|
let spectral_sample = (col * surface_area_inv as f32).to_spectral_sample(wavelength);
|
||||||
return (spectral_sample, shadow_vec, pdf as f32);
|
return (spectral_sample, shadow_vec, pdf as f32);
|
||||||
|
@ -118,8 +121,8 @@ impl LightSource for SphereLight {
|
||||||
// We're not using these, silence warnings
|
// We're not using these, silence warnings
|
||||||
let _ = (sample_dir, sample_u, sample_v, wavelength);
|
let _ = (sample_dir, sample_u, sample_v, wavelength);
|
||||||
|
|
||||||
// TODO: use transform space correctly
|
let arr = arr * *space;
|
||||||
let pos = Point::new(0.0, 0.0, 0.0) * space.inverse();
|
let pos = Point::new(0.0, 0.0, 0.0);
|
||||||
let radius: f64 = lerp_slice(&self.radii, time) as f64;
|
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 d2: f64 = (pos - arr).length2() as f64; // Distance from center of sphere squared
|
||||||
|
|
|
@ -2,7 +2,7 @@ use algorithm::merge_slices_append;
|
||||||
use bbox::BBox;
|
use bbox::BBox;
|
||||||
use lerp::lerp_slice;
|
use lerp::lerp_slice;
|
||||||
use math::{Vector, Point, Normal};
|
use math::{Vector, Point, Normal};
|
||||||
use sah::sah_split;
|
use objects_split::sah_split;
|
||||||
use shading::surface_closure::SurfaceClosure;
|
use shading::surface_closure::SurfaceClosure;
|
||||||
|
|
||||||
use super::LightAccel;
|
use super::LightAccel;
|
||||||
|
@ -187,4 +187,12 @@ impl LightAccel for LightTree {
|
||||||
// Found our light!
|
// Found our light!
|
||||||
Some((self.nodes[node_index].child_index, tot_prob, n))
|
Some((self.nodes[node_index].child_index, tot_prob, n))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn approximate_energy(&self) -> f32 {
|
||||||
|
if self.nodes.len() > 0 {
|
||||||
|
self.nodes[0].energy
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,11 +17,14 @@ pub trait LightAccel {
|
||||||
time: f32,
|
time: f32,
|
||||||
n: f32)
|
n: f32)
|
||||||
-> Option<(usize, f32, f32)>;
|
-> Option<(usize, f32, f32)>;
|
||||||
|
|
||||||
|
fn approximate_energy(&self) -> f32;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct LightArray {
|
pub struct LightArray {
|
||||||
indices: Vec<usize>,
|
indices: Vec<usize>,
|
||||||
|
aprx_energy: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LightArray {
|
impl LightArray {
|
||||||
|
@ -30,15 +33,20 @@ impl LightArray {
|
||||||
where F: 'a + Fn(&T) -> Option<(&'a [BBox], f32)>
|
where F: 'a + Fn(&T) -> Option<(&'a [BBox], f32)>
|
||||||
{
|
{
|
||||||
let mut indices = Vec::new();
|
let mut indices = Vec::new();
|
||||||
|
let mut aprx_energy = 0.0;
|
||||||
for (i, thing) in things.iter().enumerate() {
|
for (i, thing) in things.iter().enumerate() {
|
||||||
if let Some((_, power)) = q(thing) {
|
if let Some((_, power)) = q(thing) {
|
||||||
if power > 0.0 {
|
if power > 0.0 {
|
||||||
indices.push(i);
|
indices.push(i);
|
||||||
|
aprx_energy += power;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LightArray { indices: indices }
|
LightArray {
|
||||||
|
indices: indices,
|
||||||
|
aprx_energy: aprx_energy,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,4 +79,8 @@ impl LightAccel for LightArray {
|
||||||
|
|
||||||
Some((i, pdf, whittled_n))
|
Some((i, pdf, whittled_n))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn approximate_energy(&self) -> f32 {
|
||||||
|
self.aprx_energy
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,10 +28,10 @@ mod lerp;
|
||||||
mod light_accel;
|
mod light_accel;
|
||||||
mod light;
|
mod light;
|
||||||
mod math;
|
mod math;
|
||||||
|
mod objects_split;
|
||||||
mod parse;
|
mod parse;
|
||||||
mod ray;
|
mod ray;
|
||||||
mod renderer;
|
mod renderer;
|
||||||
mod sah;
|
|
||||||
mod sampling;
|
mod sampling;
|
||||||
mod scene;
|
mod scene;
|
||||||
mod shading;
|
mod shading;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use std;
|
use std;
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
use algorithm::partition;
|
use algorithm::{partition, quick_select};
|
||||||
use bbox::BBox;
|
use bbox::BBox;
|
||||||
use lerp::lerp_slice;
|
use lerp::lerp_slice;
|
||||||
|
|
||||||
|
@ -95,3 +96,105 @@ pub fn sah_split<'a, T, F>(objects: &mut [T], bounder: &F) -> (usize, usize)
|
||||||
|
|
||||||
(split_i, split_axis)
|
(split_i, split_axis)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Takes a slice of boundable objects and partitions them based on the bounds mean heuristic.
|
||||||
|
///
|
||||||
|
/// Returns the index of the partition boundary and the axis that it split on
|
||||||
|
/// (0 = x, 1 = y, 2 = z).
|
||||||
|
pub fn bounds_mean_split<'a, T, F>(objects: &mut [T], bounder: &F) -> (usize, usize)
|
||||||
|
where F: Fn(&T) -> &'a [BBox]
|
||||||
|
{
|
||||||
|
// Get combined object bounds
|
||||||
|
let bounds = {
|
||||||
|
let mut bb = BBox::new();
|
||||||
|
for obj in &objects[..] {
|
||||||
|
bb |= lerp_slice(bounder(obj), 0.5);
|
||||||
|
}
|
||||||
|
bb
|
||||||
|
};
|
||||||
|
|
||||||
|
let split_axis = {
|
||||||
|
let mut axis = 0;
|
||||||
|
let mut largest = std::f32::NEG_INFINITY;
|
||||||
|
for i in 0..3 {
|
||||||
|
let extent = bounds.max.get_n(i) - bounds.min.get_n(i);
|
||||||
|
if extent > largest {
|
||||||
|
largest = extent;
|
||||||
|
axis = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
axis
|
||||||
|
};
|
||||||
|
|
||||||
|
let div = (bounds.min.get_n(split_axis) + bounds.max.get_n(split_axis)) * 0.5;
|
||||||
|
|
||||||
|
// Partition
|
||||||
|
let mut split_i = partition(&mut objects[..], |obj| {
|
||||||
|
let tb = lerp_slice(bounder(obj), 0.5);
|
||||||
|
let centroid = (tb.min.get_n(split_axis) + tb.max.get_n(split_axis)) * 0.5;
|
||||||
|
centroid < div
|
||||||
|
});
|
||||||
|
if split_i < 1 {
|
||||||
|
split_i = 1;
|
||||||
|
} else if split_i >= objects.len() {
|
||||||
|
split_i = objects.len() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
(split_i, split_axis)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Takes a slice of boundable objects and partitions them based on the median heuristic.
|
||||||
|
///
|
||||||
|
/// Returns the index of the partition boundary and the axis that it split on
|
||||||
|
/// (0 = x, 1 = y, 2 = z).
|
||||||
|
pub fn median_split<'a, T, F>(objects: &mut [T], bounder: &F) -> (usize, usize)
|
||||||
|
where F: Fn(&T) -> &'a [BBox]
|
||||||
|
{
|
||||||
|
// Get combined object bounds
|
||||||
|
let bounds = {
|
||||||
|
let mut bb = BBox::new();
|
||||||
|
for obj in &objects[..] {
|
||||||
|
bb |= lerp_slice(bounder(obj), 0.5);
|
||||||
|
}
|
||||||
|
bb
|
||||||
|
};
|
||||||
|
|
||||||
|
let split_axis = {
|
||||||
|
let mut axis = 0;
|
||||||
|
let mut largest = std::f32::NEG_INFINITY;
|
||||||
|
for i in 0..3 {
|
||||||
|
let extent = bounds.max.get_n(i) - bounds.min.get_n(i);
|
||||||
|
if extent > largest {
|
||||||
|
largest = extent;
|
||||||
|
axis = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
axis
|
||||||
|
};
|
||||||
|
|
||||||
|
let place = {
|
||||||
|
let place = objects.len() / 2;
|
||||||
|
if place > 0 {
|
||||||
|
place
|
||||||
|
} else {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
};
|
||||||
|
quick_select(objects, place, |a, b| {
|
||||||
|
let tb_a = lerp_slice(bounder(a), 0.5);
|
||||||
|
let tb_b = lerp_slice(bounder(b), 0.5);
|
||||||
|
let centroid_a = (tb_a.min.get_n(split_axis) + tb_a.max.get_n(split_axis)) * 0.5;
|
||||||
|
let centroid_b = (tb_b.min.get_n(split_axis) + tb_b.max.get_n(split_axis)) * 0.5;
|
||||||
|
|
||||||
|
if centroid_a < centroid_b {
|
||||||
|
Ordering::Less
|
||||||
|
} else if centroid_a == centroid_b {
|
||||||
|
Ordering::Equal
|
||||||
|
} else {
|
||||||
|
Ordering::Greater
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
(place, split_axis)
|
||||||
|
}
|
|
@ -18,6 +18,7 @@ use ray::Ray;
|
||||||
use scene::Scene;
|
use scene::Scene;
|
||||||
use surface;
|
use surface;
|
||||||
use tracer::Tracer;
|
use tracer::Tracer;
|
||||||
|
use transform_stack::TransformStack;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -67,6 +68,7 @@ impl Renderer {
|
||||||
let mut paths = Vec::new();
|
let mut paths = Vec::new();
|
||||||
let mut rays = Vec::new();
|
let mut rays = Vec::new();
|
||||||
let mut tracer = Tracer::from_assembly(&self.scene.root);
|
let mut tracer = Tracer::from_assembly(&self.scene.root);
|
||||||
|
let mut xform_stack = TransformStack::new();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
paths.clear();
|
paths.clear();
|
||||||
|
@ -130,7 +132,7 @@ impl Renderer {
|
||||||
// Determine next rays to shoot based on result
|
// Determine next rays to shoot based on result
|
||||||
pi =
|
pi =
|
||||||
partition_pair(&mut paths[..pi], &mut rays[..pi], |i, path, ray| {
|
partition_pair(&mut paths[..pi], &mut rays[..pi], |i, path, ray| {
|
||||||
path.next(&self.scene, &isects[i], &mut *ray)
|
path.next(&mut xform_stack, &self.scene, &isects[i], &mut *ray)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -274,7 +276,12 @@ impl LightPath {
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next(&mut self, scene: &Scene, isect: &surface::SurfaceIntersection, ray: &mut Ray) -> bool {
|
fn next(&mut self,
|
||||||
|
xform_stack: &mut TransformStack,
|
||||||
|
scene: &Scene,
|
||||||
|
isect: &surface::SurfaceIntersection,
|
||||||
|
ray: &mut Ray)
|
||||||
|
-> bool {
|
||||||
self.round += 1;
|
self.round += 1;
|
||||||
|
|
||||||
// Result of shading ray, prepare light ray
|
// Result of shading ray, prepare light ray
|
||||||
|
@ -291,14 +298,20 @@ impl LightPath {
|
||||||
// Prepare light ray
|
// Prepare light ray
|
||||||
let light_n = self.next_lds_samp();
|
let light_n = self.next_lds_samp();
|
||||||
let light_uvw = (self.next_lds_samp(), self.next_lds_samp(), self.next_lds_samp());
|
let light_uvw = (self.next_lds_samp(), self.next_lds_samp(), self.next_lds_samp());
|
||||||
if let Some((light_color, shadow_vec, light_pdf)) = scene.root
|
xform_stack.clear();
|
||||||
.sample_lights(light_n, light_uvw, self.wavelength, self.time, isect) {
|
if let Some((light_color, shadow_vec, light_pdf, light_sel_pdf)) = scene.root
|
||||||
|
.sample_lights(xform_stack,
|
||||||
|
light_n,
|
||||||
|
light_uvw,
|
||||||
|
self.wavelength,
|
||||||
|
self.time,
|
||||||
|
isect) {
|
||||||
// Calculate and store the light that will be contributed
|
// Calculate and store the light that will be contributed
|
||||||
// to the film plane if the light is not in shadow.
|
// to the film plane if the light is not in shadow.
|
||||||
self.pending_color_addition = {
|
self.pending_color_addition = {
|
||||||
let material = closure.as_surface_closure();
|
let material = closure.as_surface_closure();
|
||||||
let la = material.evaluate(ray.dir, shadow_vec, nor, self.wavelength);
|
let la = material.evaluate(ray.dir, shadow_vec, nor, self.wavelength);
|
||||||
light_color * la * self.light_attenuation / light_pdf
|
light_color * la * self.light_attenuation / (light_pdf * light_sel_pdf)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Calculate the shadow ray for testing if the light is
|
// Calculate the shadow ray for testing if the light is
|
||||||
|
|
|
@ -323,7 +323,7 @@ impl SurfaceClosure for LambertClosure {
|
||||||
}
|
}
|
||||||
.into_vector();
|
.into_vector();
|
||||||
|
|
||||||
let cos_nv = dot(nn, v);
|
let cos_nv = dot(nn, v).max(-1.0).min(1.0);
|
||||||
|
|
||||||
return sphere_lambert(cos_nv, cos_theta);
|
return sphere_lambert(cos_nv, cos_theta);
|
||||||
}
|
}
|
||||||
|
|
2
todo.txt
2
todo.txt
|
@ -1,6 +1,6 @@
|
||||||
//- Implement support for multiple light sources in a scene.
|
//- Implement support for multiple light sources in a scene.
|
||||||
//- Implement light tree.
|
//- Implement light tree.
|
||||||
- Implement proper light source selection for hierarchical instancing.
|
//- Implement proper light source selection for hierarchical instancing.
|
||||||
- Implement shader parsing, and use in rendering.
|
- Implement shader parsing, and use in rendering.
|
||||||
- Implement floating point accuracy stuff.
|
- Implement floating point accuracy stuff.
|
||||||
- Add gui display of rendering in progress.
|
- Add gui display of rendering in progress.
|
||||||
|
|
Loading…
Reference in New Issue
Block a user