pbrt/shared/src/textures/image.rs
2026-06-07 18:53:23 +01:00

131 lines
4.6 KiB
Rust

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<Image>,
pub color_space: Ptr<RGBColorSpace>,
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<Image>,
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;
}
}
}