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 { let cos_corr = clamp(cos_theta_i, 0., 1.); let sin2_theta_i = 1. - square(cos_corr); let sin2_theta_t: Complex = sin2_theta_i / square(eta); let cos2_theta_t: Complex = (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 }