349 lines
9.3 KiB
Rust
349 lines
9.3 KiB
Rust
use crate::core::color::RGB;
|
|
use crate::core::geometry::{
|
|
Bounds2f, Bounds3f, DirectionCone, Normal3f, Point2f, Point2i, Point3f, Point3fi, Ray,
|
|
Vector3f, VectorLike, cos_theta,
|
|
};
|
|
use crate::core::image::DeviceImage;
|
|
use crate::core::interaction::{
|
|
Interaction, InteractionBase, InteractionTrait, MediumInteraction, SimpleInteraction,
|
|
SurfaceInteraction,
|
|
};
|
|
use crate::core::medium::MediumInterface;
|
|
use crate::core::spectrum::{Spectrum, SpectrumTrait};
|
|
use crate::lights::*;
|
|
use crate::spectra::{
|
|
DenselySampledSpectrum, LAMBDA_MAX, LAMBDA_MIN, RGBColorSpace, RGBIlluminantSpectrum,
|
|
SampledSpectrum, SampledWavelengths,
|
|
};
|
|
use crate::utils::Transform;
|
|
use crate::utils::math::{equal_area_sphere_to_square, radians, safe_sqrt, smooth_step, square};
|
|
use crate::utils::ptr::{DevicePtr, Ptr};
|
|
use crate::utils::sampling::DevicePiecewiseConstant2D;
|
|
use crate::{Float, PI};
|
|
use bitflags::bitflags;
|
|
|
|
use enum_dispatch::enum_dispatch;
|
|
|
|
bitflags! {
|
|
#[repr(C)]
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
|
pub struct LightType: u32 {
|
|
const DeltaPosition = 1;
|
|
const DeltaDirection = 2;
|
|
const Area = 4;
|
|
const Infinite = 8;
|
|
}
|
|
}
|
|
|
|
impl LightType {
|
|
pub fn is_infinite(&self) -> bool {
|
|
self.contains(LightType::Infinite)
|
|
}
|
|
|
|
pub fn is_delta_light(&self) -> bool {
|
|
self.contains(LightType::DeltaPosition) || self.contains(LightType::DeltaDirection)
|
|
}
|
|
}
|
|
|
|
#[repr(C)]
|
|
#[derive(Debug, Copy, Clone)]
|
|
pub struct LightLeSample {
|
|
pub l: SampledSpectrum,
|
|
pub ray: Ray,
|
|
pub pdf_pos: Float,
|
|
pub pdf_dir: Float,
|
|
pub intr: Interaction,
|
|
}
|
|
|
|
#[repr(C)]
|
|
#[derive(Debug, Clone, Copy)]
|
|
pub struct LightLiSample {
|
|
pub l: SampledSpectrum,
|
|
pub wi: Vector3f,
|
|
pub pdf: Float,
|
|
pub p_light: Interaction,
|
|
}
|
|
|
|
#[cfg(not(target_os = "cuda"))]
|
|
impl LightLiSample {
|
|
pub fn new(l: SampledSpectrum, wi: Vector3f, pdf: Float, p_light: Interaction) -> Self {
|
|
Self {
|
|
l,
|
|
wi,
|
|
pdf,
|
|
p_light,
|
|
}
|
|
}
|
|
}
|
|
#[repr(C)]
|
|
#[derive(Debug, Copy, Default, Clone)]
|
|
pub struct LightSampleContext {
|
|
pub pi: Point3fi,
|
|
pub n: Normal3f,
|
|
pub ns: Normal3f,
|
|
}
|
|
|
|
impl LightSampleContext {
|
|
pub fn new(pi: Point3fi, n: Normal3f, ns: Normal3f) -> Self {
|
|
Self { pi, n, ns }
|
|
}
|
|
|
|
pub fn p(&self) -> Point3f {
|
|
self.pi.into()
|
|
}
|
|
}
|
|
|
|
impl From<&SurfaceInteraction> for LightSampleContext {
|
|
fn from(si: &SurfaceInteraction) -> Self {
|
|
Self {
|
|
pi: si.common.pi,
|
|
n: si.common.n,
|
|
ns: si.shading.n,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<&MediumInteraction> for LightSampleContext {
|
|
fn from(mi: &MediumInteraction) -> Self {
|
|
Self {
|
|
pi: mi.common.pi,
|
|
n: Normal3f::default(),
|
|
ns: Normal3f::default(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<&Interaction> for LightSampleContext {
|
|
fn from(intr: &Interaction) -> Self {
|
|
match intr {
|
|
Interaction::Surface(si) => Self {
|
|
pi: si.common.pi,
|
|
n: si.common.n,
|
|
ns: si.shading.n,
|
|
},
|
|
|
|
Interaction::Medium(mi) => Self {
|
|
pi: mi.common.pi,
|
|
n: mi.common.n,
|
|
ns: mi.common.n,
|
|
},
|
|
|
|
Interaction::Simple(sim) => Self {
|
|
pi: sim.common.pi,
|
|
n: sim.common.n,
|
|
ns: sim.common.n,
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
#[repr(C)]
|
|
#[derive(Debug, Clone, Copy)]
|
|
pub struct LightBase {
|
|
pub render_from_light: Transform,
|
|
pub light_type: LightType,
|
|
pub medium_interface: MediumInterface,
|
|
}
|
|
|
|
impl LightBase {
|
|
pub fn new(
|
|
light_type: LightType,
|
|
render_from_light: Transform,
|
|
medium_interface: MediumInterface,
|
|
) -> Self {
|
|
Self {
|
|
light_type,
|
|
render_from_light,
|
|
medium_interface,
|
|
}
|
|
}
|
|
|
|
fn l(
|
|
&self,
|
|
_p: Point3f,
|
|
_n: Normal3f,
|
|
_uv: Point2f,
|
|
_w: Vector3f,
|
|
_lambda: &SampledWavelengths,
|
|
) -> SampledSpectrum {
|
|
SampledSpectrum::default()
|
|
}
|
|
|
|
pub fn light_type(&self) -> LightType {
|
|
self.light_type
|
|
}
|
|
|
|
pub fn le(&self, _ray: &Ray, _lambda: &SampledWavelengths) -> SampledSpectrum {
|
|
SampledSpectrum::default()
|
|
}
|
|
}
|
|
|
|
#[repr(C)]
|
|
#[derive(Debug, Copy, Clone)]
|
|
pub struct LightBounds {
|
|
pub bounds: Bounds3f,
|
|
pub phi: Float,
|
|
pub w: Vector3f,
|
|
pub cos_theta_o: Float,
|
|
pub cos_theta_e: Float,
|
|
pub two_sided: bool,
|
|
}
|
|
|
|
#[cfg(not(target_os = "cuda"))]
|
|
impl LightBounds {
|
|
pub fn new(
|
|
bounds: &Bounds3f,
|
|
w: Vector3f,
|
|
phi: Float,
|
|
cos_theta_o: Float,
|
|
cos_theta_e: Float,
|
|
two_sided: bool,
|
|
) -> Self {
|
|
Self {
|
|
bounds: *bounds,
|
|
phi,
|
|
w,
|
|
cos_theta_o,
|
|
cos_theta_e,
|
|
two_sided,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl LightBounds {
|
|
pub fn centroid(&self) -> Point3f {
|
|
self.bounds.p_min + Vector3f::from(self.bounds.p_max) / 2.
|
|
}
|
|
|
|
pub fn importance(&self, p: Point3f, n: Normal3f) -> Float {
|
|
// Compute clamped squared distance to reference point
|
|
let pc = self.centroid();
|
|
let d2_raw = p.distance_squared(pc);
|
|
let d2 = d2_raw.max(self.bounds.diagonal().norm()) / 2.;
|
|
let cos_sub_clamped = |sin_theta_a: Float,
|
|
cos_theta_a: Float,
|
|
sin_theta_b: Float,
|
|
cos_theta_b: Float|
|
|
-> Float {
|
|
if cos_theta_a > cos_theta_b {
|
|
return 1.;
|
|
}
|
|
cos_theta_a * cos_theta_b + sin_theta_a * sin_theta_b
|
|
};
|
|
|
|
let sin_sub_clamped = |sin_theta_a: Float,
|
|
cos_theta_a: Float,
|
|
sin_theta_b: Float,
|
|
cos_theta_b: Float|
|
|
-> Float {
|
|
if cos_theta_a > cos_theta_b {
|
|
return 1.;
|
|
}
|
|
sin_theta_a * cos_theta_b - cos_theta_a * sin_theta_b
|
|
};
|
|
|
|
let wi = (p - pc).normalize();
|
|
let mut cos_theta_w = self.w.dot(wi);
|
|
if self.two_sided {
|
|
cos_theta_w = cos_theta_w.abs();
|
|
}
|
|
let sin_theta_w = safe_sqrt(1. - square(cos_theta_w));
|
|
let cos_theta_b = DirectionCone::bound_subtended_directions(&self.bounds, p).cos_theta;
|
|
let sin_theta_b = safe_sqrt(1. - square(cos_theta_b));
|
|
let sin_theta_o = safe_sqrt(1. - square(self.cos_theta_o));
|
|
let cos_theta_x = cos_sub_clamped(sin_theta_w, cos_theta_w, sin_theta_o, self.cos_theta_o);
|
|
let sin_theta_x = sin_sub_clamped(sin_theta_w, cos_theta_w, sin_theta_o, self.cos_theta_o);
|
|
let cos_theta_p = cos_sub_clamped(sin_theta_x, cos_theta_x, sin_theta_b, cos_theta_b);
|
|
|
|
if cos_theta_p <= self.cos_theta_e {
|
|
return 0.;
|
|
}
|
|
|
|
let mut importance = self.phi * cos_theta_p / d2;
|
|
if n != Normal3f::new(0., 0., 0.) {
|
|
let cos_theta_i = wi.abs_dot(n.into());
|
|
let sin_theta_i = safe_sqrt(1. - square(cos_theta_i));
|
|
let cos_thetap_i = cos_sub_clamped(sin_theta_i, cos_theta_i, sin_theta_b, cos_theta_b);
|
|
importance *= cos_thetap_i;
|
|
}
|
|
importance
|
|
}
|
|
|
|
pub fn union(a: &Self, b: &Self) -> Self {
|
|
if a.phi == 0. {
|
|
return a.clone();
|
|
}
|
|
if b.phi == 0. {
|
|
return b.clone();
|
|
}
|
|
|
|
let a_cone = DirectionCone::new(a.w, a.cos_theta_o);
|
|
let b_cone = DirectionCone::new(b.w, b.cos_theta_o);
|
|
let cone = DirectionCone::union(&a_cone, &b_cone);
|
|
let cos_theta_o = cone.cos_theta;
|
|
let cos_theta_e = a.cos_theta_e.min(b.cos_theta_e);
|
|
LightBounds::new(
|
|
&a.bounds.union(b.bounds),
|
|
cone.w,
|
|
a.phi + b.phi,
|
|
cos_theta_o,
|
|
cos_theta_e,
|
|
a.two_sided || b.two_sided,
|
|
)
|
|
}
|
|
}
|
|
|
|
#[enum_dispatch]
|
|
pub trait LightTrait {
|
|
fn base(&self) -> &LightBase;
|
|
|
|
fn sample_li(
|
|
&self,
|
|
ctx: &LightSampleContext,
|
|
u: Point2f,
|
|
lambda: &SampledWavelengths,
|
|
allow_incomplete_pdf: bool,
|
|
) -> Option<LightLiSample>;
|
|
|
|
fn pdf_li(&self, ctx: &LightSampleContext, wi: Vector3f, allow_incomplete_pdf: bool) -> Float;
|
|
|
|
fn l(
|
|
&self,
|
|
p: Point3f,
|
|
n: Normal3f,
|
|
uv: Point2f,
|
|
w: Vector3f,
|
|
lambda: &SampledWavelengths,
|
|
) -> SampledSpectrum;
|
|
|
|
fn le(&self, ray: &Ray, lambda: &SampledWavelengths) -> SampledSpectrum;
|
|
|
|
fn light_type(&self) -> LightType {
|
|
self.base().light_type
|
|
}
|
|
|
|
#[cfg(not(target_os = "cuda"))]
|
|
fn bounds(&self) -> Option<LightBounds>;
|
|
|
|
#[cfg(not(target_os = "cuda"))]
|
|
fn preprocess(&mut self, scene_bounds: &Bounds3f);
|
|
|
|
#[cfg(not(target_os = "cuda"))]
|
|
fn phi(&self, lambda: SampledWavelengths) -> SampledSpectrum;
|
|
}
|
|
|
|
#[enum_dispatch(LightTrait)]
|
|
#[repr(C)]
|
|
#[derive(Debug, Clone, Copy)]
|
|
#[allow(clippy::large_enum_variant)]
|
|
pub enum Light {
|
|
DiffuseArea(DiffuseAreaLight),
|
|
Distant(DistantLight),
|
|
Goniometric(GoniometricLight),
|
|
InfiniteUniform(InfiniteUniformLight),
|
|
InfiniteImage(InfiniteImageLight),
|
|
InfinitePortal(InfinitePortalLight),
|
|
Point(PointLight),
|
|
Projection(ProjectionLight),
|
|
Spot(SpotLight),
|
|
}
|