pbrt/shared/src/materials/coated.rs

347 lines
10 KiB
Rust

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<DeviceImage>,
pub displacement: Ptr<GPUFloatTexture>,
pub reflectance: Ptr<GPUSpectrumTexture>,
pub albedo: Ptr<GPUSpectrumTexture>,
pub u_roughness: Ptr<GPUFloatTexture>,
pub v_roughness: Ptr<GPUFloatTexture>,
pub thickness: Ptr<GPUFloatTexture>,
pub g: Ptr<GPUFloatTexture>,
pub eta: Ptr<Spectrum>,
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<GPUSpectrumTexture>,
u_roughness: Ptr<GPUFloatTexture>,
v_roughness: Ptr<GPUFloatTexture>,
thickness: Ptr<GPUFloatTexture>,
albedo: Ptr<GPUSpectrumTexture>,
g: Ptr<GPUFloatTexture>,
eta: Ptr<Spectrum>,
displacement: Ptr<GPUFloatTexture>,
normal_map: Ptr<DeviceImage>,
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<T: TextureEvaluator>(
&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<T>(
&self,
_tex_eval: &T,
_ctx: &MaterialEvalContext,
_lambda: &SampledWavelengths,
) -> Option<BSSRDF> {
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<GPUFloatTexture> {
self.displacement
}
fn has_subsurface_scattering(&self) -> bool {
false
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct CoatedConductorMaterial {
normal_map: Ptr<DeviceImage>,
displacement: Ptr<GPUFloatTexture>,
interface_uroughness: Ptr<GPUFloatTexture>,
interface_vroughness: Ptr<GPUFloatTexture>,
thickness: Ptr<GPUFloatTexture>,
interface_eta: Ptr<Spectrum>,
g: Ptr<GPUFloatTexture>,
albedo: Ptr<GPUSpectrumTexture>,
conductor_uroughness: Ptr<GPUFloatTexture>,
conductor_vroughness: Ptr<GPUFloatTexture>,
conductor_eta: Ptr<GPUSpectrumTexture>,
k: Ptr<GPUSpectrumTexture>,
reflectance: Ptr<GPUSpectrumTexture>,
max_depth: u32,
n_samples: u32,
remap_roughness: bool,
seed: i32,
}
impl CoatedConductorMaterial {
#[allow(clippy::too_many_arguments)]
pub fn new(
normal_map: Ptr<DeviceImage>,
displacement: Ptr<GPUFloatTexture>,
interface_uroughness: Ptr<GPUFloatTexture>,
interface_vroughness: Ptr<GPUFloatTexture>,
thickness: Ptr<GPUFloatTexture>,
interface_eta: Ptr<Spectrum>,
g: Ptr<GPUFloatTexture>,
albedo: Ptr<GPUSpectrumTexture>,
conductor_uroughness: Ptr<GPUFloatTexture>,
conductor_vroughness: Ptr<GPUFloatTexture>,
conductor_eta: Ptr<GPUSpectrumTexture>,
k: Ptr<GPUSpectrumTexture>,
reflectance: Ptr<GPUSpectrumTexture>,
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<T: TextureEvaluator>(
&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<T>(
&self,
_tex_eval: &T,
_ctx: &MaterialEvalContext,
_lambda: &SampledWavelengths,
) -> Option<BSSRDF> {
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<GPUFloatTexture> {
self.displacement
}
fn has_subsurface_scattering(&self) -> bool {
false
}
}