240 lines
7.1 KiB
Rust
240 lines
7.1 KiB
Rust
use crate::core::bsdf::BSDFSample;
|
|
use crate::core::bxdf::{BxDFFlags, BxDFReflTransFlags, BxDFTrait, FArgs, TransportMode};
|
|
use crate::core::geometry::{
|
|
Point2f, Vector3f, VectorLike, abs_cos_theta, cos_theta, same_hemisphere, spherical_direction,
|
|
spherical_theta,
|
|
};
|
|
use crate::core::scattering::reflect;
|
|
use crate::spectra::{SampledSpectrum, SampledWavelengths};
|
|
use crate::utils::math::square;
|
|
use crate::utils::ptr::Ptr;
|
|
use crate::utils::sampling::{PiecewiseLinear2D, cosine_hemisphere_pdf, sample_cosine_hemisphere};
|
|
use crate::{Float, INV_PI, PI, PI_OVER_2};
|
|
use core::any::Any;
|
|
|
|
#[repr(C)]
|
|
#[derive(Debug, Copy, Clone)]
|
|
pub struct MeasuredBxDFData {
|
|
pub wavelengths: Ptr<Float>,
|
|
pub spectra: PiecewiseLinear2D<3>,
|
|
pub ndf: PiecewiseLinear2D<0>,
|
|
pub vndf: PiecewiseLinear2D<2>,
|
|
pub sigma: PiecewiseLinear2D<0>,
|
|
pub isotropic: bool,
|
|
pub luminance: PiecewiseLinear2D<2>,
|
|
}
|
|
|
|
#[repr(C)]
|
|
#[derive(Debug, Copy, Clone)]
|
|
pub struct MeasuredBxDF {
|
|
pub brdf: Ptr<MeasuredBxDFData>,
|
|
pub lambda: SampledWavelengths,
|
|
}
|
|
|
|
unsafe impl Send for MeasuredBxDF {}
|
|
unsafe impl Sync for MeasuredBxDF {}
|
|
|
|
impl MeasuredBxDF {
|
|
pub fn new(brdf: &MeasuredBxDFData, lambda: &SampledWavelengths) -> Self {
|
|
Self {
|
|
brdf: Ptr::from(brdf),
|
|
lambda: *lambda,
|
|
}
|
|
}
|
|
|
|
pub fn theta2u(theta: Float) -> Float {
|
|
(theta * (2. / PI)).sqrt()
|
|
}
|
|
|
|
pub fn phi2u(phi: Float) -> Float {
|
|
phi * 1. / (2. * PI) + 0.5
|
|
}
|
|
|
|
pub fn u2theta(u: Float) -> Float {
|
|
square(u) * PI_OVER_2
|
|
}
|
|
|
|
pub fn u2phi(u: Float) -> Float {
|
|
(2. * u - 1.) * PI
|
|
}
|
|
}
|
|
|
|
impl BxDFTrait for MeasuredBxDF {
|
|
fn flags(&self) -> BxDFFlags {
|
|
BxDFFlags::REFLECTION | BxDFFlags::GLOSSY
|
|
}
|
|
|
|
fn f(&self, wo: Vector3f, wi: Vector3f, _mode: TransportMode) -> SampledSpectrum {
|
|
if !same_hemisphere(wo, wi) {
|
|
return SampledSpectrum::new(0.);
|
|
}
|
|
|
|
let mut wo_curr = wo;
|
|
let mut wi_curr = wi;
|
|
if wo.z() < 0. {
|
|
wo_curr = -wo_curr;
|
|
wi_curr = -wi_curr;
|
|
}
|
|
|
|
// Get half direction vector
|
|
let wm_curr = wi_curr + wo_curr;
|
|
if wm_curr.norm_squared() == 0. {
|
|
return SampledSpectrum::new(0.);
|
|
}
|
|
let wm = wm_curr.normalize();
|
|
|
|
// Map vectors to unit square
|
|
let theta_o = spherical_theta(wo_curr);
|
|
let phi_o = wo_curr.y().atan2(wo_curr.x());
|
|
let theta_m = spherical_theta(wm);
|
|
let phi_m = wm.y().atan2(wm.x());
|
|
let u_wo = Point2f::new(MeasuredBxDF::theta2u(theta_o), MeasuredBxDF::phi2u(phi_o));
|
|
let u_wm_phi = if self.brdf.isotropic {
|
|
phi_m - phi_o
|
|
} else {
|
|
phi_m
|
|
};
|
|
let mut u_wm = Point2f::new(
|
|
MeasuredBxDF::theta2u(theta_m),
|
|
MeasuredBxDF::phi2u(u_wm_phi),
|
|
);
|
|
u_wm[1] -= u_wm[1].floor();
|
|
|
|
// Inverse parametrization
|
|
let ui = self.brdf.vndf.invert(u_wm, [phi_o, theta_o]);
|
|
let fr = SampledSpectrum::from_fn(|i| {
|
|
self.brdf
|
|
.spectra
|
|
.evaluate(ui.p, [phi_o, theta_o, self.lambda[i]])
|
|
.max(0.0)
|
|
});
|
|
|
|
fr * self.brdf.ndf.evaluate(u_wm, [])
|
|
/ (4. * self.brdf.sigma.evaluate(u_wo, []) * cos_theta(wi))
|
|
}
|
|
|
|
fn pdf(&self, wo: Vector3f, wi: Vector3f, f_args: FArgs) -> Float {
|
|
let reflection_flags =
|
|
BxDFReflTransFlags::from_bits_truncate(BxDFReflTransFlags::REFLECTION.bits());
|
|
if !f_args.sample_flags.contains(reflection_flags) {
|
|
return 0.;
|
|
}
|
|
if !same_hemisphere(wo, wi) {
|
|
return 0.;
|
|
}
|
|
|
|
let mut wo_curr = wo;
|
|
let mut wi_curr = wi;
|
|
if wo.z() < 0. {
|
|
wo_curr = -wo_curr;
|
|
wi_curr = -wi_curr;
|
|
}
|
|
|
|
let wm_curr = wi_curr + wo_curr;
|
|
if wm_curr.norm_squared() == 0. {
|
|
return 0.;
|
|
}
|
|
let wm = wm_curr.normalize();
|
|
let theta_o = spherical_theta(wo_curr);
|
|
let phi_o = wo_curr.y().atan2(wo_curr.x());
|
|
let theta_m = spherical_theta(wm);
|
|
let phi_m = wm.y().atan2(wm.x());
|
|
|
|
let u_wm_phi = if self.brdf.isotropic {
|
|
phi_m - phi_o
|
|
} else {
|
|
phi_m
|
|
};
|
|
|
|
let mut u_wm = Point2f::new(
|
|
MeasuredBxDF::theta2u(theta_m),
|
|
MeasuredBxDF::phi2u(u_wm_phi),
|
|
);
|
|
u_wm[1] = u_wm[1] - u_wm[1].floor();
|
|
|
|
let ui = self.brdf.vndf.invert(u_wm, [phi_o, theta_o]);
|
|
let sample = ui.p;
|
|
let vndf_pdf = ui.pdf;
|
|
|
|
let pdf = self.brdf.luminance.evaluate(sample, [phi_o, theta_o]);
|
|
let sin_theta_m = (square(wm.x()) + square(wm.y())).sqrt();
|
|
let jacobian = 4. * wm.dot(wo) * f32::max(2. * square(PI) * u_wm.x() * sin_theta_m, 1e-6);
|
|
vndf_pdf * pdf / jacobian
|
|
}
|
|
|
|
fn sample_f(&self, wo: Vector3f, _uc: Float, u: Point2f, f_args: FArgs) -> Option<BSDFSample> {
|
|
let reflection_flags =
|
|
BxDFReflTransFlags::from_bits_truncate(BxDFReflTransFlags::REFLECTION.bits());
|
|
if !f_args.sample_flags.contains(reflection_flags) {
|
|
return None;
|
|
}
|
|
|
|
let mut flip_w = false;
|
|
let mut wo_curr = wo;
|
|
if wo.z() <= 0. {
|
|
wo_curr = -wo_curr;
|
|
flip_w = true;
|
|
}
|
|
|
|
let theta_o = spherical_theta(wo_curr);
|
|
let phi_o = wo_curr.y().atan2(wo_curr.x());
|
|
// Warp sample using luminance distribution
|
|
let mut s = self.brdf.luminance.sample(u, [phi_o, theta_o]);
|
|
let u = s.p;
|
|
let lum_pdf = s.pdf;
|
|
|
|
// Sample visible normal distribution of measured BRDF
|
|
s = self.brdf.vndf.sample(u, [phi_o, theta_o]);
|
|
let u_wm = s.p;
|
|
let mut pdf = s.pdf;
|
|
|
|
// Map from microfacet normal to incident direction
|
|
let mut phi_m = MeasuredBxDF::u2phi(u_wm.y());
|
|
let theta_m = MeasuredBxDF::u2theta(u_wm.x());
|
|
if self.brdf.isotropic {
|
|
phi_m += phi_o;
|
|
}
|
|
let sin_theta_m = theta_m.sin();
|
|
let cos_theta_m = theta_m.cos();
|
|
let wm = spherical_direction(sin_theta_m, cos_theta_m, phi_m);
|
|
let mut wi = reflect(wo_curr, wm.into());
|
|
if wi.z() <= 0. {
|
|
return None;
|
|
}
|
|
|
|
// Interpolate spectral BRDF
|
|
let mut f = SampledSpectrum::from_fn(|i| {
|
|
self.brdf
|
|
.spectra
|
|
.evaluate(u, [phi_o, theta_o, self.lambda[i]])
|
|
.max(0.0)
|
|
});
|
|
|
|
let u_wo = Point2f::new(MeasuredBxDF::theta2u(theta_o), MeasuredBxDF::phi2u(phi_o));
|
|
f *= self.brdf.ndf.evaluate(u_wm, [])
|
|
/ (4. * self.brdf.sigma.evaluate(u_wo, []) * abs_cos_theta(wi));
|
|
pdf /= 4. * wm.dot(wo_curr) * f32::max(2. * square(PI) * u_wm.x(), 1e-6);
|
|
|
|
if flip_w {
|
|
wi = -wi;
|
|
}
|
|
|
|
let bsdf = BSDFSample {
|
|
f,
|
|
wi,
|
|
pdf: pdf * lum_pdf,
|
|
flags: BxDFFlags::GLOSSY_REFLECTION,
|
|
..Default::default()
|
|
};
|
|
|
|
Some(bsdf)
|
|
}
|
|
|
|
fn as_any(&self) -> &dyn Any {
|
|
self
|
|
}
|
|
|
|
fn regularize(&mut self) {
|
|
return;
|
|
}
|
|
}
|