Got DistantDiscLights working! Yay!
This involved fixing a bug in PsychoBlend. It was including translation in how it was transforming the sun lights' direction vector.
This commit is contained in:
parent
3cbb816d4b
commit
d504ca5e6a
|
@ -447,7 +447,7 @@ class PsychoExporter:
|
||||||
time_rad = []
|
time_rad = []
|
||||||
for i in range(self.time_samples):
|
for i in range(self.time_samples):
|
||||||
self.set_frame(self.fr, self.shutter_start + (self.shutter_diff*i))
|
self.set_frame(self.fr, self.shutter_start + (self.shutter_diff*i))
|
||||||
time_dir += [tuple(ob.matrix_world * Vector((0, 0, -1)))]
|
time_dir += [tuple(ob.matrix_world.to_3x3() * Vector((0, 0, -1)))]
|
||||||
time_col += [ob.data.color * ob.data.energy]
|
time_col += [ob.data.color * ob.data.energy]
|
||||||
time_rad += [ob.data.shadow_soft_size]
|
time_rad += [ob.data.shadow_soft_size]
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,31 @@ use hash::hash_u64;
|
||||||
use lerp::{Lerp, lerp_slice};
|
use lerp::{Lerp, lerp_slice};
|
||||||
|
|
||||||
|
|
||||||
|
/// Selects an item from a slice based on a weighting function and a
|
||||||
|
/// number (n) between 0.0 and 1.0. Returns the index of the selected
|
||||||
|
/// item and the probability that it would have been selected with a
|
||||||
|
/// random n.
|
||||||
|
pub fn weighted_choice<T, F>(slc: &[T], n: f32, weight: F) -> (usize, f32)
|
||||||
|
where F: Fn(&T) -> f32
|
||||||
|
{
|
||||||
|
assert!(slc.len() > 0);
|
||||||
|
|
||||||
|
let total_weight = slc.iter().fold(0.0, |sum, v| sum + weight(v));
|
||||||
|
let n = n * total_weight;
|
||||||
|
|
||||||
|
let mut x = 0.0;
|
||||||
|
for (i, v) in slc.iter().enumerate() {
|
||||||
|
let w = weight(v);
|
||||||
|
x += w;
|
||||||
|
if x > n || i == slc.len() {
|
||||||
|
return (i, w / total_weight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Partitions a slice in-place with the given unary predicate, returning
|
/// Partitions a slice in-place with the given unary predicate, returning
|
||||||
/// the index of the first element for which the predicate evaluates
|
/// the index of the first element for which the predicate evaluates
|
||||||
/// false.
|
/// false.
|
||||||
|
|
|
@ -36,19 +36,17 @@ impl WorldLightSource for DistantDiskLight {
|
||||||
|
|
||||||
// Create a coordinate system from the vector pointing at the center of
|
// Create a coordinate system from the vector pointing at the center of
|
||||||
// of the light.
|
// of the light.
|
||||||
let (z, x, y) = coordinate_system_from_vector(-direction);
|
let (z, x, y) = coordinate_system_from_vector(-direction.normalized());
|
||||||
let (x, y, z) = (x.normalized(), y.normalized(), z.normalized());
|
|
||||||
|
|
||||||
// Calculate the radius in terms of cosine
|
|
||||||
let cos_theta_max: f64 = radius.cos();
|
|
||||||
|
|
||||||
// Sample the cone subtended by the light.
|
// Sample the cone subtended by the light.
|
||||||
|
let cos_theta_max: f64 = radius.cos();
|
||||||
let sample = uniform_sample_cone(u, v, cos_theta_max).normalized();
|
let sample = uniform_sample_cone(u, v, cos_theta_max).normalized();
|
||||||
|
|
||||||
// Calculate the final values and return everything.
|
// Calculate the final values and return everything.
|
||||||
|
let spectral_sample = (col * solid_angle_inv as f32).to_spectral_sample(wavelength);
|
||||||
let shadow_vec = (x * sample.x()) + (y * sample.y()) + (z * sample.z());
|
let shadow_vec = (x * sample.x()) + (y * sample.y()) + (z * sample.z());
|
||||||
let pdf = uniform_sample_cone_pdf(cos_theta_max);
|
let pdf = uniform_sample_cone_pdf(cos_theta_max);
|
||||||
let spectral_sample = (col * solid_angle_inv as f32).to_spectral_sample(wavelength);
|
|
||||||
return (spectral_sample, shadow_vec, pdf as f32);
|
return (spectral_sample, shadow_vec, pdf as f32);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -114,6 +114,10 @@ pub fn log2_64(value: u64) -> u64 {
|
||||||
|
|
||||||
|
|
||||||
/// Creates a coordinate system from a single vector.
|
/// Creates a coordinate system from a single vector.
|
||||||
|
///
|
||||||
|
/// The input vector, v, becomes the first vector of the
|
||||||
|
/// returned tuple, with the other two vectors in the returned
|
||||||
|
/// tuple defining the orthoganal axes.
|
||||||
pub fn coordinate_system_from_vector(v: Vector) -> (Vector, Vector, Vector) {
|
pub fn coordinate_system_from_vector(v: Vector) -> (Vector, Vector, Vector) {
|
||||||
let v2 = if v.x().abs() > v.y().abs() {
|
let v2 = if v.x().abs() > v.y().abs() {
|
||||||
let invlen = 1.0 / ((v.x() * v.x()) + (v.z() * v.z())).sqrt();
|
let invlen = 1.0 / ((v.x() * v.x()) + (v.z() * v.z())).sqrt();
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use std;
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
use std::cmp::min;
|
use std::cmp::min;
|
||||||
|
@ -298,9 +299,8 @@ impl LightPath {
|
||||||
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());
|
||||||
xform_stack.clear();
|
xform_stack.clear();
|
||||||
if let Some((light_color, shadow_vec, light_pdf, light_sel_pdf)) =
|
if let Some((light_color, shadow_vec, light_pdf, light_sel_pdf, is_infinite)) =
|
||||||
scene.root
|
scene.sample_lights(xform_stack,
|
||||||
.sample_lights(xform_stack,
|
|
||||||
light_n,
|
light_n,
|
||||||
light_uvw,
|
light_uvw,
|
||||||
self.wavelength,
|
self.wavelength,
|
||||||
|
@ -323,6 +323,11 @@ impl LightPath {
|
||||||
self.time,
|
self.time,
|
||||||
true);
|
true);
|
||||||
|
|
||||||
|
// For distant lights
|
||||||
|
if is_infinite {
|
||||||
|
ray.max_t = std::f32::INFINITY;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -58,6 +58,12 @@ pub fn uniform_sample_sphere(u: f32, v: f32) -> Vector {
|
||||||
Vector::new(x, y, z)
|
Vector::new(x, y, z)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Samples a solid angle defined by a cone originating from (0,0,0)
|
||||||
|
/// and pointing down the positive z-axis.
|
||||||
|
///
|
||||||
|
/// u, v: sampling variables, should each be in the interval [0,1]
|
||||||
|
/// cos_theta_max: cosine of the max angle from the z-axis, defining
|
||||||
|
/// the outer extent of the cone.
|
||||||
pub fn uniform_sample_cone(u: f32, v: f32, cos_theta_max: f64) -> Vector {
|
pub fn uniform_sample_cone(u: f32, v: f32, cos_theta_max: f64) -> Vector {
|
||||||
let cos_theta = (1.0 - u as f64) + (u as f64 * cos_theta_max);
|
let cos_theta = (1.0 - u as f64) + (u as f64 * cos_theta_max);
|
||||||
let sin_theta = (1.0 - (cos_theta * cos_theta)).sqrt();
|
let sin_theta = (1.0 - (cos_theta * cos_theta)).sqrt();
|
||||||
|
|
65
src/scene.rs
65
src/scene.rs
|
@ -1,6 +1,12 @@
|
||||||
use assembly::Assembly;
|
use assembly::Assembly;
|
||||||
use camera::Camera;
|
use camera::Camera;
|
||||||
use world::World;
|
use world::World;
|
||||||
|
use algorithm::weighted_choice;
|
||||||
|
use transform_stack::TransformStack;
|
||||||
|
use color::SpectralSample;
|
||||||
|
use surface::SurfaceIntersection;
|
||||||
|
use math::Vector;
|
||||||
|
use light_accel::LightAccel;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -11,3 +17,62 @@ pub struct Scene {
|
||||||
pub world: World,
|
pub world: World,
|
||||||
pub root: Assembly,
|
pub root: Assembly,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Scene {
|
||||||
|
pub fn sample_lights(&self,
|
||||||
|
xform_stack: &mut TransformStack,
|
||||||
|
n: f32,
|
||||||
|
uvw: (f32, f32, f32),
|
||||||
|
wavelength: f32,
|
||||||
|
time: f32,
|
||||||
|
intr: &SurfaceIntersection)
|
||||||
|
-> Option<(SpectralSample, Vector, f32, f32, bool)> {
|
||||||
|
// TODO: this just selects between world lights and local lights
|
||||||
|
// with a 50/50 chance. We should do something more sophisticated
|
||||||
|
// than this, accounting for the estimated impact of the lights
|
||||||
|
// on the point being lit.
|
||||||
|
|
||||||
|
// Calculate relative probabilities of traversing into world lights
|
||||||
|
// or local lights.
|
||||||
|
let wl_energy = if self.world
|
||||||
|
.lights
|
||||||
|
.iter()
|
||||||
|
.fold(0.0, |energy, light| energy + light.approximate_energy()) <=
|
||||||
|
0.0 {
|
||||||
|
0.0
|
||||||
|
} else {
|
||||||
|
1.0
|
||||||
|
};
|
||||||
|
let ll_energy = if self.root.light_accel.approximate_energy() <= 0.0 {
|
||||||
|
0.0
|
||||||
|
} else {
|
||||||
|
1.0
|
||||||
|
};
|
||||||
|
let tot_energy = wl_energy + ll_energy;
|
||||||
|
|
||||||
|
// Decide either world or local lights, and select and sample a light.
|
||||||
|
if tot_energy <= 0.0 {
|
||||||
|
return None;
|
||||||
|
} else {
|
||||||
|
let wl_prob = wl_energy / tot_energy;
|
||||||
|
|
||||||
|
if n < wl_prob {
|
||||||
|
// World lights
|
||||||
|
let n = n / wl_prob;
|
||||||
|
let (i, p) = weighted_choice(&self.world.lights, n, |l| l.approximate_energy());
|
||||||
|
let (ss, sv, pdf) = self.world.lights[i].sample(uvw.0, uvw.1, wavelength, time);
|
||||||
|
return Some((ss, sv, pdf, p * wl_prob, true));
|
||||||
|
} else {
|
||||||
|
// Local lights
|
||||||
|
let n = (n - wl_prob) / (1.0 - wl_prob);
|
||||||
|
|
||||||
|
if let Some((ss, sv, pdf, spdf)) =
|
||||||
|
self.root.sample_lights(xform_stack, n, uvw, wavelength, time, intr) {
|
||||||
|
return Some((ss, sv, pdf, spdf * (1.0 - wl_prob), false));
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user