psychopath/src/light/distant_disk_light.rs
Nathan Vegdahl 462977bd4d WIP: multiple importance sampling.
Reorganized light and surface traits so that light sources are
surfaces as well, which will let them slide easily into
intersection tests with the rest of the scene geometry.
2017-08-16 18:17:50 -07:00

97 lines
3.0 KiB
Rust

use std::f64::consts::PI as PI_64;
use mem_arena::MemArena;
use color::{XYZ, SpectralSample, Color};
use lerp::lerp_slice;
use math::{Vector, coordinate_system_from_vector};
use sampling::{uniform_sample_cone, uniform_sample_cone_pdf};
use super::WorldLightSource;
// TODO: handle case where radius = 0.0.
#[derive(Copy, Clone, Debug)]
pub struct DistantDiskLight<'a> {
radii: &'a [f32],
directions: &'a [Vector],
colors: &'a [XYZ],
}
impl<'a> DistantDiskLight<'a> {
pub fn new(
arena: &'a MemArena,
radii: Vec<f32>,
directions: Vec<Vector>,
colors: Vec<XYZ>,
) -> DistantDiskLight<'a> {
DistantDiskLight {
radii: arena.copy_slice(&radii),
directions: arena.copy_slice(&directions),
colors: arena.copy_slice(&colors),
}
}
// fn sample_pdf(&self, sample_dir: Vector, wavelength: f32, time: f32) -> f32 {
// // We're not using these, silence warnings
// let _ = (sample_dir, wavelength);
// let radius: f64 = lerp_slice(self.radii, time) as f64;
// uniform_sample_cone_pdf(radius.cos()) as f32
// }
// fn outgoing(&self, dir: Vector, wavelength: f32, time: f32) -> SpectralSample {
// // We're not using this, silence warning
// let _ = dir;
// let radius = lerp_slice(self.radii, time) as f64;
// let col = lerp_slice(self.colors, time);
// let solid_angle = 2.0 * PI_64 * (1.0 - radius.cos());
// (col / solid_angle as f32).to_spectral_sample(wavelength)
// }
}
impl<'a> WorldLightSource for DistantDiskLight<'a> {
fn sample_from_point(
&self,
u: f32,
v: f32,
wavelength: f32,
time: f32,
) -> (SpectralSample, Vector, f32) {
// Calculate time interpolated values
let radius: f64 = lerp_slice(self.radii, time) as f64;
let direction = lerp_slice(self.directions, time);
let col = lerp_slice(self.colors, time);
let solid_angle_inv = 1.0 / (2.0 * PI_64 * (1.0 - radius.cos()));
// Create a coordinate system from the vector pointing at the center of
// of the light.
let (z, x, y) = coordinate_system_from_vector(-direction.normalized());
// 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();
// 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 pdf = uniform_sample_cone_pdf(cos_theta_max);
(spectral_sample, shadow_vec, pdf as f32)
}
fn is_delta(&self) -> bool {
false
}
fn approximate_energy(&self) -> f32 {
let color: XYZ = self.colors.iter().fold(
XYZ::new(0.0, 0.0, 0.0),
|a, &b| a + b,
) / self.colors.len() as f32;
color.y
}
}