use crate::Float; use crate::core::color::RGB; use crate::core::geometry::{ Bounds2f, Bounds3f, Normal3f, Point2f, Point2i, Point3f, Ray, Vector3f, VectorLike, cos_theta, }; use crate::core::image::DeviceImage; use crate::core::light::{ LightBase, LightBounds, LightLiSample, LightSampleContext, LightTrait, LightType, }; use crate::core::medium::MediumInterface; use crate::core::spectrum::SpectrumTrait; use crate::spectra::{SampledSpectrum, SampledWavelengths}; use crate::utils::math::{radians, square}; use crate::utils::ptr::DevicePtr; use crate::{ spectra::{RGBColorSpace, RGBIlluminantSpectrum}, utils::{Transform, sampling::DevicePiecewiseConstant2D}, }; #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct ProjectionLight { pub base: LightBase, pub scale: Float, pub hither: Float, pub screen_bounds: Bounds2f, pub screen_from_light: Transform, pub light_from_screen: Transform, pub a: Float, pub image: DevicePtr, pub distrib: DevicePtr, pub image_color_space: DevicePtr, } impl ProjectionLight { pub fn i(&self, w: Vector3f, lambda: SampledWavelengths) -> SampledSpectrum { if w.z() < self.hither { return SampledSpectrum::new(0.); } let ps = self.screen_from_light.apply_to_point(w.into()); if !self.screen_bounds.contains(Point2f::new(ps.x(), ps.y())) { return SampledSpectrum::new(0.); } let uv = Point2f::from(self.screen_bounds.offset(&Point2f::new(ps.x(), ps.y()))); let mut rgb = RGB::default(); for c in 0..3 { rgb[c] = self.image.lookup_nearest_channel(uv, c as i32); } let s = RGBIlluminantSpectrum::new(&*self.image_color_space, rgb.clamp_zero()); self.scale * s.sample(&lambda) } } impl LightTrait for ProjectionLight { fn base(&self) -> &LightBase { &self.base } fn sample_li( &self, _ctx: &LightSampleContext, _u: Point2f, _lambda: &SampledWavelengths, _allow_incomplete_pdf: bool, ) -> Option { todo!() } fn pdf_li( &self, _ctx: &LightSampleContext, _wi: Vector3f, _allow_incomplete_pdf: bool, ) -> Float { todo!() } fn l( &self, _p: Point3f, _n: Normal3f, _uv: Point2f, _w: Vector3f, _lambda: &SampledWavelengths, ) -> SampledSpectrum { todo!() } fn le(&self, _ray: &Ray, _lambda: &SampledWavelengths) -> SampledSpectrum { todo!() } fn phi(&self, lambda: SampledWavelengths) -> SampledSpectrum { let mut sum = SampledSpectrum::new(0.); for y in 0..self.image.resolution.y() { for x in 0..self.image.resolution.x() { let ps = self.screen_bounds.lerp(Point2f::new( (x as Float + 0.5) / self.image.resolution.x() as Float, (y as Float + 0.5) / self.image.resolution.y() as Float, )); let w_raw = Vector3f::from(self.light_from_screen.apply_to_point(Point3f::new( ps.x(), ps.y(), 0., ))); let w = w_raw.normalize(); let dwda = cos_theta(w).powi(3); let mut rgb = RGB::default(); for c in 0..3 { rgb[c] = self.image.get_channel(Point2i::new(x, y), c as i32); } let s = RGBIlluminantSpectrum::new(&*self.image_color_space, rgb.clamp_zero()); sum += s.sample(&lambda) * dwda; } } self.scale * self.a * sum / (self.image.resolution.x() * self.image.resolution.y()) as Float } fn preprocess(&mut self, _scene_bounds: &Bounds3f) { todo!() } fn bounds(&self) -> Option { todo!() } }