use crate::core::image::{HostImage, ImageIO}; use crate::core::light::lookup_spectrum; use crate::core::spectrum::spectrum_to_photometric; use crate::core::texture::FloatTexture; use crate::utils::resolve_filename; use crate::utils::upload::ArenaUpload; use crate::{Arena, FileLoc, ParameterDictionary}; use anyhow::{anyhow, Result}; use shared::core::geometry::Point2i; use shared::core::light::{Light, LightBase, LightType}; use shared::core::medium::{Medium, MediumInterface}; use shared::core::shape::{Shape, ShapeTrait}; use shared::core::spectrum::Spectrum; use shared::core::texture::{GPUFloatTexture, SpectrumType, TextureEvalContext}; use shared::lights::DiffuseAreaLight; use shared::spectra::RGBColorSpace; use shared::utils::Transform; use shared::{Float, PI}; use std::path::Path; pub fn create( render_from_light: Transform, medium: Option, params: &ParameterDictionary, loc: &FileLoc, shape: &Shape, alpha: &FloatTexture, colorspace: Option<&RGBColorSpace>, arena: &Arena, ) -> Result { let mut l = params.get_one_spectrum("l", None, SpectrumType::Illuminant); let default_cs = crate::spectra::default_colorspace(); let cs = colorspace.unwrap_or(&default_cs); let illum_spec = Spectrum::Dense(cs.illuminant); let mut scale = params.get_one_float("scale", 1.)?; let two_sided = params.get_one_bool("twosided", false)?; let filename = resolve_filename(¶ms.get_one_string("filename", "")?); let (image, image_color_space): (Option, Option) = if !filename.is_empty() { if l.is_some() { return Err(anyhow!("{}: both \"L\" and \"filename\" specified", loc)); } let im = HostImage::read(Path::new(&filename), None)?; if im.image.has_any_infinite_pixels() { return Err(anyhow!("{}: image has infinite pixel values", loc)); } if im.image.has_any_nan_pixels() { return Err(anyhow!("{}: image has NaN pixel values", loc)); } let channel_desc = im .image .get_channel_desc(&["R", "G", "B"]) .map_err(|_| anyhow!("{}: image must have R, G, B channels", loc))?; let image = im.image.select_channels(&channel_desc); let cs = im.metadata.get_colorspace(); (Some(image), cs) } else { if l.is_none() { l = Some(illum_spec); } (None, None) }; let l_for_scale = l.as_ref().unwrap_or(&illum_spec); scale /= spectrum_to_photometric(*l_for_scale); let phi_v = params.get_one_float("power", -1.0)?; if phi_v > 0.0 { let mut k_e: Float = 1.0; if let Some(ref img) = image { let lum_vec = image_color_space .as_ref() .expect("image present but no color space") .luminance_vector(); let mut sum_k_e = 0.0; let res = img.resolution(); for y in 0..res.y() { for x in 0..res.x() { let r = img.get_channel(Point2i::new(x, y), 0); let g = img.get_channel(Point2i::new(x, y), 1); let b = img.get_channel(Point2i::new(x, y), 2); sum_k_e += r * lum_vec[0] + g * lum_vec[1] + b * lum_vec[2]; } } k_e = sum_k_e / (res.x() * res.y()) as Float; } let side_factor = if two_sided { 2.0 } else { 1.0 }; k_e *= side_factor * shape.area() * PI; scale *= phi_v / k_e; } // Upload alpha texture to GPU and check for null texture let alpha_ptr = arena.upload(alpha); let light_type = match unsafe { alpha_ptr.as_ref() } { GPUFloatTexture::Constant(t) if t.evaluate(&TextureEvalContext::default()) == 0.0 => { LightType::DeltaPosition } _ => LightType::Area, }; let mi = match medium { Some(m) => { let ptr = arena.alloc(m); MediumInterface { inside: ptr, outside: ptr, } } None => MediumInterface::default(), }; let base = LightBase::new(light_type, render_from_light, mi); if let Some(ref img) = image { let desc = img .get_channel_desc(&["R", "G", "B"]) .expect("Image used for DiffuseAreaLight doesn't have R, G, B channels"); assert_eq!(3, desc.size()); assert!(desc.is_identity()); assert!( image_color_space.is_some(), "Image provided but ColorSpace is missing" ); } let is_triangle_or_bilinear = matches!(*shape, Shape::Triangle(_) | Shape::BilinearPatch(_)); if render_from_light.has_scale(None) && !is_triangle_or_bilinear { eprintln!( "Warning: scaling detected in rendering-to-light transform; \ image may have errors." ); } let specific = DiffuseAreaLight { base, area: shape.area(), shape: arena.alloc(*shape), alpha: alpha_ptr, image: arena.upload(image), colorspace: arena.alloc_opt(image_color_space), lemit: arena.alloc((*lookup_spectrum(l_for_scale)).clone()), two_sided, scale, }; Ok(Light::DiffuseArea(specific)) }