use crate::core::color::{RGB, XYZ}; use crate::core::image::{Image, WrapMode, WrapMode2D}; use crate::core::spectrum::SpectrumTrait; use crate::core::texture::{SpectrumType, TextureEvalContext, TextureMapping2D}; use crate::spectra::{ RGBAlbedoSpectrum, RGBColorSpace, RGBIlluminantSpectrum, RGBUnboundedSpectrum, SampledSpectrum, SampledWavelengths, }; use crate::utils::Ptr; use crate::Float; /* GPU heavy code, dont know if this will ever work the way Im doing things. * Leaving it here isolated, for careful handling */ #[repr(C)] #[derive(Clone, Debug, Copy)] pub struct SpectrumImageTexture { pub wrap_mode: WrapMode, pub tex_obj: u64, pub scale: Float, pub spectrum_type: SpectrumType, pub image: Ptr, pub color_space: Ptr, pub mapping: TextureMapping2D, pub is_single_channel: bool, pub invert: bool, } impl SpectrumImageTexture { pub fn evaluate( &self, ctx: &TextureEvalContext, lambda: &SampledWavelengths, ) -> SampledSpectrum { #[cfg(feature = "cuda")] if self.tex_obj != 0 { // FUTURE: hardware sampling path. // let c = self.mapping.map(ctx); // let rgb = tex2d_grad(self.tex_obj, c.st, [c.dsdx,c.dtdx], [c.dsdy,c.dtdy]); // return spectrum_from_rgb(rgb * self.scale, self.invert, self.spectrum_type, ...); // Until then, fall through to software path below. } let Some(image) = self.image.get() else { return SampledSpectrum::zero(); }; let mut c = self.mapping.map(ctx); c.st[1] = 1.0 - c.st[1]; // flip V to match pbrt convention let wrap = WrapMode2D { uv: [self.wrap_mode; 2], }; let rgb = if image.n_channels == 1 { let v = image.bilerp_channel_with_wrap(c.st, 0, wrap); RGB::new(v, v, v) } else { RGB::new( image.bilerp_channel_with_wrap(c.st, 0, wrap), image.bilerp_channel_with_wrap(c.st, 1, wrap), image.bilerp_channel_with_wrap(c.st, 2, wrap), ) }; let mut rgb = rgb * self.scale; if self.invert { rgb = (RGB::new(1.0, 1.0, 1.0) - rgb); } rgb = rgb.clamp_zero(); let cs = self .color_space .get() .expect("color_space must not be null"); match self.spectrum_type { SpectrumType::Unbounded => RGBUnboundedSpectrum::new(cs, rgb).sample(lambda), SpectrumType::Albedo => RGBAlbedoSpectrum::new(cs, rgb.clamp(0.0, 1.0)).sample(lambda), _ => RGBIlluminantSpectrum::new(cs, rgb).sample(lambda), } } } #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct FloatImageTexture { pub image: Ptr, pub mapping: TextureMapping2D, pub wrap_mode: WrapMode, pub tex_obj: u64, pub scale: Float, pub invert: bool, } impl FloatImageTexture { #[allow(unused_variables)] pub fn evaluate(&self, ctx: &TextureEvalContext) -> Float { #[cfg(not(feature = "cuda"))] { let wrap = WrapMode2D { uv: [self.wrap_mode; 2], }; let mut c = self.mapping.map(ctx); c.st[1] = 1.0 - c.st[1]; let Some(image) = self.image.get() else { return 0. }; let v = image.bilerp_channel_with_wrap(c.st, 0, wrap); let v = if self.invert { (1.0 - v).max(0.0) } else { v }; return v * self.scale; } #[cfg(feature = "cuda")] { if self.tex_obj != 0 { use cuda_std::intrinsics; let c = self.mapping.map(ctx); let u = c.st.x(); let v = 1.0 - c.st.y(); let d_p_dx = [c.dsdx, c.dtdx]; let d_p_dy = [c.dsdy, c.dtdy]; // let val: Float = unsafe { intrinsics::tex2d_grad(self.tex_obj, u, v, d_p_dx, d_p_dy) }; let _ = (u, v, d_p_dx, d_p_dy); let val: Float = 0.; let result = if self.invert { (1.0 - val).max(0.0) } else { val }; return result * self.scale; } // software path (no hardware texture object) let wrap = WrapMode2D { uv: [self.wrap_mode; 2] }; let mut c = self.mapping.map(ctx); c.st[1] = 1.0 - c.st[1]; let Some(image) = self.image.get() else { return 0. }; let v = image.bilerp_channel_with_wrap(c.st, 0, wrap); let v = if self.invert { (1.0 - v).max(0.0) } else { v }; return v * self.scale; } } }