use crate::bxdfs::{ CoatedConductorBxDF, CoatedDiffuseBxDF, ConductorBxDF, DielectricBxDF, DiffuseBxDF, }; use crate::core::bsdf::BSDF; use crate::core::bssrdf::BSSRDF; use crate::core::bxdf::BxDF; use crate::core::image::DeviceImage; use crate::core::material::{Material, MaterialEvalContext, MaterialTrait}; use crate::core::scattering::TrowbridgeReitzDistribution; use crate::core::spectrum::{Spectrum, SpectrumTrait}; use crate::core::texture::{GPUFloatTexture, GPUSpectrumTexture, TextureEvaluator}; use crate::spectra::{SampledSpectrum, SampledWavelengths}; use crate::utils::Ptr; use crate::utils::math::clamp; #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct CoatedDiffuseMaterial { pub normal_map: Ptr, pub displacement: Ptr, pub reflectance: Ptr, pub albedo: Ptr, pub u_roughness: Ptr, pub v_roughness: Ptr, pub thickness: Ptr, pub g: Ptr, pub eta: Ptr, pub max_depth: u32, pub n_samples: u32, pub remap_roughness: bool, pub seed: i32, } impl CoatedDiffuseMaterial { #[allow(clippy::too_many_arguments)] pub fn new( reflectance: Ptr, u_roughness: Ptr, v_roughness: Ptr, thickness: Ptr, albedo: Ptr, g: Ptr, eta: Ptr, displacement: Ptr, normal_map: Ptr, remap_roughness: bool, max_depth: u32, n_samples: u32, seed: i32, ) -> Self { Self { displacement, normal_map, reflectance, albedo, u_roughness, v_roughness, thickness, g, eta, remap_roughness, max_depth, n_samples, seed, } } } impl MaterialTrait for CoatedDiffuseMaterial { fn get_bsdf( &self, tex_eval: &T, ctx: &MaterialEvalContext, lambda: &SampledWavelengths, ) -> BSDF { let r = SampledSpectrum::clamp( &tex_eval.evaluate_spectrum(&self.reflectance, ctx, lambda), 0., 1., ); let mut u_rough = tex_eval.evaluate_float(&self.u_roughness, ctx); let mut v_rough = tex_eval.evaluate_float(&self.v_roughness, ctx); if self.remap_roughness { u_rough = TrowbridgeReitzDistribution::roughness_to_alpha(u_rough); v_rough = TrowbridgeReitzDistribution::roughness_to_alpha(v_rough); } let distrib = TrowbridgeReitzDistribution::new(u_rough, v_rough); let thick = tex_eval.evaluate_float(&self.thickness, ctx); let mut sampled_eta = self.eta.evaluate(lambda[0]); if self.eta.is_constant() { let mut lambda = *lambda; lambda.terminate_secondary_inplace(); } if sampled_eta == 0. { sampled_eta = 1. } let a = SampledSpectrum::clamp( &tex_eval.evaluate_spectrum(&self.albedo, ctx, lambda), 0., 1., ); let gg = clamp(tex_eval.evaluate_float(&self.g, ctx), -1., 1.); let bxdf = BxDF::CoatedDiffuse(CoatedDiffuseBxDF::new( DielectricBxDF::new(sampled_eta, distrib), DiffuseBxDF::new(r), thick, a, gg, self.max_depth, self.n_samples, self.seed, )); BSDF::new(ctx.ns, ctx.dpdus, Ptr::from(&bxdf)) } fn get_bssrdf( &self, _tex_eval: &T, _ctx: &MaterialEvalContext, _lambda: &SampledWavelengths, ) -> Option { None } fn can_evaluate_textures(&self, tex_eval: &dyn TextureEvaluator) -> bool { tex_eval.can_evaluate( &[self.u_roughness, self.v_roughness, self.thickness, self.g], &[self.reflectance, self.albedo], ) } fn get_normal_map(&self) -> Option<&DeviceImage> { Some(&*self.normal_map) } fn get_displacement(&self) -> Ptr { self.displacement } fn has_subsurface_scattering(&self) -> bool { false } } #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct CoatedConductorMaterial { normal_map: Ptr, displacement: Ptr, interface_uroughness: Ptr, interface_vroughness: Ptr, thickness: Ptr, interface_eta: Ptr, g: Ptr, albedo: Ptr, conductor_uroughness: Ptr, conductor_vroughness: Ptr, conductor_eta: Ptr, k: Ptr, reflectance: Ptr, max_depth: u32, n_samples: u32, remap_roughness: bool, seed: i32, } impl CoatedConductorMaterial { #[allow(clippy::too_many_arguments)] pub fn new( normal_map: Ptr, displacement: Ptr, interface_uroughness: Ptr, interface_vroughness: Ptr, thickness: Ptr, interface_eta: Ptr, g: Ptr, albedo: Ptr, conductor_uroughness: Ptr, conductor_vroughness: Ptr, conductor_eta: Ptr, k: Ptr, reflectance: Ptr, max_depth: u32, n_samples: u32, remap_roughness: bool, seed: i32, ) -> Self { Self { displacement, normal_map, interface_uroughness, interface_vroughness, thickness, interface_eta, g, albedo, conductor_uroughness, conductor_vroughness, conductor_eta, k, reflectance, remap_roughness, max_depth, n_samples, seed, } } } impl MaterialTrait for CoatedConductorMaterial { fn get_bsdf( &self, tex_eval: &T, ctx: &MaterialEvalContext, lambda: &SampledWavelengths, ) -> BSDF { let mut iurough = tex_eval.evaluate_float(&self.interface_uroughness, ctx); let mut ivrough = tex_eval.evaluate_float(&self.interface_vroughness, ctx); if self.remap_roughness { iurough = TrowbridgeReitzDistribution::roughness_to_alpha(iurough); ivrough = TrowbridgeReitzDistribution::roughness_to_alpha(ivrough); } let interface_distrib = TrowbridgeReitzDistribution::new(iurough, ivrough); let thick = tex_eval.evaluate_float(&self.thickness, ctx); let mut ieta = self.interface_eta.evaluate(lambda[0]); if self.interface_eta.is_constant() { let mut lambda = *lambda; lambda.terminate_secondary_inplace(); } if ieta == 0. { ieta = 1.; } let (mut ce, mut ck) = if !self.conductor_eta.is_null() { let k_tex = self.k; let ce = tex_eval.evaluate_spectrum(&self.conductor_eta, ctx, lambda); let ck = tex_eval.evaluate_spectrum(unsafe { k_tex.as_ref() }, ctx, lambda); (ce, ck) } else { let r = SampledSpectrum::clamp( &tex_eval.evaluate_spectrum(&self.reflectance, ctx, lambda), 0., 0.9999, ); let ce = SampledSpectrum::new(1.0); let one_minus_r = SampledSpectrum::new(1.) - r; let ck = 2. * r.sqrt() / SampledSpectrum::clamp_zero(&one_minus_r).sqrt(); (ce, ck) }; ce /= ieta; ck /= ieta; let mut curough = tex_eval.evaluate_float(&self.conductor_uroughness, ctx); let mut cvrough = tex_eval.evaluate_float(&self.conductor_vroughness, ctx); if self.remap_roughness { curough = TrowbridgeReitzDistribution::roughness_to_alpha(curough); cvrough = TrowbridgeReitzDistribution::roughness_to_alpha(cvrough); } let conductor_distrib = TrowbridgeReitzDistribution::new(curough, cvrough); let a = SampledSpectrum::clamp( &tex_eval.evaluate_spectrum(&self.albedo, ctx, lambda), 0., 1., ); let gg = clamp(tex_eval.evaluate_float(&self.g, ctx), -1., 1.); let bxdf = BxDF::CoatedConductor(CoatedConductorBxDF::new( DielectricBxDF::new(ieta, interface_distrib), ConductorBxDF::new(&conductor_distrib, ce, ck), thick, a, gg, self.max_depth, self.n_samples, self.seed, )); BSDF::new(ctx.ns, ctx.dpdus, Ptr::from(&bxdf)) } fn get_bssrdf( &self, _tex_eval: &T, _ctx: &MaterialEvalContext, _lambda: &SampledWavelengths, ) -> Option { None } fn can_evaluate_textures(&self, tex_eval: &dyn TextureEvaluator) -> bool { let float_textures = [ self.interface_uroughness, self.interface_vroughness, self.thickness, self.g, self.conductor_uroughness, self.conductor_vroughness, ]; let mut spectrum_textures = [Ptr::null(); 4]; let mut n = 0; spectrum_textures[n] = self.albedo; n += 1; if !self.conductor_eta.is_null() { spectrum_textures[n] = self.conductor_eta; n += 1; } if !self.k.is_null() { spectrum_textures[n] = self.k; n += 1; } if !self.conductor_eta.is_null() { spectrum_textures[n] = self.reflectance; } tex_eval.can_evaluate(&float_textures, &spectrum_textures) } fn get_normal_map(&self) -> Option<&DeviceImage> { Some(&*self.normal_map) } fn get_displacement(&self) -> Ptr { self.displacement } fn has_subsurface_scattering(&self) -> bool { false } }