pbrt/src/lights/spot.rs
2026-01-22 14:18:57 +00:00

104 lines
3.4 KiB
Rust

// use crate::core::image::{Image, ImageIO, ImageMetadata};
use crate::core::light::{CreateLight, lookup_spectrum};
use crate::core::spectrum::spectrum_to_photometric;
use crate::core::texture::FloatTexture;
use crate::utils::{Arena, FileLoc, ParameterDictionary};
use shared::core::geometry::{Frame, Point3f, VectorLike};
use shared::core::light::{Light, LightBase, LightType};
use shared::core::medium::{Medium, MediumInterface};
use shared::core::shape::Shape;
use shared::core::spectrum::Spectrum;
use shared::core::texture::SpectrumType;
use shared::lights::SpotLight;
use shared::spectra::RGBColorSpace;
use shared::utils::Transform;
use shared::utils::math::radians;
use shared::{Float, PI};
use std::fmt::Error;
pub trait CreateSpotLight {
fn new(
render_from_light: Transform,
medium_interface: MediumInterface,
le: Spectrum,
scale: shared::Float,
cos_falloff_start: Float,
total_width: Float,
) -> Self;
}
impl CreateSpotLight for SpotLight {
fn new(
render_from_light: Transform,
medium_interface: MediumInterface,
le: Spectrum,
scale: shared::Float,
cos_falloff_start: Float,
total_width: Float,
) -> Self {
let base = LightBase::new(
LightType::DeltaPosition,
render_from_light,
MediumInterface::empty(),
);
let iemit = Ptr::from(&lookup_spectrum(&le));
Self {
base,
iemit,
scale,
cos_falloff_end: radians(total_width).cos(),
cos_falloff_start: radians(cos_falloff_start).cos(),
}
}
}
impl CreateLight for SpotLight {
fn create(
arena: &mut Arena,
render_from_light: Transform,
medium: Medium,
parameters: &ParameterDictionary,
_loc: &FileLoc,
_shape: &Shape,
_alpha_tex: &FloatTexture,
colorspace: Option<&RGBColorSpace>,
) -> Result<Light, Error> {
let i = parameters
.get_one_spectrum(
"I",
Some(Spectrum::Dense(colorspace.unwrap().illuminant)),
SpectrumType::Illuminant,
)
.expect("No spectrum");
let mut scale = parameters.get_one_float("scale", 1.);
let coneangle = parameters.get_one_float("coneangle", 30.);
let conedelta = parameters.get_one_float("conedelta", 5.);
let from = parameters.get_one_point3f("from", Point3f::zero());
let to = parameters.get_one_point3f("to", Point3f::new(0., 0., 1.));
let dir_to_z = Transform::from(Frame::from_z((to - from).normalize()));
let t = Transform::translate(from.into()) * dir_to_z.inverse();
let final_render = render_from_light * t;
scale /= spectrum_to_photometric(i);
let phi_v = parameters.get_one_float("power", -1.);
if phi_v > 0. {
let cos_falloff_end = radians(coneangle).cos();
let cos_falloff_start = radians(coneangle - conedelta).cos();
let k_e =
2. * PI * ((1. - cos_falloff_start) + (cos_falloff_start - cos_falloff_end) / 2.);
scale *= phi_v / k_e;
}
let specific = SpotLight::new(
final_render,
medium.into(),
i,
scale,
coneangle,
coneangle - conedelta,
);
arena.alloc(specific);
Ok(Light::Spot(specific))
}
}