diff --git a/shared/src/core/color.rs b/shared/src/core/color.rs index c45f972..204cef6 100644 --- a/shared/src/core/color.rs +++ b/shared/src/core/color.rs @@ -8,6 +8,7 @@ use core::fmt; use core::ops::{ Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub, SubAssign, }; +use anyhow::{Result, bail}; use enum_dispatch::enum_dispatch; use num_traits::Float as NumFloat; @@ -680,6 +681,16 @@ pub enum ColorEncoding { SRGB(SRGBEncoding), } +impl ColorEncoding { + pub fn from_name(name: &str) -> Result { + match name { + "sRGB" | "srgb" => Ok(ColorEncoding::SRGB(SRGBEncoding)), + "linear" => Ok(ColorEncoding::Linear(LinearEncoding)), + _ => bail!("Unknown color encoding: {}", name), + } + } +} + impl fmt::Display for ColorEncoding { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Encoding") diff --git a/shared/src/core/image.rs b/shared/src/core/image.rs index f0e9258..4b00969 100644 --- a/shared/src/core/image.rs +++ b/shared/src/core/image.rs @@ -2,6 +2,7 @@ use crate::core::color::{ColorEncoding, ColorEncodingTrait, LINEAR}; use crate::core::geometry::{Bounds2f, Point2f, Point2fi, Point2i}; use crate::utils::math::{f16_to_f32_software, lerp, square}; use crate::{gvec_with_capacity, Float, GVec}; +use anyhow::{Result, bail}; use core::hash; use core::ops::{Deref, DerefMut}; use num_traits::Float as NumFloat; @@ -14,6 +15,18 @@ pub enum WrapMode { OctahedralSphere, } +impl WrapMode { + pub fn parse(name: &str) -> Result { + match name { + "clamp" => Ok(WrapMode::Clamp), + "black" => Ok(WrapMode::Black), + "repeat" => Ok(WrapMode::Repeat), + "octahedralsphere" => Ok(WrapMode::OctahedralSphere), + _ => bail!("{:?}: wrap mode unknown", name) + } + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct WrapMode2D { pub uv: [WrapMode; 2], diff --git a/shared/src/textures/mod.rs b/shared/src/textures/mod.rs index 7e369ab..1931f45 100644 --- a/shared/src/textures/mod.rs +++ b/shared/src/textures/mod.rs @@ -6,7 +6,6 @@ pub mod fbm; pub mod image; pub mod marble; pub mod mix; -pub mod ptex; pub mod scaled; pub mod windy; pub mod wrinkled; @@ -19,7 +18,6 @@ pub use fbm::*; pub use image::*; pub use marble::*; pub use mix::*; -pub use ptex::*; pub use scaled::*; pub use windy::*; pub use wrinkled::*; diff --git a/shared/src/textures/ptex.rs b/shared/src/textures/ptex.rs deleted file mode 100644 index b5ed2bc..0000000 --- a/shared/src/textures/ptex.rs +++ /dev/null @@ -1,51 +0,0 @@ -use crate::Float; -use crate::core::color::{ColorEncoding, RGB}; -use crate::core::spectrum::{SpectrumTrait, StandardSpectra}; -use crate::core::texture::{SpectrumType, TextureEvalContext}; -use crate::spectra::{ - DeviceStandardColorSpaces, RGBAlbedoSpectrum, RGBColorSpace, RGBIlluminantSpectrum, - RGBUnboundedSpectrum, SampledSpectrum, SampledWavelengths, -}; -use crate::utils::Ptr; - -#[repr(C)] -#[derive(Debug, Clone, Copy)] -pub struct GPUFloatPtexTexture { - pub face_values: *const Float, -} - -impl GPUFloatPtexTexture { - pub fn evaluate(&self, ctx: &TextureEvalContext) -> Float { - unsafe { *self.face_values.add(ctx.face_index as usize) } - } -} - -#[repr(C)] -#[derive(Clone, Debug, Copy)] -pub struct GPUSpectrumPtexTexture { - pub face_values: Ptr, - pub n_faces: u32, - pub spectrum_type: SpectrumType, - pub colorspaces: DeviceStandardColorSpaces, -} - -impl GPUSpectrumPtexTexture { - pub fn evaluate( - &self, - ctx: &TextureEvalContext, - lambda: &SampledWavelengths, - ) -> SampledSpectrum { - let index = (ctx.face_index as u32).clamp(0, self.n_faces.saturating_sub(1)); - let rgb = unsafe { &*self.face_values.add(index as usize) }; - let s_rgb = self.colorspaces.srgb; - - match self.spectrum_type { - SpectrumType::Unbounded => RGBUnboundedSpectrum::new(&s_rgb, *rgb).sample(lambda), - SpectrumType::Albedo => { - let clamped_rgb = rgb.clamp(0.0, 1.0); - RGBAlbedoSpectrum::new(&s_rgb, clamped_rgb).sample(lambda) - } - SpectrumType::Illuminant => RGBIlluminantSpectrum::new(&s_rgb, *rgb).sample(lambda), - } - } -} diff --git a/src/core/film.rs b/src/core/film.rs index cfb967a..abac594 100644 --- a/src/core/film.rs +++ b/src/core/film.rs @@ -335,9 +335,6 @@ pub trait FilmTrait: Sync { for x in pixel_bounds.p_min.x()..pixel_bounds.p_max.x() { let p = Point2i::new(x, y); let mut rgb = self.get_pixel_rgb(p, Some(splat_scale)); - if rgb.r > 1.0 || rgb.g > 1.0 || rgb.b > 1.0 { - eprintln!("p={:?} get_pixel_rgb=({}, {}, {})", p, rgb.r, rgb.g, rgb.b); - } let mut was_clamped = false; if write_fp16 { if rgb.r > 65504.0 { diff --git a/src/textures/image.rs b/src/textures/image.rs index d2b6106..a44affd 100644 --- a/src/textures/image.rs +++ b/src/textures/image.rs @@ -3,16 +3,16 @@ use crate::core::texture::{ CreateFloatTexture, CreateSpectrumTexture, FloatTexture, FloatTextureTrait, SpectrumTexture, SpectrumTextureTrait, }; -use crate::utils::mipmap::{MIPMap, MIPMapFilterOptions}; -use crate::utils::{FileLoc, TextureParameterDictionary}; -use crate::Arena; +use crate::utils::mipmap::{FilterFunction, MIPMap, MIPMapFilterOptions}; +use crate::utils::{FileLoc, TextureParameterDictionary, resolve_filename}; +use crate::{Arena}; use anyhow::Result; use shared::core::color::RGB; use shared::core::color::{ColorEncoding, SRGBEncoding}; use shared::core::geometry::Vector2f; use shared::core::image::WrapMode; use shared::core::spectrum::SpectrumTrait; -use shared::core::texture::{SpectrumType, TextureEvalContext, TextureMapping2D}; +use shared::core::texture::{SpectrumType, TexCoord2D, TextureEvalContext, TextureMapping2D}; use shared::spectra::{ RGBAlbedoSpectrum, RGBIlluminantSpectrum, RGBUnboundedSpectrum, SampledSpectrum, SampledWavelengths, @@ -168,6 +168,7 @@ impl CreateSpectrumTexture for SpectrumImageTexture { let filter_options = MIPMapFilterOptions::default(); let wrap_str = parameters.get_one_string("wrap", "repeat")?; + // let wrap_mode = WrapMode::parse(wrap_str)?; let wrap_mode = match wrap_str.as_str() { "repeat" => WrapMode::Repeat, "clamp" => WrapMode::Clamp, @@ -222,18 +223,65 @@ impl FloatImageTexture { } impl FloatTextureTrait for FloatImageTexture { - fn evaluate(&self, _ctx: &TextureEvalContext) -> Float { - todo!() + fn evaluate(&self, ctx: &TextureEvalContext) -> Float { + let mut c: TexCoord2D = self.base.mapping.map(ctx); + c.st[1] = 1. - c.st[1]; + let v: Float = self.base.scale + * self.base.mipmap.filter::( + c.st, + Vector2f::new(c.dsdx, c.dtdx), + Vector2f::new(c.dsdy, c.dtdy), + ); + + if self.base.invert { + (1. - v).max(0.) + } else { + v + } } } impl CreateFloatTexture for FloatImageTexture { fn create( - _render_from_texture: Transform, - _parameters: TextureParameterDictionary, - _loc: FileLoc, + render_from_texture: Transform, + parameters: TextureParameterDictionary, + loc: FileLoc, _arena: &Arena, ) -> Result { - todo!() + let mapping = TextureMapping2D::create(¶meters, &render_from_texture, &loc)?; + let max_aniso = parameters.get_one_float("maxanisotropy", 8.)?; + let filter = parameters.get_one_string("filter", "bilinear")?; + let mut filter_options = MIPMapFilterOptions::default(); + filter_options.max_anisotropy = max_aniso; + + let ff = FilterFunction::parse(&filter)?; + filter_options.filter = ff; + let wrap_string = parameters.get_one_string("wrap", "repeat")?; + let wrap_mode = WrapMode::parse(&wrap_string)?; + let scale = parameters.get_one_float("scale", 1.)?; + let invert = parameters.get_one_bool("invert", false)?; + let filename = resolve_filename(¶meters.get_one_string("filename", "")?); + let default_encoding = if Path::new(&filename) + .extension() + .map_or(false, |ext| ext == "png") + { + "sRGB" + } else { + "linear" + }; + let encoding_str = parameters.get_one_string("encoding", default_encoding)?; + let encoding = ColorEncoding::from_name(&encoding_str)?; + + let tex = FloatImageTexture::new( + mapping, + filename, + filter_options, + wrap_mode, + scale, + invert, + encoding, + ); + + Ok(FloatTexture::Image(tex)) } } diff --git a/src/utils/mipmap.rs b/src/utils/mipmap.rs index 1040c37..8487945 100644 --- a/src/utils/mipmap.rs +++ b/src/utils/mipmap.rs @@ -3,6 +3,7 @@ use shared::core::color::{ColorEncoding, RGB}; use shared::core::geometry::{Point2f, Point2i, Vector2f, VectorLike}; use shared::core::image::{WrapMode, WrapMode2D}; use shared::spectra::RGBColorSpace; +use anyhow::{bail, Result}; use shared::utils::math::{lerp, safe_sqrt, square}; use shared::Float; use std::hash::{Hash, Hasher}; @@ -21,6 +22,19 @@ pub enum FilterFunction { Ewa, } +impl FilterFunction { + pub fn parse(name: &str) -> Result { + match name { + "ewa" | "EWA" => Ok(FilterFunction::Ewa), + "trilinear" => Ok(FilterFunction::Trilinear), + "bilinear" => Ok(FilterFunction::Bilinear), + "point" => Ok(FilterFunction::Point), + _ => bail!("Filter function unknown") + } + } +} + + impl std::fmt::Display for FilterFunction { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let s = match self {