diff --git a/shared/src/materials/dielectric.rs b/shared/src/materials/dielectric.rs index e89eacb..d2f12be 100644 --- a/shared/src/materials/dielectric.rs +++ b/shared/src/materials/dielectric.rs @@ -96,13 +96,13 @@ impl MaterialTrait for ThinDielectricMaterial { ctx: &MaterialEvalContext, lambda: &mut SampledWavelengths, ) -> BSDF { - let sampled_eta = self.eta.evaluate(lambda[0]); + let mut sampled_eta = self.eta.evaluate(lambda[0]); if !self.eta.is_constant() { lambda.terminate_secondary_inplace(); } if sampled_eta == 0. { - sampled_eta == 1.; + sampled_eta = 1.; } let bxdf = BxDF::ThinDielectric(ThinDielectricBxDF::new(sampled_eta)); diff --git a/shared/src/textures/checkerboard.rs b/shared/src/textures/checkerboard.rs index ea4ee3d..2e09e4d 100644 --- a/shared/src/textures/checkerboard.rs +++ b/shared/src/textures/checkerboard.rs @@ -7,14 +7,17 @@ use crate::spectra::{SampledSpectrum, SampledWavelengths}; use crate::utils::{Ptr, math::square}; use num_traits::Float as NumFloat; -fn checkerboard( - ctx: &TextureEvalContext, - map2d: Ptr, - map3d: Ptr, -) -> Float { +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub enum CheckerMap { + D2(TextureMapping2D), + D3(TextureMapping3D), +} + +fn checkerboard(ctx: &TextureEvalContext, checker_map: CheckerMap) -> Float { let d = |x: Float| -> Float { let y = x / 2. - (x / 2.).floor() - 0.5; - return x / 2. + y * (1. - 2. * y.abs()); + x / 2. + y * (1. - 2. * y.abs()) }; let bf = |x: Float, r: Float| -> Float { @@ -24,48 +27,52 @@ fn checkerboard( (d(x + r) - 2. * d(x) + d(x - r)) / square(r) }; - if !map2d.is_null() { - assert!(map3d.is_null()); - let c = map2d.map(&ctx); - let ds = 1.5 * c.dsdx.abs().max(c.dsdy.abs()); - let dt = 1.5 * c.dtdx.abs().max(c.dtdy.abs()); - // Integrate product of 2D checkerboard function and triangle filter - 0.5 - bf(c.st[0], ds) * bf(c.st[1], dt) / 2. - } else { - assert!(!map3d.is_null()); - let c = map3d.map(&ctx); - let dx = 1.5 * c.dpdx.x().abs().max(c.dpdy.x().abs()); - let dy = 1.5 * c.dpdx.y().abs().max(c.dpdy.y().abs()); - let dz = 1.5 * c.dpdx.z().abs().max(c.dpdy.z().abs()); - 0.5 - bf(c.p.x(), dx) * bf(c.p.y(), dy) * bf(c.p.z(), dz) + match checker_map { + CheckerMap::D2(map) => { + let c = map.map(ctx); + let ds = 1.5 * c.dsdx.abs().max(c.dsdy.abs()); + let dt = 1.5 * c.dtdx.abs().max(c.dtdy.abs()); + // Integrate product of 2D checkerboard function and triangle filter + 0.5 - bf(c.st[0], ds) * bf(c.st[1], dt) / 2. + } + CheckerMap::D3(map) => { + let c = map.map(ctx); + let dx = 1.5 * c.dpdx.x().abs().max(c.dpdy.x().abs()); + let dy = 1.5 * c.dpdx.y().abs().max(c.dpdy.y().abs()); + let dz = 1.5 * c.dpdx.z().abs().max(c.dpdy.z().abs()); + 0.5 - bf(c.p.x(), dx) * bf(c.p.y(), dy) * bf(c.p.z(), dz) + } } } #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct FloatCheckerboardTexture { - pub map2d: Ptr, - pub map3d: Ptr, + pub map: CheckerMap, pub tex: [Ptr; 2], } impl FloatCheckerboardTexture { + pub fn new(map: CheckerMap, tex: [Ptr; 2]) -> Self { + Self { map, tex } + } + pub fn evaluate(&self, ctx: &TextureEvalContext) -> Float { - let w = checkerboard(&ctx, self.map2d, self.map3d); + let w = checkerboard(ctx, self.map); let mut t0 = 0.0; let mut t1 = 0.0; - if w != 1.0 { - if let Some(tex) = self.tex[0].get() { - t0 = tex.evaluate(ctx); - } + if w != 1.0 + && let Some(tex) = self.tex[0].get() + { + t0 = tex.evaluate(ctx); } - if w != 0.0 { - if let Some(tex) = self.tex[1].get() { - t1 = tex.evaluate(ctx); - } + if w != 0.0 + && let Some(tex) = self.tex[1].get() + { + t1 = tex.evaluate(ctx); } (1.0 - w) * t0 + w * t1 @@ -75,30 +82,33 @@ impl FloatCheckerboardTexture { #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct SpectrumCheckerboardTexture { - pub map2d: Ptr, - pub map3d: Ptr, + pub map: CheckerMap, pub tex: [Ptr; 2], } impl SpectrumCheckerboardTexture { + pub fn new(map: CheckerMap, tex: [Ptr; 2]) -> Self { + Self { map, tex } + } + pub fn evaluate( &self, ctx: &TextureEvalContext, lambda: &SampledWavelengths, ) -> SampledSpectrum { - let w = checkerboard(ctx, self.map2d, self.map3d); + let w = checkerboard(ctx, self.map); let mut t0 = SampledSpectrum::new(0.); let mut t1 = SampledSpectrum::new(0.); - if w != 1.0 { - if let Some(tex) = self.tex[0].get() { - t0 = tex.evaluate(ctx, lambda); - } + if w != 1.0 + && let Some(tex) = self.tex[0].get() + { + t0 = tex.evaluate(ctx, lambda); } - if w != 0.0 { - if let Some(tex) = self.tex[1].get() { - t1 = tex.evaluate(ctx, lambda); - } + if w != 0.0 + && let Some(tex) = self.tex[1].get() + { + t1 = tex.evaluate(ctx, lambda); } t0 * (1.0 - w) + t1 * w diff --git a/shared/src/textures/dots.rs b/shared/src/textures/dots.rs index 84191da..f10e728 100644 --- a/shared/src/textures/dots.rs +++ b/shared/src/textures/dots.rs @@ -1,8 +1,6 @@ use crate::Float; use crate::core::geometry::{Point2f, VectorLike}; -use crate::core::texture::{ - FloatTexture, SpectrumTexture, TextureEvalContext, TextureMapping2D, -}; +use crate::core::texture::{FloatTexture, SpectrumTexture, TextureEvalContext, TextureMapping2D}; use crate::spectra::sampled::{SampledSpectrum, SampledWavelengths}; use crate::utils::Ptr; use crate::utils::math::square; @@ -22,18 +20,30 @@ fn inside_polka_dot(st: Point2f) -> bool { return true; } } - return false; + false } #[repr(C)] #[derive(Debug, Clone, Copy)] pub struct FloatDotsTexture { pub mapping: TextureMapping2D, - pub outside_dot: Ptr, pub inside_dot: Ptr, + pub outside_dot: Ptr, } impl FloatDotsTexture { + pub fn new( + mapping: TextureMapping2D, + inside_dot: Ptr, + outside_dot: Ptr, + ) -> Self { + Self { + mapping, + inside_dot, + outside_dot, + } + } + pub fn evaluate(&self, ctx: &TextureEvalContext) -> Float { let c = self.mapping.map(ctx); let target_texture = if inside_polka_dot(c.st) { @@ -54,11 +64,22 @@ impl FloatDotsTexture { #[derive(Clone, Copy, Debug)] pub struct SpectrumDotsTexture { pub mapping: TextureMapping2D, - pub outside_dot: Ptr, pub inside_dot: Ptr, + pub outside_dot: Ptr, } impl SpectrumDotsTexture { + pub fn new( + mapping: TextureMapping2D, + inside_dot: Ptr, + outside_dot: Ptr, + ) -> Self { + Self { + mapping, + inside_dot, + outside_dot, + } + } pub fn evaluate( &self, ctx: &TextureEvalContext, diff --git a/shared/src/textures/fbm.rs b/shared/src/textures/fbm.rs index 06b289f..d411010 100644 --- a/shared/src/textures/fbm.rs +++ b/shared/src/textures/fbm.rs @@ -5,11 +5,18 @@ use crate::utils::noise::fbm; #[derive(Debug, Clone, Copy)] pub struct FBmTexture { pub mapping: TextureMapping3D, - pub omega: Float, pub octaves: u32, + pub omega: Float, } impl FBmTexture { + pub fn new(mapping: TextureMapping3D, octaves: u32, omega: Float) -> Self { + Self { + mapping, + omega, + octaves, + } + } pub fn evaluate(&self, ctx: &TextureEvalContext) -> Float { let c = self.mapping.map(ctx); fbm(c.p, c.dpdx, c.dpdy, self.omega, self.octaves) diff --git a/src/textures/bilerp.rs b/src/textures/bilerp.rs index 965e0c6..2ca81b3 100644 --- a/src/textures/bilerp.rs +++ b/src/textures/bilerp.rs @@ -30,7 +30,6 @@ impl CreateFloatTexture for FloatBilerpTexture { } } - impl CreateSpectrumTexture for SpectrumBilerpTexture { fn create( render_from_texture: Transform, @@ -43,9 +42,7 @@ impl CreateSpectrumTexture for SpectrumBilerpTexture { let zero = Spectrum::Constant(ConstantSpectrum::new(0.)); let one = Spectrum::Constant(ConstantSpectrum::new(1.)); - // `Spectrum` is already a device type, so it is allocated into the arena - // rather than uploaded -- upload is for host trees (Arc) -> device (Ptr). - let mut get = |name: &str, def: Spectrum| { + let get = |name: &str, def: Spectrum| { let s = parameters .get_one_spectrum(name, Some(def), spectrum_type) .unwrap_or(def); diff --git a/src/textures/checkerboard.rs b/src/textures/checkerboard.rs index 0b1dafd..c908514 100644 --- a/src/textures/checkerboard.rs +++ b/src/textures/checkerboard.rs @@ -1,37 +1,79 @@ use crate::Arena; -use anyhow::Result; +use anyhow::{Result, bail}; use shared::{ - core::texture::SpectrumType, - textures::{FloatCheckerboardTexture, SpectrumCheckerboardTexture}, - utils::Transform + core::spectrum::Spectrum, + core::texture::{SpectrumType, TextureMapping2D, TextureMapping3D}, + spectra::ConstantSpectrum, + textures::{CheckerMap, FloatCheckerboardTexture, SpectrumCheckerboardTexture}, + utils::Transform, }; use crate::{ core::texture::{ - CreateFloatTexture, CreateSpectrumTexture, FloatTexture, SpectrumTexture }, - utils::{FileLoc, TextureParameterDictionary} + CreateFloatTexture, CreateSpectrumTexture, CreateTextureMapping, FloatTexture, + SpectrumTexture, + }, + utils::{ArenaUpload, FileLoc, TextureParameterDictionary}, }; +fn checker_map( + render_from_texture: Transform, + parameters: TextureParameterDictionary, + loc: FileLoc, +) -> Result { + match parameters.get_one_int("dimension", 2)? { + 2 => Ok(CheckerMap::D2(TextureMapping2D::create( + ¶meters, + &render_from_texture, + &loc, + )?)), + 3 => Ok(CheckerMap::D3(TextureMapping3D::create( + ¶meters, + &render_from_texture, + &loc, + )?)), + dim => bail!("{loc}: {dim} dimensional checkerboard texture not supported"), + } +} + impl CreateFloatTexture for FloatCheckerboardTexture { fn create( - _render_from_texture: Transform, - _parameters: TextureParameterDictionary, - _loc: FileLoc, - _arena: &Arena, + render_from_texture: Transform, + parameters: TextureParameterDictionary, + loc: FileLoc, + arena: &Arena, ) -> Result { - todo!() + let tex1 = arena.upload(parameters.get_float_texture("tex1", 1.)?); + let tex2 = arena.upload(parameters.get_float_texture("tex2", 0.)?); + let map = checker_map(render_from_texture, parameters, loc)?; + let tex = FloatCheckerboardTexture::new(map, [tex1, tex2]); + Ok(FloatTexture::Checkerboard(tex)) } } impl CreateSpectrumTexture for SpectrumCheckerboardTexture { fn create( - _render_from_texture: Transform, - _parameters: TextureParameterDictionary, - _spectrum_type: SpectrumType, - _loc: FileLoc, - _arena: &Arena, + render_from_texture: Transform, + parameters: TextureParameterDictionary, + spectrum_type: SpectrumType, + loc: FileLoc, + arena: &Arena, ) -> Result { - todo!() + let zero = Spectrum::Constant(ConstantSpectrum::new(0.)); + let one = Spectrum::Constant(ConstantSpectrum::new(1.)); + let tex = |name: &str, def: Spectrum| { + arena.upload( + parameters + .get_spectrum_texture(name, Some(def), spectrum_type) + .expect("default supplied"), + ) + }; + + let tex1 = tex("tex1", one); + let tex2 = tex("tex2", zero); + + let map = checker_map(render_from_texture, parameters, loc)?; + let tex = SpectrumCheckerboardTexture::new(map, [tex1, tex2]); + Ok(SpectrumTexture::Checkerboard(tex)) } } - diff --git a/src/textures/dots.rs b/src/textures/dots.rs index 97cd160..f35f7be 100644 --- a/src/textures/dots.rs +++ b/src/textures/dots.rs @@ -1,36 +1,57 @@ -use crate::Arena; +use crate::{Arena, ArenaUpload}; use anyhow::Result; use shared::{ - core::texture::SpectrumType, + core::spectrum::Spectrum, + core::texture::{SpectrumType, TextureMapping2D}, + spectra::ConstantSpectrum, textures::{FloatDotsTexture, SpectrumDotsTexture}, - utils::Transform + utils::Transform, }; use crate::{ core::texture::{ - CreateFloatTexture, CreateSpectrumTexture, FloatTexture, SpectrumTexture }, - utils::{FileLoc, TextureParameterDictionary} + CreateFloatTexture, CreateSpectrumTexture, CreateTextureMapping, FloatTexture, + SpectrumTexture, + }, + utils::{FileLoc, TextureParameterDictionary}, }; impl CreateFloatTexture for FloatDotsTexture { fn create( - _render_from_texture: Transform, - _parameters: TextureParameterDictionary, - _loc: FileLoc, - _arena: &Arena, + render_from_texture: Transform, + parameters: TextureParameterDictionary, + loc: FileLoc, + arena: &Arena, ) -> Result { - todo!() + let map = TextureMapping2D::create(¶meters, &render_from_texture, &loc)?; + let inside = parameters.get_float_texture("inside", 1.)?; + let outside = parameters.get_float_texture("outside", 1.)?; + + let tex = FloatDotsTexture::new(map, arena.upload(inside), arena.upload(outside)); + + Ok(FloatTexture::Dots(tex)) } } impl CreateSpectrumTexture for SpectrumDotsTexture { fn create( - _render_from_texture: Transform, - _parameters: TextureParameterDictionary, - _spectrum_type: SpectrumType, - _loc: FileLoc, - _arena: &Arena, + render_from_texture: Transform, + parameters: TextureParameterDictionary, + spectrum_type: SpectrumType, + loc: FileLoc, + arena: &Arena, ) -> Result { - todo!() + let map = TextureMapping2D::create(¶meters, &render_from_texture, &loc)?; + let zero = Spectrum::Constant(ConstantSpectrum::new(0.)); + let one = Spectrum::Constant(ConstantSpectrum::new(1.)); + + let get = |name: &str, def: Spectrum| { + let t = parameters + .get_spectrum_texture(name, Some(def), spectrum_type) + .expect("default supplied"); + arena.upload(t) + }; + let tex = SpectrumDotsTexture::new(map, get("inside", one), get("outside", zero)); + Ok(SpectrumTexture::Dots(tex)) } } diff --git a/src/textures/fbm.rs b/src/textures/fbm.rs index 2508c4a..54eaf65 100644 --- a/src/textures/fbm.rs +++ b/src/textures/fbm.rs @@ -1,21 +1,26 @@ use crate::Arena; use anyhow::Result; -use shared::core::texture::TextureEvalContext; +use shared::core::texture::{TextureEvalContext, TextureMapping3D}; use shared::{textures::FBmTexture, utils::Transform}; use crate::{ - core::texture::{CreateFloatTexture, FloatTexture }, - utils::{FileLoc, TextureParameterDictionary} + core::texture::{CreateFloatTexture, CreateTextureMapping, FloatTexture}, + utils::{FileLoc, TextureParameterDictionary}, }; impl CreateFloatTexture for FBmTexture { fn create( - _render_from_texture: Transform, - _parameters: TextureParameterDictionary, - _loc: FileLoc, - _arena: &Arena, + render_from_texture: Transform, + parameters: TextureParameterDictionary, + loc: FileLoc, + arena: &Arena, ) -> Result { - todo!() + let map = TextureMapping3D::create(¶meters, &render_from_texture, &loc)?; + let tex = FBmTexture::new( + map, + parameters.get_one_int("octaves", 5)?.try_into().unwrap(), + parameters.get_one_float("roughness", 0.5)?, + ); + Ok(FloatTexture::FBm(tex)) } } -