use crate::core::geometry::{ Bounds3f, Normal3f, Point2f, Point2i, Point3f, Ray, Vector3f, VectorLike, }; use crate::core::image::Image; use crate::core::interaction::{Interaction, InteractionBase, SimpleInteraction}; use crate::core::light::{ LightBase, LightBounds, LightLiSample, LightSampleContext, LightTrait, LightType, }; use crate::core::medium::MediumInterface; use crate::core::spectrum::{Spectrum, SpectrumTrait}; use crate::spectra::{DenselySampledSpectrum, SampledSpectrum, SampledWavelengths}; use crate::utils::math::equal_area_sphere_to_square; use crate::utils::sampling::PiecewiseConstant2D; use crate::utils::{Ptr, Transform}; use crate::{Float, PI}; #[derive(Debug, Clone, Copy)] pub struct GoniometricLight { pub base: LightBase, pub iemit: Ptr, pub scale: Float, pub image: Ptr, pub distrib: Ptr, } impl GoniometricLight { pub fn i(&self, w: Vector3f, lambda: &SampledWavelengths) -> SampledSpectrum { let uv = equal_area_sphere_to_square(w); self.scale * self.iemit.sample(lambda) * self.image.lookup_nearest_channel(uv, 0) } } impl LightTrait for GoniometricLight { fn base(&self) -> &LightBase { &self.base } fn sample_li( &self, ctx: &LightSampleContext, _u: Point2f, lambda: &SampledWavelengths, _allow_incomplete_pdf: bool, ) -> Option { let render_from_light = self.base().render_from_light; let p = render_from_light.apply_to_point(Point3f::new(0., 0., 0.)); let wi = (p - ctx.p()).normalize(); let wl = render_from_light.apply_inverse_vector(-wi); let li = self.i(wl, lambda) / p.distance_squared(ctx.p()); let base = InteractionBase::new_boundary(p, 0., self.base.medium_interface); let intr = SimpleInteraction::new(base); Some(LightLiSample::new(li, wi, 1., Interaction::Simple(intr))) } fn pdf_li( &self, _ctx: &LightSampleContext, _wi: Vector3f, _allow_incomplete_pdf: bool, ) -> Float { 0. } fn preprocess(&mut self, _scene_bounds: &Bounds3f) {} fn bounds(&self) -> Option { let mut sum_y = 0.; for x in 0..self.image.resolution().x() { for y in 0..self.image.resolution().y() { sum_y += self.image.get_channel(Point2i::new(x, y), 0); } } let phi = self.scale * self.iemit.max_value() * 4. * PI * sum_y / (self.image.resolution().x() * self.image.resolution().y()) as f32; let p = self .base() .render_from_light .apply_to_point(Point3f::new(0., 0., 0.)); Some(LightBounds::new( &Bounds3f::from_points(p, p), Vector3f::new(0., 0., 1.), phi, PI.cos(), (PI / 2.).cos(), false, )) } #[cfg(not(gpu))] fn phi(&self, lambda: SampledWavelengths) -> SampledSpectrum { let resolution = self.image.resolution(); let mut sum_y = 0.; for y in 0..resolution.y() { for x in 0..resolution.x() { sum_y += self.image.get_channel(Point2i::new(x, y), 0); } } self.scale * self.iemit.sample(&lambda) * 4. * PI * sum_y / (resolution.x() * resolution.y()) as Float } }