pbrt/src/lights/spot.rs

108 lines
3.4 KiB
Rust

use crate::core::light::lookup_spectrum;
use crate::core::spectrum::spectrum_to_photometric;
use crate::core::texture::FloatTexture;
use crate::{Arena, FileLoc, ParameterDictionary};
use anyhow::Result;
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::math::radians;
use shared::{Float, Ptr, Transform, PI};
trait CreateSpotLight {
fn new(
render_from_light: Transform,
_medium_interface: MediumInterface,
le: Spectrum,
scale: Float,
cos_falloff_start: Float,
total_width: Float,
arena: &Arena
) -> Self;
}
impl CreateSpotLight for SpotLight {
fn new(
render_from_light: Transform,
_medium_interface: MediumInterface,
le: Spectrum,
scale: Float,
cos_falloff_start: Float,
total_width: Float,
arena: &Arena
) -> Self {
let base = LightBase::new(
LightType::DeltaPosition,
render_from_light,
MediumInterface::empty(),
);
let i = lookup_spectrum(&le);
let iemit = arena.alloc_arc(i);
Self {
base,
iemit,
scale,
cos_falloff_end: radians(total_width).cos(),
cos_falloff_start: radians(cos_falloff_start).cos(),
}
}
}
pub fn create(
render_from_light: Transform,
medium: Option<Medium>,
parameters: &ParameterDictionary,
_loc: &FileLoc,
_shape: &Shape,
_alpha_tex: &FloatTexture,
colorspace: Option<&RGBColorSpace>,
arena: &Arena,
) -> Result<Light> {
let default_cs = crate::spectra::default_colorspace();
let cs = colorspace.unwrap_or(&default_cs);
let i = parameters
.get_one_spectrum(
"I",
Some(Spectrum::Dense(cs.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 mi = match medium {
Some(m) => {
let ptr = arena.alloc(m);
MediumInterface {
inside: ptr,
outside: ptr,
}
}
None => MediumInterface::default(),
};
let specific = SpotLight::new(final_render, mi, i, scale, coneangle, coneangle - conedelta, arena);
arena.alloc(specific);
Ok(Light::Spot(specific))
}