173 lines
5.3 KiB
Rust
173 lines
5.3 KiB
Rust
use crate::core::geometry::{
|
|
Normal3f, Point2f, Vector2f, Vector3f, VectorLike, abs_cos_theta, cos_phi, cos2_theta, sin_phi,
|
|
tan2_theta,
|
|
};
|
|
use crate::core::pbrt::{Float, PI};
|
|
use crate::spectra::{N_SPECTRUM_SAMPLES, SampledSpectrum};
|
|
use crate::utils::math::{clamp, lerp, safe_sqrt, square};
|
|
use crate::utils::sampling::sample_uniform_disk_polar;
|
|
|
|
use num::complex::Complex;
|
|
|
|
#[repr(C)]
|
|
#[derive(Debug, Default, Clone, Copy)]
|
|
pub struct TrowbridgeReitzDistribution {
|
|
alpha_x: Float,
|
|
alpha_y: Float,
|
|
}
|
|
|
|
impl TrowbridgeReitzDistribution {
|
|
pub fn new(alpha_x: Float, alpha_y: Float) -> Self {
|
|
Self { alpha_x, alpha_y }
|
|
}
|
|
|
|
pub fn d(&self, wm: Vector3f) -> Float {
|
|
let tan2_theta = tan2_theta(wm);
|
|
if tan2_theta.is_infinite() {
|
|
return 0.;
|
|
}
|
|
let cos4_theta = square(cos2_theta(wm));
|
|
let e =
|
|
tan2_theta * (square(cos_phi(wm) / self.alpha_x) + square(sin_phi(wm) / self.alpha_y));
|
|
1.0 / (PI * self.alpha_x * self.alpha_y * cos4_theta * square(1. + e))
|
|
}
|
|
|
|
pub fn effectively_smooth(&self) -> bool {
|
|
self.alpha_x.max(self.alpha_y) < 1e-3
|
|
}
|
|
|
|
pub fn lambda(&self, w: Vector3f) -> Float {
|
|
let tan2_theta = tan2_theta(w);
|
|
if tan2_theta.is_infinite() {
|
|
return 0.;
|
|
}
|
|
let alpha2 = square(cos_phi(w) * self.alpha_x) + square(sin_phi(w) * self.alpha_y);
|
|
((1. + alpha2 * tan2_theta).sqrt() - 1.) / 2.
|
|
}
|
|
|
|
pub fn g(&self, wo: Vector3f, wi: Vector3f) -> Float {
|
|
1. / (1. + self.lambda(wo) + self.lambda(wi))
|
|
}
|
|
|
|
pub fn g1(&self, w: Vector3f) -> Float {
|
|
1. / (1. / self.lambda(w))
|
|
}
|
|
|
|
pub fn d_from_w(&self, w: Vector3f, wm: Vector3f) -> Float {
|
|
self.g1(w) / abs_cos_theta(w) * self.d(wm) * w.dot(wm).abs()
|
|
}
|
|
|
|
pub fn pdf(&self, w: Vector3f, wm: Vector3f) -> Float {
|
|
self.d_from_w(w, wm)
|
|
}
|
|
|
|
pub fn sample_wm(&self, w: Vector3f, u: Point2f) -> Vector3f {
|
|
let mut wh = Vector3f::new(self.alpha_x * w.x(), self.alpha_y * w.y(), w.z()).normalize();
|
|
if wh.z() < 0. {
|
|
wh = -wh;
|
|
}
|
|
let t1 = if wh.z() < 0.99999 {
|
|
Vector3f::new(0., 0., 1.).cross(wh).normalize()
|
|
} else {
|
|
Vector3f::new(1., 0., 0.)
|
|
};
|
|
let t2 = wh.cross(t1);
|
|
let mut p = sample_uniform_disk_polar(u);
|
|
let h = (1. - square(p.x())).sqrt();
|
|
p[1] = lerp((1. + wh.z()) / 2., h, p.y());
|
|
let pz = 0_f32.max(1. - Vector2f::from(p).norm_squared());
|
|
let nh = p.x() * t1 + p.y() * t2 + pz * wh;
|
|
Vector3f::new(
|
|
self.alpha_x * nh.x(),
|
|
self.alpha_y * nh.y(),
|
|
nh.z().max(1e-6),
|
|
)
|
|
.normalize()
|
|
}
|
|
|
|
pub fn roughness_to_alpha(roughness: Float) -> Float {
|
|
roughness.sqrt()
|
|
}
|
|
|
|
pub fn regularize(&mut self) {
|
|
if self.alpha_x < 0.3 {
|
|
self.alpha_x = clamp(2. * self.alpha_x, 0.1, 0.3);
|
|
}
|
|
if self.alpha_y < 0.3 {
|
|
self.alpha_y = clamp(2. * self.alpha_y, 0.1, 0.3);
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn refract(wi: Vector3f, n: Normal3f, eta_ratio: Float) -> Option<(Vector3f, Float)> {
|
|
let mut n_interface = n;
|
|
let mut eta = eta_ratio;
|
|
|
|
let mut cos_theta_i = Vector3f::from(n_interface).dot(wi);
|
|
|
|
if cos_theta_i < 0.0 {
|
|
eta = 1.0 / eta;
|
|
cos_theta_i = -cos_theta_i;
|
|
n_interface = -n_interface;
|
|
}
|
|
|
|
let sin2_theta_i = (1.0 - square(cos_theta_i)).max(0.0_f32);
|
|
let sin2_theta_t = sin2_theta_i / square(eta);
|
|
|
|
// Handle total internal reflection
|
|
if sin2_theta_t >= 1.0 {
|
|
return None;
|
|
}
|
|
|
|
let cos_theta_t = (1.0 - sin2_theta_t).sqrt();
|
|
|
|
let wt = -wi / eta + (cos_theta_i / eta - cos_theta_t) * Vector3f::from(n_interface);
|
|
Some((wt, eta))
|
|
}
|
|
|
|
pub fn reflect(wo: Vector3f, n: Normal3f) -> Vector3f {
|
|
-wo + Vector3f::from(2. * wo.dot(n.into()) * n)
|
|
}
|
|
|
|
pub fn fr_dielectric(cos_theta_i: Float, eta: Float) -> Float {
|
|
let mut cos_safe = clamp(cos_theta_i, -1., 1.);
|
|
let mut eta_corr = eta;
|
|
if cos_theta_i < 0. {
|
|
eta_corr = 1. / eta_corr;
|
|
cos_safe = -cos_safe;
|
|
}
|
|
let sin2_theta_i = 1. - square(cos_safe);
|
|
let sin2_theta_t = sin2_theta_i / square(eta_corr);
|
|
if sin2_theta_t >= 1. {
|
|
return 1.;
|
|
}
|
|
let cos_theta_t = safe_sqrt(1. - sin2_theta_t);
|
|
let r_parl = (eta_corr * cos_safe - cos_theta_t) / (eta_corr * cos_safe + cos_theta_t);
|
|
let r_perp = (cos_safe - eta_corr * cos_theta_t) / (cos_safe + eta_corr * cos_theta_t);
|
|
|
|
(square(r_parl) + square(r_perp)) / 2.
|
|
}
|
|
|
|
pub fn fr_complex(cos_theta_i: Float, eta: Complex<Float>) -> Float {
|
|
let cos_corr = clamp(cos_theta_i, 0., 1.);
|
|
let sin2_theta_i = 1. - square(cos_corr);
|
|
let sin2_theta_t: Complex<Float> = sin2_theta_i / square(eta);
|
|
let cos2_theta_t: Complex<Float> = (1. - sin2_theta_t).sqrt();
|
|
|
|
let r_parl = (eta * cos_corr - cos2_theta_t) / (eta * cos_corr + cos2_theta_t);
|
|
let r_perp = (cos_corr - eta * cos2_theta_t) / (cos_corr + eta * cos2_theta_t);
|
|
|
|
(r_parl.norm() + r_perp.norm()) / 2.
|
|
}
|
|
|
|
pub fn fr_complex_from_spectrum(
|
|
cos_theta_i: Float,
|
|
eta: SampledSpectrum,
|
|
k: SampledSpectrum,
|
|
) -> SampledSpectrum {
|
|
let mut result = SampledSpectrum::default();
|
|
for i in 0..N_SPECTRUM_SAMPLES {
|
|
result[i] = fr_complex(cos_theta_i, Complex::new(eta[i], k[i]));
|
|
}
|
|
result
|
|
}
|