use crate::core::color::{RGB, XYZ}; use crate::core::geometry::*; use crate::core::image::Image; use crate::core::interaction::{ Interaction, InteractionTrait, MediumInteraction, SurfaceInteraction, }; use crate::core::light::{ LightBase, LightBounds, LightLiSample, LightSampleContext, LightTrait, LightType, }; use crate::core::medium::MediumInterface; use crate::core::shape::{Shape, ShapeSampleContext, ShapeTrait}; use crate::core::spectrum::{Spectrum, SpectrumTrait}; use crate::core::texture::{ GPUFloatTexture, TextureEvalContext, TextureEvaluator, UniversalTextureEvaluator, }; use crate::spectra::*; use crate::utils::hash::hash_float; use crate::utils::{Ptr, Transform}; use crate::{Float, PI}; use num_traits::Float as NumFloat; #[repr(C)] #[derive(Clone, Debug, Copy)] pub struct DiffuseAreaLight { pub base: LightBase, pub shape: Ptr, pub alpha: Ptr, pub colorspace: Ptr, pub lemit: Ptr, pub image: Ptr, pub area: Float, pub two_sided: bool, pub scale: Float, } unsafe impl Send for DiffuseAreaLight {} unsafe impl Sync for DiffuseAreaLight {} impl DiffuseAreaLight { fn l_base(&self, n: Normal3f, wo: Vector3f, lambda: &SampledWavelengths) -> SampledSpectrum { if !self.two_sided && n.dot(wo.into()) <= 0.0 { return SampledSpectrum::new(0.0); } self.lemit.sample(lambda) * self.scale } fn alpha_masked(&self, intr: &Interaction) -> bool { if self.alpha.is_null() { return false; }; let ctx = TextureEvalContext::from(intr); let a = UniversalTextureEvaluator.evaluate_float(&self.alpha, &ctx); if a >= 1.0 { return false; } if a <= 0.0 { return true; } hash_float(&intr.p()) > a } } impl LightTrait for DiffuseAreaLight { fn base(&self) -> &LightBase { &self.base } fn sample_li( &self, ctx: &LightSampleContext, u: Point2f, lambda: &SampledWavelengths, _allow_incomplete_pdf: bool, ) -> Option { let shape_ctx = ShapeSampleContext::new(ctx.pi, ctx.n, ctx.ns, 0.0); let ss = self.shape.sample_from_context(&shape_ctx, u)?; let mut intr = ss.intr; intr.set_medium_interface(self.base.medium_interface); let p = intr.p(); let n = intr.n(); let uv = intr.get_common().uv; // let generic_intr = Interaction::Surface(intr); if self.alpha_masked(&intr) { return None; } let wi = (p - ctx.p()).normalize(); let le = self.l(p, n, uv, -wi, lambda); if le.is_black() { return None; } Some(LightLiSample::new(le, wi, ss.pdf, intr)) } fn pdf_li(&self, ctx: &LightSampleContext, wi: Vector3f, _allow_incomplete_pdf: bool) -> Float { let shape_ctx = ShapeSampleContext::new(ctx.pi, ctx.n, ctx.ns, 0.); self.shape.pdf_from_context(&shape_ctx, wi) } fn l( &self, p: Point3f, n: Normal3f, mut uv: Point2f, w: Vector3f, lambda: &SampledWavelengths, ) -> SampledSpectrum { if self.two_sided && n.dot(w.into()) < 0. { return SampledSpectrum::new(0.); } let intr = Interaction::Surface(SurfaceInteraction::new_minimal( Point3fi::new_from_point(p), uv, )); if self.alpha_masked(&intr) { return SampledSpectrum::new(0.); } if !self.image.is_null() { let mut rgb = RGB::default(); uv[1] = 1. - uv[1]; for c in 0..3 { rgb[c] = self.image.bilerp_channel(uv, c as i32); } let spec = RGBIlluminantSpectrum::new(&self.colorspace, rgb.clamp_zero()); self.scale * spec.sample(lambda) } else { self.scale * self.lemit.sample(lambda) } } fn le(&self, _ray: &Ray, _lambda: &SampledWavelengths) -> SampledSpectrum { todo!() } #[cfg(not(target_os = "cuda"))] fn phi(&self, lambda: SampledWavelengths) -> SampledSpectrum { let mut l = SampledSpectrum::new(0.); if !self.image.is_null() { for y in 0..self.image.resolution().y() { for x in 0..self.image.resolution().x() { let mut rgb = RGB::default(); for c in 0..3 { rgb[c] = self.image.get_channel(Point2i::new(x, y), c as i32); } l += RGBIlluminantSpectrum::new(&self.colorspace, rgb.clamp_zero()).sample(&lambda); } } l *= self.scale / (self.image.resolution().x() * self.image.resolution().y()) as Float; } else { l = self.lemit.sample(&lambda) * self.scale; } let two_side = if self.two_sided { 2. } else { 1. }; PI * two_side * self.area * l } #[cfg(not(target_os = "cuda"))] fn preprocess(&mut self, _scene_bounds: &Bounds3f) { return } #[cfg(not(target_os = "cuda"))] fn bounds(&self) -> Option { let mut phi = 0.; if !self.image.is_null() { for y in 0..self.image.resolution().y() { for x in 0..self.image.resolution().x() { for c in 0..3 { phi += self.image.get_channel(Point2i::new(x, y), c); } } } } else { phi = self.lemit.max_value(); } let nb = self.shape.normal_bounds(); Some(LightBounds::new( &self.shape.bounds(), nb.w, phi, nb.cos_theta, (PI / 2.).cos(), self.two_sided, )) } }