896 lines
24 KiB
Rust
896 lines
24 KiB
Rust
use bumpalo::Bump;
|
|
use enum_dispatch::enum_dispatch;
|
|
use std::ops::Deref;
|
|
use std::sync::Arc;
|
|
|
|
use crate::core::bssrdf::BSSRDF;
|
|
use crate::core::bxdf::{
|
|
BSDF, BxDF, CoatedConductorBxDF, CoatedDiffuseBxDF, ConductorBxDF, DielectricBxDF, DiffuseBxDF,
|
|
};
|
|
use crate::core::geometry::{Frame, Normal3f, Point2f, Point3f, Vector2f, Vector3f, VectorLike};
|
|
use crate::core::interaction::InteractionTrait;
|
|
use crate::core::interaction::{Interaction, Shadinggeom, SurfaceInteraction};
|
|
use crate::core::pbrt::Float;
|
|
use crate::core::scattering::TrowbridgeReitzDistribution;
|
|
use crate::core::texture::{
|
|
FloatTexture, FloatTextureTrait, SpectrumTexture, TextureEvalContext, TextureEvaluator,
|
|
};
|
|
use crate::image::{Image, WrapMode, WrapMode2D};
|
|
use crate::spectra::{SampledSpectrum, SampledWavelengths, Spectrum, SpectrumProvider};
|
|
use crate::utils::hash::hash_float;
|
|
use crate::utils::math::clamp;
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct MaterialEvalContext {
|
|
pub texture: TextureEvalContext,
|
|
pub wo: Vector3f,
|
|
pub ns: Normal3f,
|
|
pub dpdus: Vector3f,
|
|
}
|
|
|
|
impl Deref for MaterialEvalContext {
|
|
type Target = TextureEvalContext;
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
&self.texture
|
|
}
|
|
}
|
|
|
|
impl From<&SurfaceInteraction> for MaterialEvalContext {
|
|
fn from(si: &SurfaceInteraction) -> Self {
|
|
Self {
|
|
texture: TextureEvalContext::from(si),
|
|
wo: si.common.wo,
|
|
ns: si.shading.n,
|
|
dpdus: si.shading.dpdu,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug, Default)]
|
|
pub struct NormalBumpEvalContext {
|
|
p: Point3f,
|
|
uv: Point2f,
|
|
n: Normal3f,
|
|
shading: Shadinggeom,
|
|
dpdx: Vector3f,
|
|
dpdy: Vector3f,
|
|
// All 0
|
|
dudx: Float,
|
|
dudy: Float,
|
|
dvdx: Float,
|
|
dvdy: Float,
|
|
face_index: usize,
|
|
}
|
|
|
|
impl From<&SurfaceInteraction> for NormalBumpEvalContext {
|
|
fn from(si: &SurfaceInteraction) -> Self {
|
|
Self {
|
|
p: si.p(),
|
|
uv: si.uv,
|
|
n: si.n(),
|
|
shading: si.shading.clone(),
|
|
dudx: si.dudx,
|
|
dudy: si.dudy,
|
|
dvdx: si.dvdx,
|
|
dvdy: si.dvdy,
|
|
dpdx: si.dpdx,
|
|
dpdy: si.dpdy,
|
|
face_index: si.face_index,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<&NormalBumpEvalContext> for TextureEvalContext {
|
|
fn from(ctx: &NormalBumpEvalContext) -> Self {
|
|
Self {
|
|
p: ctx.p,
|
|
uv: ctx.uv,
|
|
n: ctx.n,
|
|
dpdx: ctx.dpdx,
|
|
dpdy: ctx.dpdy,
|
|
dudx: ctx.dudx,
|
|
dudy: ctx.dudy,
|
|
dvdx: ctx.dvdx,
|
|
dvdy: ctx.dvdy,
|
|
face_index: ctx.face_index,
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn normal_map(normal_map: &Image, ctx: &NormalBumpEvalContext) -> (Vector3f, Vector3f) {
|
|
let wrap = WrapMode2D::from(WrapMode::Repeat);
|
|
let uv = Point2f::new(ctx.uv[0], 1. - ctx.uv[1]);
|
|
let r = normal_map.bilerp_channel_with_wrap(uv, 0, wrap);
|
|
let g = normal_map.bilerp_channel_with_wrap(uv, 1, wrap);
|
|
let b = normal_map.bilerp_channel_with_wrap(uv, 2, wrap);
|
|
let mut ns = Vector3f::new(2.0 * r - 1.0, 2.0 * g - 1.0, 2.0 * b - 1.0);
|
|
ns = ns.normalize();
|
|
let frame = Frame::from_xz(ctx.shading.dpdu.normalize(), Vector3f::from(ctx.shading.n));
|
|
ns = frame.from_local(ns);
|
|
let ulen = ctx.shading.dpdu.norm();
|
|
let vlen = ctx.shading.dpdv.norm();
|
|
let dpdu = ctx.shading.dpdu.gram_schmidt(ns).normalize() * ulen;
|
|
let dpdv = ctx.shading.dpdu.cross(dpdu).normalize() * vlen;
|
|
(dpdu, dpdv)
|
|
}
|
|
|
|
pub fn bump_map<T: TextureEvaluator>(
|
|
tex_eval: &T,
|
|
displacement: &FloatTexture,
|
|
ctx: &NormalBumpEvalContext,
|
|
) -> (Vector3f, Vector3f) {
|
|
debug_assert!(tex_eval.can_evaluate(&[displacement], &[]));
|
|
let mut du = 0.5 * (ctx.dudx.abs() + ctx.dudy.abs());
|
|
if du == 0.0 {
|
|
du = 0.0005;
|
|
}
|
|
|
|
let mut dv = 0.5 * (ctx.dvdx.abs() + ctx.dvdy.abs());
|
|
if dv == 0.0 {
|
|
dv = 0.0005;
|
|
}
|
|
let mut shifted_ctx = TextureEvalContext::from(ctx);
|
|
shifted_ctx.p = ctx.p + ctx.shading.dpdu * du;
|
|
shifted_ctx.uv = ctx.uv + Vector2f::new(du, 0.0);
|
|
let u_displace = tex_eval.evaluate_float(displacement, &shifted_ctx);
|
|
shifted_ctx.p = ctx.p + ctx.shading.dpdv * dv;
|
|
shifted_ctx.uv = ctx.uv + Vector2f::new(0.0, dv);
|
|
let v_displace = tex_eval.evaluate_float(displacement, &shifted_ctx);
|
|
let center_ctx = TextureEvalContext::from(ctx);
|
|
let displace = tex_eval.evaluate_float(displacement, ¢er_ctx);
|
|
|
|
let d_displace_du = (u_displace - displace) / du;
|
|
let d_displace_dv = (v_displace - displace) / dv;
|
|
let n_vec = Vector3f::from(ctx.shading.n);
|
|
let dndu_vec = Vector3f::from(ctx.shading.dndu);
|
|
let dndv_vec = Vector3f::from(ctx.shading.dndv);
|
|
|
|
let dpdu = ctx.shading.dpdu + n_vec * d_displace_du + dndu_vec * displace;
|
|
let dpdv = ctx.shading.dpdv + n_vec * d_displace_dv + dndv_vec * displace;
|
|
(dpdu, dpdv)
|
|
}
|
|
|
|
#[enum_dispatch]
|
|
pub trait MaterialTrait: Send + Sync + std::fmt::Debug {
|
|
fn get_bsdf<T: TextureEvaluator>(
|
|
&self,
|
|
tex_eval: &T,
|
|
ctx: &MaterialEvalContext,
|
|
lambda: &SampledWavelengths,
|
|
) -> BSDF;
|
|
|
|
fn get_bssrdf<T: TextureEvaluator>(
|
|
&self,
|
|
tex_eval: &T,
|
|
ctx: &MaterialEvalContext,
|
|
lambda: &SampledWavelengths,
|
|
) -> Option<BSSRDF>;
|
|
|
|
fn can_evaluate_textures(&self, tex_eval: &dyn TextureEvaluator) -> bool;
|
|
fn get_normal_map(&self) -> Option<Arc<Image>>;
|
|
fn get_displacement(&self) -> Option<FloatTexture>;
|
|
fn has_surface_scattering(&self) -> bool;
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
#[enum_dispatch(MaterialTrait)]
|
|
pub enum Material {
|
|
CoatedDiffuse(CoatedDiffuseMaterial),
|
|
CoatedConductor(CoatedConductorMaterial),
|
|
Conductor(ConductorMaterial),
|
|
Dielectric(DielectricMaterial),
|
|
Diffuse(DiffuseMaterial),
|
|
DiffuseTransmission(DiffuseTransmissionMaterial),
|
|
Hair(HairMaterial),
|
|
Measured(MeasuredMaterial),
|
|
Subsurface(SubsurfaceMaterial),
|
|
ThinDielectric(ThinDielectricMaterial),
|
|
Mix(MixMaterial),
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct CoatedDiffuseMaterial {
|
|
displacement: FloatTexture,
|
|
normal_map: Option<Arc<Image>>,
|
|
reflectance: SpectrumTexture,
|
|
albedo: SpectrumTexture,
|
|
u_roughness: FloatTexture,
|
|
v_roughness: FloatTexture,
|
|
thickness: FloatTexture,
|
|
g: FloatTexture,
|
|
eta: Spectrum,
|
|
remap_roughness: bool,
|
|
max_depth: usize,
|
|
n_samples: usize,
|
|
}
|
|
|
|
impl CoatedDiffuseMaterial {
|
|
#[allow(clippy::too_many_arguments)]
|
|
pub fn new(
|
|
reflectance: SpectrumTexture,
|
|
u_roughness: FloatTexture,
|
|
v_roughness: FloatTexture,
|
|
thickness: FloatTexture,
|
|
albedo: SpectrumTexture,
|
|
g: FloatTexture,
|
|
eta: Spectrum,
|
|
displacement: FloatTexture,
|
|
normal_map: Option<Arc<Image>>,
|
|
remap_roughness: bool,
|
|
max_depth: usize,
|
|
n_samples: usize,
|
|
) -> Self {
|
|
Self {
|
|
displacement,
|
|
normal_map,
|
|
reflectance,
|
|
albedo,
|
|
u_roughness,
|
|
v_roughness,
|
|
thickness,
|
|
g,
|
|
eta,
|
|
remap_roughness,
|
|
max_depth,
|
|
n_samples,
|
|
}
|
|
}
|
|
}
|
|
|
|
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,
|
|
));
|
|
|
|
BSDF::new(ctx.ns, ctx.dpdus, Some(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<Arc<Image>> {
|
|
self.normal_map.clone()
|
|
}
|
|
|
|
fn get_displacement(&self) -> Option<FloatTexture> {
|
|
Some(self.displacement.clone())
|
|
}
|
|
|
|
fn has_surface_scattering(&self) -> bool {
|
|
false
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct CoatedConductorMaterial {
|
|
displacement: FloatTexture,
|
|
normal_map: Option<Arc<Image>>,
|
|
interface_uroughness: FloatTexture,
|
|
interface_vroughness: FloatTexture,
|
|
thickness: FloatTexture,
|
|
interface_eta: Spectrum,
|
|
g: FloatTexture,
|
|
albedo: SpectrumTexture,
|
|
conductor_uroughness: FloatTexture,
|
|
conductor_vroughness: FloatTexture,
|
|
conductor_eta: Option<SpectrumTexture>,
|
|
k: Option<SpectrumTexture>,
|
|
reflectance: SpectrumTexture,
|
|
remap_roughness: bool,
|
|
max_depth: usize,
|
|
n_samples: usize,
|
|
}
|
|
|
|
impl CoatedConductorMaterial {
|
|
#[allow(clippy::too_many_arguments)]
|
|
pub fn new(
|
|
displacement: FloatTexture,
|
|
normal_map: Option<Arc<Image>>,
|
|
interface_uroughness: FloatTexture,
|
|
interface_vroughness: FloatTexture,
|
|
thickness: FloatTexture,
|
|
interface_eta: Spectrum,
|
|
g: FloatTexture,
|
|
albedo: SpectrumTexture,
|
|
conductor_uroughness: FloatTexture,
|
|
conductor_vroughness: FloatTexture,
|
|
conductor_eta: Option<SpectrumTexture>,
|
|
k: Option<SpectrumTexture>,
|
|
reflectance: SpectrumTexture,
|
|
remap_roughness: bool,
|
|
max_depth: usize,
|
|
n_samples: usize,
|
|
) -> 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,
|
|
}
|
|
}
|
|
}
|
|
|
|
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 let Some(eta_tex) = &self.conductor_eta {
|
|
let k_tex = self
|
|
.k
|
|
.as_ref()
|
|
.expect("CoatedConductor: 'k' must be provided if 'conductor_eta' is present");
|
|
let ce = tex_eval.evaluate_spectrum(eta_tex, ctx, lambda);
|
|
let ck = tex_eval.evaluate_spectrum(k_tex, 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,
|
|
));
|
|
BSDF::new(ctx.ns, ctx.dpdus, Some(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 = Vec::with_capacity(4);
|
|
|
|
spectrum_textures.push(&self.albedo);
|
|
|
|
if let Some(eta) = &self.conductor_eta {
|
|
spectrum_textures.push(eta);
|
|
}
|
|
if let Some(k) = &self.k {
|
|
spectrum_textures.push(k);
|
|
}
|
|
|
|
if self.conductor_eta.is_none() {
|
|
spectrum_textures.push(&self.reflectance);
|
|
}
|
|
|
|
tex_eval.can_evaluate(&float_textures, &spectrum_textures)
|
|
}
|
|
|
|
fn get_normal_map(&self) -> Option<Arc<Image>> {
|
|
self.normal_map.clone()
|
|
}
|
|
|
|
fn get_displacement(&self) -> Option<FloatTexture> {
|
|
Some(self.displacement.clone())
|
|
}
|
|
|
|
fn has_surface_scattering(&self) -> bool {
|
|
false
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct ConductorMaterial;
|
|
impl MaterialTrait for ConductorMaterial {
|
|
fn get_bsdf<T: TextureEvaluator>(
|
|
&self,
|
|
_tex_eval: &T,
|
|
_ctx: &MaterialEvalContext,
|
|
_lambda: &SampledWavelengths,
|
|
) -> BSDF {
|
|
todo!()
|
|
}
|
|
fn get_bssrdf<T>(
|
|
&self,
|
|
_tex_eval: &T,
|
|
_ctx: &MaterialEvalContext,
|
|
_lambda: &SampledWavelengths,
|
|
) -> Option<BSSRDF> {
|
|
todo!()
|
|
}
|
|
fn can_evaluate_textures(&self, _tex_eval: &dyn TextureEvaluator) -> bool {
|
|
todo!()
|
|
}
|
|
fn get_normal_map(&self) -> Option<Arc<Image>> {
|
|
todo!()
|
|
}
|
|
fn get_displacement(&self) -> Option<FloatTexture> {
|
|
todo!()
|
|
}
|
|
fn has_surface_scattering(&self) -> bool {
|
|
todo!()
|
|
}
|
|
}
|
|
#[derive(Clone, Debug)]
|
|
pub struct DielectricMaterial {
|
|
normal_map: Option<Arc<Image>>,
|
|
displacement: FloatTexture,
|
|
u_roughness: FloatTexture,
|
|
v_roughness: FloatTexture,
|
|
remap_roughness: bool,
|
|
eta: Spectrum,
|
|
}
|
|
|
|
impl MaterialTrait for DielectricMaterial {
|
|
fn get_bsdf<T: TextureEvaluator>(
|
|
&self,
|
|
tex_eval: &T,
|
|
ctx: &MaterialEvalContext,
|
|
lambda: &SampledWavelengths,
|
|
) -> BSDF {
|
|
let mut sampled_eta = self.eta.evaluate(lambda[0]);
|
|
if !self.eta.is_constant() {
|
|
lambda.terminate_secondary();
|
|
}
|
|
|
|
if sampled_eta == 0.0 {
|
|
sampled_eta = 1.0;
|
|
}
|
|
|
|
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 bxdf = BxDF::Dielectric(DielectricBxDF::new(sampled_eta, distrib));
|
|
|
|
BSDF::new(ctx.ns, ctx.dpdus, Some(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], &[])
|
|
}
|
|
|
|
fn get_normal_map(&self) -> Option<Arc<Image>> {
|
|
self.normal_map.clone()
|
|
}
|
|
|
|
fn get_displacement(&self) -> Option<FloatTexture> {
|
|
Some(self.displacement.clone())
|
|
}
|
|
|
|
fn has_surface_scattering(&self) -> bool {
|
|
false
|
|
}
|
|
}
|
|
#[derive(Clone, Debug)]
|
|
pub struct DiffuseMaterial {
|
|
normal_map: Option<Arc<Image>>,
|
|
displacement: FloatTexture,
|
|
reflectance: SpectrumTexture,
|
|
}
|
|
|
|
impl MaterialTrait for DiffuseMaterial {
|
|
fn get_bsdf<T: TextureEvaluator>(
|
|
&self,
|
|
tex_eval: &T,
|
|
ctx: &MaterialEvalContext,
|
|
lambda: &SampledWavelengths,
|
|
) -> BSDF {
|
|
let r = tex_eval.evaluate_spectrum(&self.reflectance, ctx, lambda);
|
|
let bxdf = BxDF::Diffuse(DiffuseBxDF::new(r));
|
|
BSDF::new(ctx.ns, ctx.dpdus, Some(bxdf))
|
|
}
|
|
|
|
fn get_bssrdf<T>(
|
|
&self,
|
|
_tex_eval: &T,
|
|
_ctx: &MaterialEvalContext,
|
|
_lambda: &SampledWavelengths,
|
|
) -> Option<BSSRDF> {
|
|
todo!()
|
|
}
|
|
|
|
fn can_evaluate_textures(&self, tex_eval: &dyn TextureEvaluator) -> bool {
|
|
tex_eval.can_evaluate(&[], &[&self.reflectance])
|
|
}
|
|
|
|
fn get_normal_map(&self) -> Option<Arc<Image>> {
|
|
self.normal_map.clone()
|
|
}
|
|
|
|
fn get_displacement(&self) -> Option<FloatTexture> {
|
|
Some(self.displacement.clone())
|
|
}
|
|
|
|
fn has_surface_scattering(&self) -> bool {
|
|
false
|
|
}
|
|
}
|
|
#[derive(Clone, Debug)]
|
|
pub struct DiffuseTransmissionMaterial;
|
|
impl MaterialTrait for DiffuseTransmissionMaterial {
|
|
fn get_bsdf<T: TextureEvaluator>(
|
|
&self,
|
|
_tex_eval: &T,
|
|
_ctx: &MaterialEvalContext,
|
|
_lambda: &SampledWavelengths,
|
|
) -> BSDF {
|
|
todo!()
|
|
}
|
|
fn get_bssrdf<T>(
|
|
&self,
|
|
_tex_eval: &T,
|
|
_ctx: &MaterialEvalContext,
|
|
_lambda: &SampledWavelengths,
|
|
) -> Option<BSSRDF> {
|
|
todo!()
|
|
}
|
|
|
|
fn can_evaluate_textures(&self, _tex_eval: &dyn TextureEvaluator) -> bool {
|
|
todo!()
|
|
}
|
|
|
|
fn get_normal_map(&self) -> Option<Arc<Image>> {
|
|
todo!()
|
|
}
|
|
|
|
fn get_displacement(&self) -> Option<FloatTexture> {
|
|
todo!()
|
|
}
|
|
|
|
fn has_surface_scattering(&self) -> bool {
|
|
todo!()
|
|
}
|
|
}
|
|
#[derive(Clone, Debug)]
|
|
pub struct HairMaterial;
|
|
impl MaterialTrait for HairMaterial {
|
|
fn get_bsdf<T: TextureEvaluator>(
|
|
&self,
|
|
_tex_eval: &T,
|
|
_ctx: &MaterialEvalContext,
|
|
_lambda: &SampledWavelengths,
|
|
) -> BSDF {
|
|
todo!()
|
|
}
|
|
fn get_bssrdf<T>(
|
|
&self,
|
|
_tex_eval: &T,
|
|
_ctx: &MaterialEvalContext,
|
|
_lambda: &SampledWavelengths,
|
|
) -> Option<BSSRDF> {
|
|
todo!()
|
|
}
|
|
|
|
fn can_evaluate_textures(&self, _tex_eval: &dyn TextureEvaluator) -> bool {
|
|
todo!()
|
|
}
|
|
fn get_normal_map(&self) -> Option<Arc<Image>> {
|
|
todo!()
|
|
}
|
|
fn get_displacement(&self) -> Option<FloatTexture> {
|
|
todo!()
|
|
}
|
|
fn has_surface_scattering(&self) -> bool {
|
|
todo!()
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct MeasuredMaterial;
|
|
impl MaterialTrait for MeasuredMaterial {
|
|
fn get_bsdf<T: TextureEvaluator>(
|
|
&self,
|
|
_tex_eval: &T,
|
|
_ctx: &MaterialEvalContext,
|
|
_lambda: &SampledWavelengths,
|
|
) -> BSDF {
|
|
todo!()
|
|
}
|
|
fn get_bssrdf<T>(
|
|
&self,
|
|
_tex_eval: &T,
|
|
_ctx: &MaterialEvalContext,
|
|
_lambda: &SampledWavelengths,
|
|
) -> Option<BSSRDF> {
|
|
todo!()
|
|
}
|
|
|
|
fn can_evaluate_textures(&self, _tex_eval: &dyn TextureEvaluator) -> bool {
|
|
todo!()
|
|
}
|
|
fn get_normal_map(&self) -> Option<Arc<Image>> {
|
|
todo!()
|
|
}
|
|
fn get_displacement(&self) -> Option<FloatTexture> {
|
|
todo!()
|
|
}
|
|
fn has_surface_scattering(&self) -> bool {
|
|
todo!()
|
|
}
|
|
}
|
|
#[derive(Clone, Debug)]
|
|
pub struct SubsurfaceMaterial;
|
|
impl MaterialTrait for SubsurfaceMaterial {
|
|
fn get_bsdf<T: TextureEvaluator>(
|
|
&self,
|
|
_tex_eval: &T,
|
|
_ctx: &MaterialEvalContext,
|
|
_lambda: &SampledWavelengths,
|
|
) -> BSDF {
|
|
todo!()
|
|
}
|
|
fn get_bssrdf<T>(
|
|
&self,
|
|
_tex_eval: &T,
|
|
_ctx: &MaterialEvalContext,
|
|
_lambda: &SampledWavelengths,
|
|
) -> Option<BSSRDF> {
|
|
todo!()
|
|
}
|
|
|
|
fn can_evaluate_textures(&self, _tex_eval: &dyn TextureEvaluator) -> bool {
|
|
todo!()
|
|
}
|
|
fn get_normal_map(&self) -> Option<Arc<Image>> {
|
|
todo!()
|
|
}
|
|
fn get_displacement(&self) -> Option<FloatTexture> {
|
|
todo!()
|
|
}
|
|
fn has_surface_scattering(&self) -> bool {
|
|
todo!()
|
|
}
|
|
}
|
|
#[derive(Clone, Debug)]
|
|
pub struct ThinDielectricMaterial;
|
|
impl MaterialTrait for ThinDielectricMaterial {
|
|
fn get_bsdf<T: TextureEvaluator>(
|
|
&self,
|
|
_tex_eval: &T,
|
|
_ctx: &MaterialEvalContext,
|
|
_lambda: &SampledWavelengths,
|
|
) -> BSDF {
|
|
todo!()
|
|
}
|
|
fn get_bssrdf<T>(
|
|
&self,
|
|
_tex_eval: &T,
|
|
_ctx: &MaterialEvalContext,
|
|
_lambda: &SampledWavelengths,
|
|
) -> Option<BSSRDF> {
|
|
todo!()
|
|
}
|
|
fn can_evaluate_textures(&self, _tex_eval: &dyn TextureEvaluator) -> bool {
|
|
todo!()
|
|
}
|
|
fn get_normal_map(&self) -> Option<Arc<Image>> {
|
|
todo!()
|
|
}
|
|
fn get_displacement(&self) -> Option<FloatTexture> {
|
|
todo!()
|
|
}
|
|
fn has_surface_scattering(&self) -> bool {
|
|
todo!()
|
|
}
|
|
}
|
|
#[derive(Clone, Debug)]
|
|
pub struct MixMaterial {
|
|
pub amount: FloatTexture,
|
|
pub materials: [Box<Material>; 2],
|
|
}
|
|
|
|
impl MixMaterial {
|
|
pub fn choose_material<T: TextureEvaluator>(
|
|
&self,
|
|
tex_eval: &T,
|
|
ctx: &MaterialEvalContext,
|
|
) -> &Material {
|
|
let amt = tex_eval.evaluate_float(&self.amount, ctx);
|
|
|
|
if amt <= 0.0 {
|
|
return &self.materials[0];
|
|
}
|
|
if amt >= 1.0 {
|
|
return &self.materials[1];
|
|
}
|
|
|
|
let u = hash_float(&(ctx.p, ctx.wo));
|
|
|
|
if amt < u {
|
|
&self.materials[0]
|
|
} else {
|
|
&self.materials[1]
|
|
}
|
|
}
|
|
}
|
|
|
|
impl MaterialTrait for MixMaterial {
|
|
fn get_bsdf<T: TextureEvaluator>(
|
|
&self,
|
|
tex_eval: &T,
|
|
ctx: &MaterialEvalContext,
|
|
lambda: &SampledWavelengths,
|
|
) -> BSDF {
|
|
let chosen_mat = self.choose_material(tex_eval, ctx);
|
|
chosen_mat.get_bsdf(tex_eval, ctx, lambda)
|
|
}
|
|
|
|
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.amount], &[])
|
|
}
|
|
|
|
fn get_normal_map(&self) -> Option<Arc<Image>> {
|
|
None
|
|
}
|
|
|
|
fn get_displacement(&self) -> Option<FloatTexture> {
|
|
panic!(
|
|
"MixMaterial::get_displacement() shouldn't be called. \
|
|
Displacement is not supported on Mix materials directly."
|
|
);
|
|
}
|
|
|
|
fn has_surface_scattering(&self) -> bool {
|
|
false
|
|
}
|
|
}
|