Updated Hair bsdf getter, added enum for clearer variant construction on absorption
This commit is contained in:
parent
0fcfcbd467
commit
f496c6721e
3 changed files with 101 additions and 41 deletions
|
|
@ -8,7 +8,7 @@ use crate::core::scattering::{
|
||||||
TrowbridgeReitzDistribution, fr_complex_from_spectrum, fr_dielectric, fresnel_moment1, reflect,
|
TrowbridgeReitzDistribution, fr_complex_from_spectrum, fr_dielectric, fresnel_moment1, reflect,
|
||||||
refract,
|
refract,
|
||||||
};
|
};
|
||||||
use crate::spectra::{DeviceStandardColorSpaces, RGBUnboundedSpectrum, SampledSpectrum};
|
use crate::spectra::{N_SPECTRUM_SAMPLES, RGBColorSpace, RGBUnboundedSpectrum, SampledSpectrum};
|
||||||
use crate::utils::gpu_array_from_fn;
|
use crate::utils::gpu_array_from_fn;
|
||||||
use crate::utils::math::{
|
use crate::utils::math::{
|
||||||
clamp, fast_exp, i0, lerp, log_i0, radians, safe_acos, safe_asin, safe_sqrt, sample_discrete,
|
clamp, fast_exp, i0, lerp, log_i0, radians, safe_acos, safe_asin, safe_sqrt, sample_discrete,
|
||||||
|
|
@ -17,7 +17,7 @@ use crate::utils::math::{
|
||||||
use crate::utils::sampling::{
|
use crate::utils::sampling::{
|
||||||
cosine_hemisphere_pdf, sample_cosine_hemisphere, sample_trimmed_logistic,
|
cosine_hemisphere_pdf, sample_cosine_hemisphere, sample_trimmed_logistic,
|
||||||
};
|
};
|
||||||
use crate::{Float, INV_2_PI, INV_PI, PI};
|
use crate::{Float, INV_2_PI, INV_PI, PI, Ptr};
|
||||||
use core::any::Any;
|
use core::any::Any;
|
||||||
use num_traits::Float as NumFloat;
|
use num_traits::Float as NumFloat;
|
||||||
|
|
||||||
|
|
@ -34,7 +34,6 @@ pub struct HairBxDF {
|
||||||
pub s: Float,
|
pub s: Float,
|
||||||
pub sin_2k_alpha: [Float; P_MAX],
|
pub sin_2k_alpha: [Float; P_MAX],
|
||||||
pub cos_2k_alpha: [Float; P_MAX],
|
pub cos_2k_alpha: [Float; P_MAX],
|
||||||
pub colorspaces: DeviceStandardColorSpaces,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HairBxDF {
|
impl HairBxDF {
|
||||||
|
|
@ -45,7 +44,6 @@ impl HairBxDF {
|
||||||
beta_m: Float,
|
beta_m: Float,
|
||||||
beta_n: Float,
|
beta_n: Float,
|
||||||
alpha: Float,
|
alpha: Float,
|
||||||
colorspaces: DeviceStandardColorSpaces,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut sin_2k_alpha = [0.; P_MAX];
|
let mut sin_2k_alpha = [0.; P_MAX];
|
||||||
let mut cos_2k_alpha = [0.; P_MAX];
|
let mut cos_2k_alpha = [0.; P_MAX];
|
||||||
|
|
@ -67,7 +65,6 @@ impl HairBxDF {
|
||||||
s: 0.,
|
s: 0.,
|
||||||
sin_2k_alpha,
|
sin_2k_alpha,
|
||||||
cos_2k_alpha,
|
cos_2k_alpha,
|
||||||
colorspaces,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -141,12 +138,25 @@ impl HairBxDF {
|
||||||
pub fn sigma_a_from_concentration(
|
pub fn sigma_a_from_concentration(
|
||||||
ce: Float,
|
ce: Float,
|
||||||
cp: Float,
|
cp: Float,
|
||||||
stdcs: DeviceStandardColorSpaces,
|
srgb: Ptr<RGBColorSpace>,
|
||||||
) -> RGBUnboundedSpectrum {
|
) -> RGBUnboundedSpectrum {
|
||||||
let eumelanin_sigma_a = RGB::new(0.419, 0.697, 1.37);
|
let eumelanin_sigma_a = RGB::new(0.419, 0.697, 1.37);
|
||||||
let pheomelanin_sigma_a = RGB::new(0.187, 0.4, 1.05);
|
let pheomelanin_sigma_a = RGB::new(0.187, 0.4, 1.05);
|
||||||
let sigma_a = ce * eumelanin_sigma_a + cp * pheomelanin_sigma_a;
|
let sigma_a = ce * eumelanin_sigma_a + cp * pheomelanin_sigma_a;
|
||||||
RGBUnboundedSpectrum::new(&stdcs.srgb, sigma_a)
|
RGBUnboundedSpectrum::new(&srgb, sigma_a)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sigma_a_from_reflectance(c: SampledSpectrum, beta_n: Float) -> SampledSpectrum {
|
||||||
|
let mut sigma_a = SampledSpectrum::zero();
|
||||||
|
for i in 0..N_SPECTRUM_SAMPLES {
|
||||||
|
sigma_a[i] = square(
|
||||||
|
c[i].ln()
|
||||||
|
/ (5.969 - 0.215 * beta_n + 2.532 * square(beta_n) - 10.73 * beta_n.powf(3.)
|
||||||
|
+ 5.574 * beta_n.powf(4.)
|
||||||
|
+ 0.245 * beta_n.powf(5.)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
sigma_a
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,46 +11,50 @@ use crate::core::material::{Material, MaterialEvalContext, MaterialTrait};
|
||||||
use crate::core::scattering::TrowbridgeReitzDistribution;
|
use crate::core::scattering::TrowbridgeReitzDistribution;
|
||||||
use crate::core::spectrum::{Spectrum, SpectrumTrait};
|
use crate::core::spectrum::{Spectrum, SpectrumTrait};
|
||||||
use crate::core::texture::{FloatTexture, SpectrumTexture, TextureEvaluator};
|
use crate::core::texture::{FloatTexture, SpectrumTexture, TextureEvaluator};
|
||||||
use crate::spectra::{SampledSpectrum, SampledWavelengths};
|
use crate::spectra::{RGBColorSpace, SampledSpectrum, SampledWavelengths};
|
||||||
use crate::textures::SpectrumMixTexture;
|
use crate::textures::SpectrumMixTexture;
|
||||||
use crate::utils::Ptr;
|
use crate::utils::Ptr;
|
||||||
use crate::utils::math::clamp;
|
use crate::utils::math::clamp;
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub enum HairAbsorption {
|
||||||
|
SigmaA(Ptr<SpectrumTexture>),
|
||||||
|
Color(Ptr<SpectrumTexture>),
|
||||||
|
Melanin {
|
||||||
|
eumelanin: Ptr<FloatTexture>,
|
||||||
|
pheomelanin: Ptr<FloatTexture>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct HairMaterial {
|
pub struct HairMaterial {
|
||||||
pub sigma_a: Ptr<SpectrumTexture>,
|
pub hair_absorption: HairAbsorption,
|
||||||
pub color: Ptr<SpectrumTexture>,
|
|
||||||
pub eumelanin: Ptr<FloatTexture>,
|
|
||||||
pub pheomelanin: Ptr<FloatTexture>,
|
|
||||||
pub eta: Ptr<FloatTexture>,
|
pub eta: Ptr<FloatTexture>,
|
||||||
pub beta_m: Ptr<FloatTexture>,
|
pub beta_m: Ptr<FloatTexture>,
|
||||||
pub beta_n: Ptr<FloatTexture>,
|
pub beta_n: Ptr<FloatTexture>,
|
||||||
pub alpha: Ptr<FloatTexture>,
|
pub alpha: Ptr<FloatTexture>,
|
||||||
|
pub colorspace: Ptr<RGBColorSpace>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HairMaterial {
|
impl HairMaterial {
|
||||||
#[cfg(not(gpu))]
|
#[cfg(not(gpu))]
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
pub fn new(
|
pub fn new(
|
||||||
sigma_a: Ptr<SpectrumTexture>,
|
hair_absorption: HairAbsorption,
|
||||||
color: Ptr<SpectrumTexture>,
|
|
||||||
eumelanin: Ptr<FloatTexture>,
|
|
||||||
pheomelanin: Ptr<FloatTexture>,
|
|
||||||
eta: Ptr<FloatTexture>,
|
eta: Ptr<FloatTexture>,
|
||||||
beta_m: Ptr<FloatTexture>,
|
beta_m: Ptr<FloatTexture>,
|
||||||
beta_n: Ptr<FloatTexture>,
|
beta_n: Ptr<FloatTexture>,
|
||||||
alpha: Ptr<FloatTexture>,
|
alpha: Ptr<FloatTexture>,
|
||||||
|
colorspace: Ptr<RGBColorSpace>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
sigma_a,
|
hair_absorption,
|
||||||
color,
|
|
||||||
eumelanin,
|
|
||||||
pheomelanin,
|
|
||||||
eta,
|
eta,
|
||||||
beta_m,
|
beta_m,
|
||||||
beta_n,
|
beta_n,
|
||||||
alpha,
|
alpha,
|
||||||
|
colorspace,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -58,19 +62,60 @@ impl HairMaterial {
|
||||||
impl MaterialTrait for HairMaterial {
|
impl MaterialTrait for HairMaterial {
|
||||||
fn get_bsdf<T: TextureEvaluator>(
|
fn get_bsdf<T: TextureEvaluator>(
|
||||||
&self,
|
&self,
|
||||||
_tex_eval: &T,
|
tex_eval: &T,
|
||||||
_ctx: &MaterialEvalContext,
|
ctx: &MaterialEvalContext,
|
||||||
_lambda: &mut SampledWavelengths,
|
lambda: &mut SampledWavelengths,
|
||||||
) -> BSDF {
|
) -> BSDF {
|
||||||
todo!()
|
let bm = tex_eval.evaluate_float(&self.beta_m, ctx).clamp(1e-2, 1.0);
|
||||||
|
let bn = tex_eval.evaluate_float(&self.beta_n, ctx).clamp(1e-2, 1.0);
|
||||||
|
let a = tex_eval.evaluate_float(&self.alpha, ctx);
|
||||||
|
let e = tex_eval.evaluate_float(&self.eta, ctx);
|
||||||
|
let sig_a = match self.hair_absorption {
|
||||||
|
// Absorption coefficient, not a reflectance, can be larger than 1
|
||||||
|
HairAbsorption::SigmaA(sigma_a) => {
|
||||||
|
SampledSpectrum::clamp_zero(&tex_eval.evaluate_spectrum(&sigma_a, ctx, lambda))
|
||||||
|
}
|
||||||
|
HairAbsorption::Color(color) => {
|
||||||
|
let c = SampledSpectrum::clamp(
|
||||||
|
&tex_eval.evaluate_spectrum(&color, ctx, lambda),
|
||||||
|
0.,
|
||||||
|
1.,
|
||||||
|
);
|
||||||
|
HairBxDF::sigma_a_from_reflectance(c, bn)
|
||||||
|
}
|
||||||
|
HairAbsorption::Melanin {
|
||||||
|
eumelanin,
|
||||||
|
pheomelanin,
|
||||||
|
} => {
|
||||||
|
debug_assert!(!eumelanin.is_null() || !pheomelanin.is_null());
|
||||||
|
let eu = if !eumelanin.is_null() {
|
||||||
|
tex_eval.evaluate_float(&eumelanin, ctx)
|
||||||
|
} else {
|
||||||
|
0.
|
||||||
|
};
|
||||||
|
let pheo = if !pheomelanin.is_null() {
|
||||||
|
tex_eval.evaluate_float(&pheomelanin, ctx)
|
||||||
|
} else {
|
||||||
|
0.
|
||||||
|
};
|
||||||
|
|
||||||
|
HairBxDF::sigma_a_from_concentration(eu.max(0.0), pheo.max(0.0), self.colorspace)
|
||||||
|
.sample(lambda)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let h = -1. + 2. * ctx.uv[1];
|
||||||
|
let bxdf = BxDF::Hair(HairBxDF::new(h, e, sig_a, bm, bn, a));
|
||||||
|
BSDF::new(ctx.ns, ctx.dpdus, bxdf)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_bssrdf<T>(
|
fn get_bssrdf<T>(
|
||||||
&self,
|
&self,
|
||||||
_tex_eval: &T,
|
_tex_eval: &T,
|
||||||
_ctx: &MaterialEvalContext,
|
_ctx: &MaterialEvalContext,
|
||||||
_lambda: &SampledWavelengths,
|
_lambda: &SampledWavelengths,
|
||||||
) -> Option<BSSRDF> {
|
) -> Option<BSSRDF> {
|
||||||
todo!()
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn can_evaluate_textures(&self, _tex_eval: &dyn TextureEvaluator) -> bool {
|
fn can_evaluate_textures(&self, _tex_eval: &dyn TextureEvaluator) -> bool {
|
||||||
|
|
|
||||||
|
|
@ -29,31 +29,36 @@ impl CreateMaterial for HairMaterial {
|
||||||
let eumelanin = parameters.get_float_texture_or_null("eumelanin")?;
|
let eumelanin = parameters.get_float_texture_or_null("eumelanin")?;
|
||||||
let pheomelanin = parameters.get_float_texture_or_null("pheomelanin")?;
|
let pheomelanin = parameters.get_float_texture_or_null("pheomelanin")?;
|
||||||
let has_melanin = eumelanin.is_some() || pheomelanin.is_some();
|
let has_melanin = eumelanin.is_some() || pheomelanin.is_some();
|
||||||
|
|
||||||
let sigma_a = if sigma_a.is_none() && reflectance.is_none() && !has_melanin {
|
|
||||||
let stdcs = get_colorspace_device();
|
|
||||||
let default_rgb = HairBxDF::sigma_a_from_concentration(1.3, 0.0, stdcs);
|
|
||||||
let spectrum = Spectrum::RGBUnbounded(default_rgb);
|
|
||||||
let texture = SpectrumTexture::Constant(SpectrumConstantTexture::new(spectrum));
|
|
||||||
Some(Arc::new(texture))
|
|
||||||
} else {
|
|
||||||
sigma_a
|
|
||||||
};
|
|
||||||
|
|
||||||
let eta = parameters.get_float_texture("eta", 1.55)?;
|
let eta = parameters.get_float_texture("eta", 1.55)?;
|
||||||
let beta_m = parameters.get_float_texture("beta_m", 0.3)?;
|
let beta_m = parameters.get_float_texture("beta_m", 0.3)?;
|
||||||
let beta_n = parameters.get_float_texture("beta_n", 0.3)?;
|
let beta_n = parameters.get_float_texture("beta_n", 0.3)?;
|
||||||
let alpha = parameters.get_float_texture("alpha", 2.)?;
|
let alpha = parameters.get_float_texture("alpha", 2.)?;
|
||||||
|
|
||||||
|
let stdcs = get_colorspace_device();
|
||||||
|
|
||||||
|
let absorption = if let Some(s) = sigma_a {
|
||||||
|
HairAbsorption::SigmaA(arena.upload(s))
|
||||||
|
} else if let Some(r) = reflectance {
|
||||||
|
HairAbsorption::Color(arena.upload(r))
|
||||||
|
} else if has_melanin {
|
||||||
|
HairAbsorption::Melanin {
|
||||||
|
eumelanin: arena.upload(eumelanin),
|
||||||
|
pheomelanin: arena.upload(pheomelanin),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let default_rgb = HairBxDF::sigma_a_from_concentration(1.3, 0.0, stdcs.srgb);
|
||||||
|
let spectrum = Spectrum::RGBUnbounded(default_rgb);
|
||||||
|
let texture = SpectrumTexture::Constant(SpectrumConstantTexture::new(spectrum));
|
||||||
|
HairAbsorption::SigmaA(arena.upload(Arc::new(texture)))
|
||||||
|
};
|
||||||
|
|
||||||
let material = HairMaterial::new(
|
let material = HairMaterial::new(
|
||||||
arena.upload(sigma_a),
|
absorption,
|
||||||
arena.upload(reflectance),
|
|
||||||
arena.upload(eumelanin),
|
|
||||||
arena.upload(pheomelanin),
|
|
||||||
arena.upload(eta),
|
arena.upload(eta),
|
||||||
arena.upload(beta_m),
|
arena.upload(beta_m),
|
||||||
arena.upload(beta_n),
|
arena.upload(beta_n),
|
||||||
arena.upload(alpha),
|
arena.upload(alpha),
|
||||||
|
stdcs.srgb,
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(Material::Hair(material))
|
Ok(Material::Hair(material))
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue