Everything is broken
This commit is contained in:
parent
96e437921f
commit
d42437e860
18 changed files with 1617 additions and 312 deletions
286
src/core/bssrdf.rs
Normal file
286
src/core/bssrdf.rs
Normal file
|
|
@ -0,0 +1,286 @@
|
|||
use crate::core::bxdf::BSDF;
|
||||
use crate::core::interaction::SurfaceInteraction;
|
||||
use crate::core::pbrt::{Float, PI};
|
||||
use crate::geometry::{Frame, Normal3f, Point2f, Point3f, Point3fi, Vector3f};
|
||||
use crate::spectra::{N_SPECTRUM_SAMPLES, SampledSpectrum};
|
||||
use crate::utils::math::{catmull_rom_weights, square};
|
||||
use crate::utils::sampling::sample_catmull_rom_2d;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct BSSRDFSample {
|
||||
sp: SampledSpectrum,
|
||||
pdf: SampledSpectrum,
|
||||
sw: BSDF,
|
||||
wo: Vector3f,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SubsurfaceInteraction {
|
||||
pi: Point3fi,
|
||||
n: Normal3f,
|
||||
ns: Normal3f,
|
||||
dpdu: Vector3f,
|
||||
dpdv: Vector3f,
|
||||
dpdus: Vector3f,
|
||||
dpdvs: Vector3f,
|
||||
}
|
||||
|
||||
impl SubsurfaceInteraction {
|
||||
pub fn new(si: &SurfaceInteraction) -> Self {
|
||||
Self {
|
||||
pi: si.common.pi,
|
||||
n: si.common.n,
|
||||
dpdu: si.dpdu,
|
||||
dpdv: si.dpdv,
|
||||
ns: si.shading.n,
|
||||
dpdus: si.shading.dpdu,
|
||||
dpdvs: si.shading.dpdv,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn p(&self) -> Point3f {
|
||||
self.pi.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SurfaceInteraction> for SubsurfaceInteraction {
|
||||
fn from(si: SurfaceInteraction) -> SubsurfaceInteraction {
|
||||
SubsurfaceInteraction {
|
||||
pi: si.common.pi,
|
||||
n: si.common.n,
|
||||
ns: si.shading.n,
|
||||
dpdu: si.dpdu,
|
||||
dpdv: si.dpdv,
|
||||
dpdus: si.shading.dpdu,
|
||||
dpdvs: si.shading.dpdv,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct BSSRDFTable {
|
||||
rho_samples: Vec<Float>,
|
||||
radius_samples: Vec<Float>,
|
||||
profile: Vec<Float>,
|
||||
rho_eff: Vec<Float>,
|
||||
profile_cdf: Vec<Float>,
|
||||
}
|
||||
|
||||
impl BSSRDFTable {
|
||||
pub fn new(n_rho_samples: usize, n_radius_samples: usize) -> Self {
|
||||
let rho_samples: Vec<Float> = Vec::with_capacity(n_rho_samples);
|
||||
let radius_samples: Vec<Float> = Vec::with_capacity(n_radius_samples);
|
||||
let profile: Vec<Float> = Vec::with_capacity(n_radius_samples * n_rho_samples);
|
||||
let rho_eff: Vec<Float> = Vec::with_capacity(n_rho_samples);
|
||||
let profile_cdf: Vec<Float> = Vec::with_capacity(n_radius_samples * n_rho_samples);
|
||||
Self {
|
||||
rho_samples,
|
||||
radius_samples,
|
||||
profile,
|
||||
rho_eff,
|
||||
profile_cdf,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eval_profile(&self, rho_index: usize, radius_index: usize) -> Float {
|
||||
assert!(rho_index < self.rho_samples.len());
|
||||
assert!(radius_index < self.radius_samples.len());
|
||||
self.profile[rho_index * self.radius_samples.len() + radius_index]
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Debug)]
|
||||
pub struct BSSRDFProbeSegment {
|
||||
p0: Point3f,
|
||||
p1: Point3f,
|
||||
}
|
||||
|
||||
pub trait BSSRDFTrait: Send + Sync + std::fmt::Debug {
|
||||
fn sample_sp(&self, u1: Float, u2: Point2f) -> Option<BSSRDFProbeSegment>;
|
||||
fn probe_intersection_to_sample(si: &SubsurfaceInteraction) -> BSSRDFSample;
|
||||
}
|
||||
|
||||
pub enum BSSRDF<'a> {
|
||||
Tabulated(TabulatedBSSRDF<'a>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct TabulatedBSSRDF<'a> {
|
||||
po: Point3f,
|
||||
wo: Vector3f,
|
||||
ns: Normal3f,
|
||||
eta: Float,
|
||||
sigma_t: SampledSpectrum,
|
||||
rho: SampledSpectrum,
|
||||
table: &'a BSSRDFTable,
|
||||
}
|
||||
|
||||
impl<'a> TabulatedBSSRDF<'a> {
|
||||
pub fn new(
|
||||
po: Point3f,
|
||||
wo: Vector3f,
|
||||
ns: Normal3f,
|
||||
eta: Float,
|
||||
sigma_a: &SampledSpectrum,
|
||||
sigma_s: &SampledSpectrum,
|
||||
table: &'a BSSRDFTable,
|
||||
) -> Self {
|
||||
let sigma_t = *sigma_a + *sigma_s;
|
||||
let rho = SampledSpectrum::safe_div(sigma_s, &sigma_t);
|
||||
Self {
|
||||
po,
|
||||
wo,
|
||||
ns,
|
||||
eta,
|
||||
table,
|
||||
sigma_t,
|
||||
rho,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sp(&self, pi: Point3f) -> SampledSpectrum {
|
||||
self.sr(self.po.distance(pi))
|
||||
}
|
||||
|
||||
pub fn sr(&self, r: Float) -> SampledSpectrum {
|
||||
let mut sr_spectrum = SampledSpectrum::new(0.);
|
||||
for i in 0..N_SPECTRUM_SAMPLES {
|
||||
let r_optical = r * self.sigma_t[i];
|
||||
let (rho_offset, rho_weights) =
|
||||
match catmull_rom_weights(&self.table.rho_samples, self.rho[i]) {
|
||||
Some(res) => res,
|
||||
None => continue,
|
||||
};
|
||||
|
||||
let (radius_offset, radius_weights) =
|
||||
match catmull_rom_weights(&self.table.radius_samples, r_optical) {
|
||||
Some(res) => res,
|
||||
None => continue,
|
||||
};
|
||||
|
||||
let mut sr = 0.;
|
||||
for (j, rho_weight) in rho_weights.iter().enumerate() {
|
||||
for (k, radius_weight) in radius_weights.iter().enumerate() {
|
||||
let weight = rho_weight * radius_weight;
|
||||
if weight != 0. {
|
||||
sr += weight * self.table.eval_profile(rho_offset + j, radius_offset + k);
|
||||
}
|
||||
}
|
||||
}
|
||||
if r_optical != 0. {
|
||||
sr /= 2. * PI * r_optical;
|
||||
}
|
||||
sr_spectrum[i] = sr;
|
||||
}
|
||||
|
||||
sr_spectrum *= self.sigma_t * self.sigma_t;
|
||||
SampledSpectrum::clamp_zero(&sr_spectrum)
|
||||
}
|
||||
|
||||
pub fn sample_sr(&self, u: Float) -> Option<Float> {
|
||||
if self.sigma_t[0] == 0. {
|
||||
return None;
|
||||
}
|
||||
let (ret, _, _) = sample_catmull_rom_2d(
|
||||
&self.table.rho_samples,
|
||||
&self.table.radius_samples,
|
||||
&self.table.profile,
|
||||
&self.table.profile_cdf,
|
||||
self.rho[0],
|
||||
u,
|
||||
);
|
||||
Some(ret / self.sigma_t[0])
|
||||
}
|
||||
|
||||
pub fn pdf_sr(&self, r: Float) -> SampledSpectrum {
|
||||
let mut pdf = SampledSpectrum::new(0.);
|
||||
for i in 0..N_SPECTRUM_SAMPLES {
|
||||
let r_optical = r * self.sigma_t[i];
|
||||
let (rho_offset, rho_weights) =
|
||||
match catmull_rom_weights(&self.table.rho_samples, self.rho[i]) {
|
||||
Some(res) => res,
|
||||
None => continue,
|
||||
};
|
||||
|
||||
let (radius_offset, radius_weights) =
|
||||
match catmull_rom_weights(&self.table.radius_samples, r_optical) {
|
||||
Some(res) => res,
|
||||
None => continue,
|
||||
};
|
||||
|
||||
let mut sr = 0.;
|
||||
let mut rho_eff = 0.;
|
||||
for (j, rho_weight) in rho_weights.iter().enumerate() {
|
||||
if *rho_weight != 0. {
|
||||
// Update _rhoEff_ and _sr_ for wavelength
|
||||
// We use 'j' for the offset calculation and 'rho_weight' for calculation
|
||||
rho_eff += self.table.rho_eff[rho_offset + j] * rho_weight;
|
||||
|
||||
// Fix: Use .iter().enumerate() for 'k'
|
||||
for (k, radius_weight) in radius_weights.iter().enumerate() {
|
||||
if *radius_weight != 0. {
|
||||
sr += self.table.eval_profile(rho_offset + j, radius_offset + k)
|
||||
* rho_weight
|
||||
* radius_weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Cancel marginal PDF factor from tabulated BSSRDF profile
|
||||
if r_optical != 0. {
|
||||
sr /= 2. * PI * r_optical;
|
||||
}
|
||||
pdf[i] = sr * square(self.sigma_t[i]) / rho_eff;
|
||||
}
|
||||
|
||||
SampledSpectrum::clamp_zero(&pdf)
|
||||
}
|
||||
|
||||
pub fn pdf_sp(&self, pi: Point3f, ni: Normal3f) -> SampledSpectrum {
|
||||
let d = pi - self.po;
|
||||
let f = Frame::from_z(self.ns.into());
|
||||
let d_local = f.to_local(d);
|
||||
let n_local = f.to_local(ni.into());
|
||||
let r_proj = [
|
||||
(square(d_local.y() + square(d_local.z()))).sqrt(),
|
||||
(square(d_local.z() + square(d_local.x()))).sqrt(),
|
||||
(square(d_local.x() + square(d_local.y()))).sqrt(),
|
||||
];
|
||||
|
||||
let axis_prob = [0.25, 0.25, 0.25];
|
||||
let mut pdf = SampledSpectrum::new(0.);
|
||||
for axis in 0..3 {
|
||||
pdf += self.pdf_sr(r_proj[axis] * n_local[axis].abs() * axis_prob[axis]);
|
||||
}
|
||||
pdf
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> BSSRDFTrait for TabulatedBSSRDF<'a> {
|
||||
fn sample_sp(&self, u1: Float, u2: Point2f) -> Option<BSSRDFProbeSegment> {
|
||||
let f = if u1 < 0.25 {
|
||||
Frame::from_x(self.ns.into())
|
||||
} else if u1 < 0.5 {
|
||||
Frame::from_y(self.ns.into())
|
||||
} else {
|
||||
Frame::from_z(self.ns.into())
|
||||
};
|
||||
|
||||
let r = self.sample_sr(u2[0])?;
|
||||
let phi = 2. * PI * u2[1];
|
||||
let r_max = self.sample_sr(0.999)?;
|
||||
let l = 2. * (square(r_max) - square(r)).sqrt();
|
||||
let p_start = self.po + r * (f.x * phi.cos() + f.y * phi.sin()) - l * f.z / 2.;
|
||||
let p_target = p_start + l * f.z;
|
||||
Some(BSSRDFProbeSegment {
|
||||
p0: p_start,
|
||||
p1: p_target,
|
||||
})
|
||||
}
|
||||
|
||||
fn probe_intersection_to_sample(_si: &SubsurfaceInteraction) -> BSSRDFSample {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
364
src/core/bxdf.rs
364
src/core/bxdf.rs
|
|
@ -173,6 +173,12 @@ pub struct DiffuseBxDF {
|
|||
pub r: SampledSpectrum,
|
||||
}
|
||||
|
||||
impl DiffuseBxDF {
|
||||
pub fn new(r: SampledSpectrum) -> Self {
|
||||
Self { r }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DiffuseTransmissionBxDF;
|
||||
|
||||
|
|
@ -407,16 +413,16 @@ impl Default for FArgs {
|
|||
pub trait BxDFTrait: Any + Send + Sync + std::fmt::Debug {
|
||||
fn flags(&self) -> BxDFFlags;
|
||||
|
||||
fn f(&self, wo: &Vector3f, wi: &Vector3f, mode: TransportMode) -> SampledSpectrum;
|
||||
fn f(&self, wo: Vector3f, wi: Vector3f, mode: TransportMode) -> SampledSpectrum;
|
||||
|
||||
fn sample_f(&self, wo: &Vector3f, uc: Float, u: Point2f, f_args: FArgs) -> Option<BSDFSample>;
|
||||
fn sample_f(&self, wo: Vector3f, uc: Float, u: Point2f, f_args: FArgs) -> Option<BSDFSample>;
|
||||
|
||||
fn pdf(&self, wo: &Vector3f, wi: &Vector3f, f_args: FArgs) -> Float;
|
||||
fn pdf(&self, wo: Vector3f, wi: Vector3f, f_args: FArgs) -> Float;
|
||||
|
||||
fn rho_wo(&self, wo: Vector3f, uc: &[Float], u2: &[Point2f]) -> SampledSpectrum {
|
||||
let mut r = SampledSpectrum::new(0.);
|
||||
for i in 0..uc.len() {
|
||||
if let Some(bs) = self.sample_f(&wo, uc[i], u2[i], FArgs::default()) {
|
||||
if let Some(bs) = self.sample_f(wo, uc[i], u2[i], FArgs::default()) {
|
||||
r += bs.f * abs_cos_theta(bs.wi) / bs.pdf;
|
||||
}
|
||||
}
|
||||
|
|
@ -431,7 +437,7 @@ pub trait BxDFTrait: Any + Send + Sync + std::fmt::Debug {
|
|||
continue;
|
||||
}
|
||||
let pdfo = uniform_hemisphere_pdf();
|
||||
if let Some(bs) = self.sample_f(&wo, uc[i], u2[i], FArgs::default()) {
|
||||
if let Some(bs) = self.sample_f(wo, uc[i], u2[i], FArgs::default()) {
|
||||
r += bs.f * abs_cos_theta(bs.wi) * abs_cos_theta(wo) / (pdfo * bs.pdf);
|
||||
}
|
||||
}
|
||||
|
|
@ -448,19 +454,19 @@ pub trait BxDFTrait: Any + Send + Sync + std::fmt::Debug {
|
|||
#[derive(Debug)]
|
||||
pub struct EmptyBxDF;
|
||||
impl BxDFTrait for EmptyBxDF {
|
||||
fn f(&self, _wo: &Vector3f, _wi: &Vector3f, _mode: TransportMode) -> SampledSpectrum {
|
||||
fn f(&self, _wo: Vector3f, _wi: Vector3f, _mode: TransportMode) -> SampledSpectrum {
|
||||
SampledSpectrum::default()
|
||||
}
|
||||
fn sample_f(
|
||||
&self,
|
||||
_wo: &Vector3f,
|
||||
_wo: Vector3f,
|
||||
_u: Float,
|
||||
_u2: Point2f,
|
||||
_f_args: FArgs,
|
||||
) -> Option<BSDFSample> {
|
||||
None
|
||||
}
|
||||
fn pdf(&self, _wo: &Vector3f, _wi: &Vector3f, _f_args: FArgs) -> Float {
|
||||
fn pdf(&self, _wo: Vector3f, _wi: Vector3f, _f_args: FArgs) -> Float {
|
||||
0.0
|
||||
}
|
||||
fn flags(&self) -> BxDFFlags {
|
||||
|
|
@ -486,23 +492,23 @@ pub enum BxDF {
|
|||
// DiffuseTransmission(DiffuseTransmissionBxDF),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct BSDF {
|
||||
bxdf: Arc<RwLock<dyn BxDFTrait>>,
|
||||
#[derive(Debug)]
|
||||
pub struct BSDF<'a> {
|
||||
bxdf: Option<&'a mut dyn BxDFTrait>,
|
||||
shading_frame: Frame,
|
||||
}
|
||||
|
||||
impl Default for BSDF {
|
||||
impl<'a> Default for BSDF<'a> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
bxdf: Arc::new(RwLock::new(EmptyBxDF)),
|
||||
bxdf: None,
|
||||
shading_frame: Frame::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BSDF {
|
||||
pub fn new(ns: Normal3f, dpdus: Vector3f, bxdf: Arc<RwLock<dyn BxDFTrait>>) -> Self {
|
||||
impl<'a> BSDF<'a> {
|
||||
pub fn new(ns: Normal3f, dpdus: Vector3f, bxdf: Option<&'a mut dyn BxDFTrait>) -> Self {
|
||||
Self {
|
||||
bxdf,
|
||||
shading_frame: Frame::new(dpdus.normalize(), Vector3f::from(ns)),
|
||||
|
|
@ -510,13 +516,14 @@ impl BSDF {
|
|||
}
|
||||
|
||||
pub fn is_valid(&self) -> bool {
|
||||
let guard = self.bxdf.read().unwrap();
|
||||
guard.as_any().downcast_ref::<EmptyBxDF>().is_none()
|
||||
self.bxdf.is_some()
|
||||
}
|
||||
|
||||
pub fn flags(&self) -> BxDFFlags {
|
||||
let guard = self.bxdf.read().unwrap();
|
||||
guard.flags()
|
||||
match &self.bxdf {
|
||||
Some(b) => b.flags(),
|
||||
None => BxDFFlags::empty(), // or Transmissive if it's invisible
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render_to_local(&self, v: Vector3f) -> Vector3f {
|
||||
|
|
@ -528,134 +535,92 @@ impl BSDF {
|
|||
}
|
||||
|
||||
pub fn f(
|
||||
&self,
|
||||
wo_render: &Vector3f,
|
||||
wi_render: &Vector3f,
|
||||
mode: TransportMode,
|
||||
) -> SampledSpectrum {
|
||||
let wo = self.render_to_local(*wo_render);
|
||||
if wo.z() == 0. {
|
||||
return SampledSpectrum::default();
|
||||
}
|
||||
let wi = self.render_to_local(*wi_render);
|
||||
let guard = self.bxdf.read().unwrap();
|
||||
guard.f(&wo, &wi, mode)
|
||||
}
|
||||
|
||||
pub fn sample_f(
|
||||
&self,
|
||||
wo_render: &Vector3f,
|
||||
u: Float,
|
||||
u2: Point2f,
|
||||
f_args: FArgs,
|
||||
) -> Option<BSDFSample> {
|
||||
let wo = self.render_to_local(*wo_render);
|
||||
let sampling_flags = BxDFFlags::from_bits_truncate(f_args.sample_flags.bits());
|
||||
let guard = self.bxdf.read().unwrap();
|
||||
|
||||
if wo.z() == 0. || !(guard.flags().contains(sampling_flags)) {
|
||||
return None;
|
||||
}
|
||||
if let Some(mut bs) = guard.sample_f(&wo, u, u2, f_args)
|
||||
&& bs.pdf > 0.0
|
||||
&& bs.wi.z() != 0.0
|
||||
{
|
||||
bs.wi = self.local_to_render(bs.wi);
|
||||
return Some(bs);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn pdf(&self, wo_render: &Vector3f, wi_render: &Vector3f, f_args: FArgs) -> Float {
|
||||
let wo = self.render_to_local(*wo_render);
|
||||
if wo.z() == 0.0 {
|
||||
return 0.;
|
||||
}
|
||||
let wi = self.render_to_local(*wi_render);
|
||||
let guard = self.bxdf.read().unwrap();
|
||||
guard.pdf(&wo, &wi, f_args)
|
||||
}
|
||||
|
||||
pub fn f_specific<B: BxDFTrait>(
|
||||
&self,
|
||||
wo_render: Vector3f,
|
||||
wi_render: Vector3f,
|
||||
mode: TransportMode,
|
||||
) -> SampledSpectrum {
|
||||
let bxdf = match &self.bxdf {
|
||||
Some(b) => b,
|
||||
None => return SampledSpectrum::default(),
|
||||
};
|
||||
|
||||
let wi = self.render_to_local(wi_render);
|
||||
let wo = self.render_to_local(wo_render);
|
||||
if wo.z() == 0.0 {
|
||||
|
||||
if wo.z() == 0.0 || wi.z() == 0.0 {
|
||||
return SampledSpectrum::default();
|
||||
}
|
||||
let wi = self.render_to_local(wi_render);
|
||||
let guard = self.bxdf.read().unwrap();
|
||||
|
||||
if let Some(specific_bxdf) = guard.as_any().downcast_ref::<B>() {
|
||||
specific_bxdf.f(&wo, &wi, mode)
|
||||
} else {
|
||||
SampledSpectrum::default()
|
||||
}
|
||||
bxdf.f(wo, wi, mode)
|
||||
}
|
||||
|
||||
pub fn sample_f_specific<B: BxDFTrait>(
|
||||
pub fn sample_f(
|
||||
&self,
|
||||
wo_render: &Vector3f,
|
||||
wo_render: Vector3f,
|
||||
u: Float,
|
||||
u2: Point2f,
|
||||
f_args: FArgs,
|
||||
) -> Option<BSDFSample> {
|
||||
let wo = self.render_to_local(*wo_render);
|
||||
let sampling_flags = BxDFFlags::from_bits_truncate(f_args.sample_flags.bits());
|
||||
let guard = self.bxdf.read().unwrap();
|
||||
let bxdf = self.bxdf.as_ref()?;
|
||||
|
||||
if wo.z() == 0. || !guard.flags().contains(sampling_flags) {
|
||||
let sampling_flags = BxDFFlags::from_bits_truncate(f_args.sample_flags.bits());
|
||||
let wo = self.render_to_local(wo_render);
|
||||
if wo.z() == 0.0 || !bxdf.flags().contains(sampling_flags) {
|
||||
return None;
|
||||
}
|
||||
|
||||
guard
|
||||
.as_any()
|
||||
.downcast_ref::<B>()
|
||||
.and_then(|specific_bxdf| specific_bxdf.sample_f(&wo, u, u2, f_args))
|
||||
.filter(|bs| bs.pdf > 0.0 && bs.wi.z() != 0.0)
|
||||
.map(|mut bs| {
|
||||
bs.wi = self.local_to_render(bs.wi);
|
||||
bs
|
||||
})
|
||||
let mut sample = bxdf.sample_f(wo, u, u2, f_args)?;
|
||||
|
||||
if sample.pdf > 0.0 && sample.wi.z() != 0.0 {
|
||||
sample.wi = self.local_to_render(sample.wi);
|
||||
return Some(sample);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn pdf_specific<B: BxDFTrait>(
|
||||
&self,
|
||||
wo_render: &Vector3f,
|
||||
wi_render: &Vector3f,
|
||||
f_args: FArgs,
|
||||
) -> Float {
|
||||
let wo = self.render_to_local(*wo_render);
|
||||
if wo.z() == 0.0 {
|
||||
return 0.;
|
||||
}
|
||||
let wi = self.render_to_local(*wi_render);
|
||||
let guard = self.bxdf.read().unwrap();
|
||||
pub fn pdf(&self, wo_render: Vector3f, wi_render: Vector3f, f_args: FArgs) -> Float {
|
||||
let bxdf = match &self.bxdf {
|
||||
Some(b) => b,
|
||||
None => return 0.0,
|
||||
};
|
||||
|
||||
if let Some(specific_bxdf) = guard.as_any().downcast_ref::<B>() {
|
||||
specific_bxdf.pdf(&wo, &wi, f_args)
|
||||
} else {
|
||||
0.
|
||||
let sample_flags = BxDFFlags::from_bits_truncate(f_args.sample_flags.bits());
|
||||
|
||||
let wo = self.render_to_local(wo_render);
|
||||
let wi = self.render_to_local(wi_render);
|
||||
|
||||
if wo.z() == 0.0 || !bxdf.flags().contains(sample_flags) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
bxdf.pdf(wo, wi, f_args)
|
||||
}
|
||||
|
||||
pub fn rho_u(&self, u1: &[Point2f], uc: &[Float], u2: &[Point2f]) -> SampledSpectrum {
|
||||
let guard = self.bxdf.read().unwrap();
|
||||
guard.rho_u(u1, uc, u2)
|
||||
let bxdf = match &self.bxdf {
|
||||
Some(b) => b,
|
||||
None => return SampledSpectrum::default(),
|
||||
};
|
||||
|
||||
bxdf.rho_u(u1, uc, u2)
|
||||
}
|
||||
|
||||
pub fn rho_wo(&self, wo_render: Vector3f, uc: &[Float], u: &[Point2f]) -> SampledSpectrum {
|
||||
let bxdf = match &self.bxdf {
|
||||
Some(b) => b,
|
||||
None => return SampledSpectrum::default(),
|
||||
};
|
||||
|
||||
let wo = self.render_to_local(wo_render);
|
||||
let guard = self.bxdf.read().unwrap();
|
||||
guard.rho_wo(wo, uc, u)
|
||||
bxdf.rho_wo(wo, uc, u)
|
||||
}
|
||||
|
||||
pub fn regularize(&mut self) {
|
||||
let mut guard = self.bxdf.write().unwrap();
|
||||
guard.regularize()
|
||||
if let Some(bxdf) = &mut self.bxdf {
|
||||
bxdf.regularize()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -668,14 +633,14 @@ impl BxDFTrait for DiffuseBxDF {
|
|||
}
|
||||
}
|
||||
|
||||
fn f(&self, wo: &Vector3f, wi: &Vector3f, _mode: TransportMode) -> SampledSpectrum {
|
||||
if !same_hemisphere(*wo, *wi) {
|
||||
fn f(&self, wo: Vector3f, wi: Vector3f, _mode: TransportMode) -> SampledSpectrum {
|
||||
if !same_hemisphere(wo, wi) {
|
||||
return SampledSpectrum::new(0.);
|
||||
}
|
||||
self.r * INV_PI
|
||||
}
|
||||
|
||||
fn sample_f(&self, wo: &Vector3f, _uc: Float, u: Point2f, f_args: FArgs) -> Option<BSDFSample> {
|
||||
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) {
|
||||
|
|
@ -696,13 +661,13 @@ impl BxDFTrait for DiffuseBxDF {
|
|||
Some(bsdf)
|
||||
}
|
||||
|
||||
fn pdf(&self, wo: &Vector3f, wi: &Vector3f, f_args: FArgs) -> Float {
|
||||
fn pdf(&self, wo: Vector3f, wi: Vector3f, f_args: FArgs) -> Float {
|
||||
let reflection_flags =
|
||||
BxDFReflTransFlags::from_bits_truncate(BxDFReflTransFlags::ALL.bits());
|
||||
if !f_args.sample_flags.contains(reflection_flags) || !same_hemisphere(*wo, *wi) {
|
||||
if !f_args.sample_flags.contains(reflection_flags) || !same_hemisphere(wo, wi) {
|
||||
return 0.;
|
||||
}
|
||||
cosine_hemisphere_pdf(abs_cos_theta(*wi))
|
||||
cosine_hemisphere_pdf(abs_cos_theta(wi))
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
|
|
@ -718,7 +683,7 @@ impl BxDFTrait for ConductorBxDF {
|
|||
BxDFFlags::GLOSSY_REFLECTION
|
||||
}
|
||||
}
|
||||
fn sample_f(&self, wo: &Vector3f, _uc: Float, u: Point2f, f_args: FArgs) -> Option<BSDFSample> {
|
||||
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) {
|
||||
|
|
@ -744,22 +709,22 @@ impl BxDFTrait for ConductorBxDF {
|
|||
if wo.z() == 0. {
|
||||
return None;
|
||||
}
|
||||
let wm = self.mf_distrib.sample_wm(*wo, u);
|
||||
let wi = reflect(*wo, wm.into());
|
||||
if !same_hemisphere(*wo, wi) {
|
||||
let wm = self.mf_distrib.sample_wm(wo, u);
|
||||
let wi = reflect(wo, wm.into());
|
||||
if !same_hemisphere(wo, wi) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let pdf = self.mf_distrib.pdf(*wo, wm) / (4. * (*wo).dot(wm).abs());
|
||||
let pdf = self.mf_distrib.pdf(wo, wm) / (4. * wo.dot(wm).abs());
|
||||
|
||||
let cos_theta_o = abs_cos_theta(*wo);
|
||||
let cos_theta_o = abs_cos_theta(wo);
|
||||
let cos_theta_i = abs_cos_theta(wi);
|
||||
if cos_theta_i == 0. || cos_theta_o == 0. {
|
||||
return None;
|
||||
}
|
||||
|
||||
let f_spectrum = fr_complex_from_spectrum((*wo).dot(wi).abs(), self.eta, self.k);
|
||||
let f = self.mf_distrib.d(wm) * f_spectrum * self.mf_distrib.g(*wo, wi)
|
||||
let f_spectrum = fr_complex_from_spectrum(wo.dot(wi).abs(), self.eta, self.k);
|
||||
let f = self.mf_distrib.d(wm) * f_spectrum * self.mf_distrib.g(wo, wi)
|
||||
/ (4. * cos_theta_i * cos_theta_o);
|
||||
|
||||
let bsdf = BSDFSample {
|
||||
|
|
@ -773,49 +738,49 @@ impl BxDFTrait for ConductorBxDF {
|
|||
Some(bsdf)
|
||||
}
|
||||
|
||||
fn f(&self, wo: &Vector3f, wi: &Vector3f, _mode: TransportMode) -> SampledSpectrum {
|
||||
if !same_hemisphere(*wo, *wi) {
|
||||
fn f(&self, wo: Vector3f, wi: Vector3f, _mode: TransportMode) -> SampledSpectrum {
|
||||
if !same_hemisphere(wo, wi) {
|
||||
return SampledSpectrum::default();
|
||||
}
|
||||
if self.mf_distrib.effectively_smooth() {
|
||||
return SampledSpectrum::default();
|
||||
}
|
||||
|
||||
let cos_theta_o = abs_cos_theta(*wo);
|
||||
let cos_theta_i = abs_cos_theta(*wi);
|
||||
let cos_theta_o = abs_cos_theta(wo);
|
||||
let cos_theta_i = abs_cos_theta(wi);
|
||||
if cos_theta_i == 0. || cos_theta_o == 0. {
|
||||
return SampledSpectrum::new(0.);
|
||||
}
|
||||
|
||||
let wm = *wi + *wo;
|
||||
let wm = wi + wo;
|
||||
if wm.norm_squared() == 0. {
|
||||
return SampledSpectrum::new(0.);
|
||||
}
|
||||
let wm_norm = wm.normalize();
|
||||
|
||||
let f_spectrum = fr_complex_from_spectrum((*wo).dot(wm).abs(), self.eta, self.k);
|
||||
self.mf_distrib.d(wm_norm) * f_spectrum * self.mf_distrib.g(*wo, *wi)
|
||||
let f_spectrum = fr_complex_from_spectrum(wo.dot(wm).abs(), self.eta, self.k);
|
||||
self.mf_distrib.d(wm_norm) * f_spectrum * self.mf_distrib.g(wo, wi)
|
||||
/ (4. * cos_theta_i * cos_theta_o)
|
||||
}
|
||||
|
||||
fn pdf(&self, wo: &Vector3f, wi: &Vector3f, f_args: FArgs) -> Float {
|
||||
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) {
|
||||
if !same_hemisphere(wo, wi) {
|
||||
return 0.;
|
||||
}
|
||||
if self.mf_distrib.effectively_smooth() {
|
||||
return 0.;
|
||||
}
|
||||
let wm = *wo + *wi;
|
||||
let wm = wo + wi;
|
||||
if wm.norm_squared() == 0. {
|
||||
return 0.;
|
||||
}
|
||||
let wm_corr = Normal3f::new(0., 0., 1.).face_forward(wm);
|
||||
self.mf_distrib.pdf(*wo, wm_corr.into()) / (4. * (*wo).dot(wm).abs())
|
||||
self.mf_distrib.pdf(wo, wm_corr.into()) / (4. * wo.dot(wm).abs())
|
||||
}
|
||||
|
||||
fn regularize(&mut self) {
|
||||
|
|
@ -842,14 +807,14 @@ impl BxDFTrait for DielectricBxDF {
|
|||
}
|
||||
}
|
||||
|
||||
fn f(&self, wo: &Vector3f, wi: &Vector3f, mode: TransportMode) -> SampledSpectrum {
|
||||
fn f(&self, wo: Vector3f, wi: Vector3f, mode: TransportMode) -> SampledSpectrum {
|
||||
if self.eta == 1. || self.mf_distrib.effectively_smooth() {
|
||||
return SampledSpectrum::new(0.);
|
||||
}
|
||||
|
||||
// Generalized half vector wm
|
||||
let cos_theta_o = cos_theta(*wo);
|
||||
let cos_theta_i = cos_theta(*wi);
|
||||
let cos_theta_o = cos_theta(wo);
|
||||
let cos_theta_i = cos_theta(wi);
|
||||
let reflect = cos_theta_i * cos_theta_o > 0.;
|
||||
|
||||
let mut etap = 1.;
|
||||
|
|
@ -861,31 +826,30 @@ impl BxDFTrait for DielectricBxDF {
|
|||
};
|
||||
}
|
||||
|
||||
let wm_orig = *wi * etap + *wo;
|
||||
let wm_orig = wi * etap + wo;
|
||||
if cos_theta_i == 0. || cos_theta_o == 0. || wm_orig.norm_squared() == 0. {
|
||||
return SampledSpectrum::new(0.);
|
||||
}
|
||||
let wm = Normal3f::new(0., 0., 1.).face_forward(wm_orig.normalize());
|
||||
|
||||
if (*wi).dot(wm.into()) * cos_theta_i < 0. || (*wo).dot(wm.into()) * cos_theta_o < 0. {
|
||||
if wi.dot(wm.into()) * cos_theta_i < 0. || wo.dot(wm.into()) * cos_theta_o < 0. {
|
||||
return SampledSpectrum::new(0.);
|
||||
}
|
||||
|
||||
let fr = fr_dielectric((*wo).dot(wm.into()), self.eta);
|
||||
let fr = fr_dielectric(wo.dot(wm.into()), self.eta);
|
||||
|
||||
if reflect {
|
||||
SampledSpectrum::new(
|
||||
self.mf_distrib.d(wm.into()) * self.mf_distrib.g(*wo, *wi) * fr
|
||||
self.mf_distrib.d(wm.into()) * self.mf_distrib.g(wo, wi) * fr
|
||||
/ (4. * cos_theta_i * cos_theta_o).abs(),
|
||||
)
|
||||
} else {
|
||||
let denom = square((*wi).dot(wm.into()) + (*wo).dot(wm.into()) / etap)
|
||||
* cos_theta_i
|
||||
* cos_theta_o;
|
||||
let denom =
|
||||
square(wi.dot(wm.into()) + wo.dot(wm.into()) / etap) * cos_theta_i * cos_theta_o;
|
||||
let mut ft = self.mf_distrib.d(wm.into())
|
||||
* (1. - fr)
|
||||
* self.mf_distrib.g(*wo, *wi)
|
||||
* ((*wi).dot(wm.into()) * (*wo).dot(wm.into()) / denom).abs();
|
||||
* self.mf_distrib.g(wo, wi)
|
||||
* (wi.dot(wm.into()) * wo.dot(wm.into()) / denom).abs();
|
||||
if mode == TransportMode::Radiance {
|
||||
ft /= square(etap)
|
||||
}
|
||||
|
|
@ -894,13 +858,13 @@ impl BxDFTrait for DielectricBxDF {
|
|||
}
|
||||
}
|
||||
|
||||
fn pdf(&self, wo: &Vector3f, wi: &Vector3f, f_args: FArgs) -> Float {
|
||||
fn pdf(&self, wo: Vector3f, wi: Vector3f, f_args: FArgs) -> Float {
|
||||
if self.eta == 1. || self.mf_distrib.effectively_smooth() {
|
||||
return 0.;
|
||||
}
|
||||
|
||||
let cos_theta_o = cos_theta(*wo);
|
||||
let cos_theta_i = cos_theta(*wi);
|
||||
let cos_theta_o = cos_theta(wo);
|
||||
let cos_theta_i = cos_theta(wi);
|
||||
|
||||
let reflect = cos_theta_i * cos_theta_o > 0.;
|
||||
let mut etap = 1.;
|
||||
|
|
@ -912,7 +876,7 @@ impl BxDFTrait for DielectricBxDF {
|
|||
};
|
||||
}
|
||||
|
||||
let wm_orig = *wi * etap + *wo;
|
||||
let wm_orig = wi * etap + wo;
|
||||
|
||||
if cos_theta_i == 0. || cos_theta_o == 0. || wm_orig.norm_squared() == 0. {
|
||||
return 0.;
|
||||
|
|
@ -920,11 +884,11 @@ impl BxDFTrait for DielectricBxDF {
|
|||
let wm = Normal3f::new(0., 0., 1.).face_forward(wm_orig.normalize());
|
||||
|
||||
// Discard backfacing microfacets
|
||||
if (*wi).dot(wm.into()) * cos_theta_i < 0. || (*wo).dot(wm.into()) * cos_theta_o < 0. {
|
||||
if wi.dot(wm.into()) * cos_theta_i < 0. || wo.dot(wm.into()) * cos_theta_o < 0. {
|
||||
return 0.;
|
||||
}
|
||||
|
||||
let r = fr_dielectric((*wo).dot(wm.into()), self.eta);
|
||||
let r = fr_dielectric(wo.dot(wm.into()), self.eta);
|
||||
let t = 1. - r;
|
||||
let mut pr = r;
|
||||
let mut pt = t;
|
||||
|
|
@ -944,19 +908,19 @@ impl BxDFTrait for DielectricBxDF {
|
|||
|
||||
if reflect {
|
||||
self.mf_distrib.pdf(
|
||||
*wo,
|
||||
Vector3f::from(wm) / (4. * (*wo).dot(wm.into()).abs()) * pr / (pt + pr),
|
||||
wo,
|
||||
Vector3f::from(wm) / (4. * wo.dot(wm.into()).abs()) * pr / (pt + pr),
|
||||
)
|
||||
} else {
|
||||
let denom = square((*wi).dot(wm.into()) + (*wo).dot(wm.into()) / etap);
|
||||
let dwm_dwi = (*wi).dot(wm.into()).abs() / denom;
|
||||
self.mf_distrib.pdf(*wo, wm.into()) * dwm_dwi * pr / (pr + pt)
|
||||
let denom = square(wi.dot(wm.into()) + wo.dot(wm.into()) / etap);
|
||||
let dwm_dwi = wi.dot(wm.into()).abs() / denom;
|
||||
self.mf_distrib.pdf(wo, wm.into()) * dwm_dwi * pr / (pr + pt)
|
||||
}
|
||||
}
|
||||
|
||||
fn sample_f(&self, wo: &Vector3f, uc: Float, u: Point2f, f_args: FArgs) -> Option<BSDFSample> {
|
||||
fn sample_f(&self, wo: Vector3f, uc: Float, u: Point2f, f_args: FArgs) -> Option<BSDFSample> {
|
||||
if self.eta == 1. || self.mf_distrib.effectively_smooth() {
|
||||
let r = fr_dielectric(cos_theta(*wo), self.eta);
|
||||
let r = fr_dielectric(cos_theta(wo), self.eta);
|
||||
let t = 1. - r;
|
||||
let mut pr = r;
|
||||
let mut pt = t;
|
||||
|
|
@ -988,7 +952,7 @@ impl BxDFTrait for DielectricBxDF {
|
|||
Some(bsdf)
|
||||
} else {
|
||||
// Compute ray direction for specular transmission
|
||||
if let Some((wi, etap)) = refract(*wo, Normal3f::new(0., 0., 1.), self.eta) {
|
||||
if let Some((wi, etap)) = refract(wo, Normal3f::new(0., 0., 1.), self.eta) {
|
||||
let mut ft = SampledSpectrum::new(t / abs_cos_theta(wi));
|
||||
if f_args.mode == TransportMode::Radiance {
|
||||
ft /= square(etap);
|
||||
|
|
@ -1008,8 +972,8 @@ impl BxDFTrait for DielectricBxDF {
|
|||
}
|
||||
} else {
|
||||
// Sample rough dielectric BSDF
|
||||
let wm = self.mf_distrib.sample_wm(*wo, u);
|
||||
let r = fr_dielectric((*wo).dot(wm), self.eta);
|
||||
let wm = self.mf_distrib.sample_wm(wo, u);
|
||||
let r = fr_dielectric(wo.dot(wm), self.eta);
|
||||
let t = 1. - r;
|
||||
let mut pr = r;
|
||||
let mut pt = t;
|
||||
|
|
@ -1029,15 +993,15 @@ impl BxDFTrait for DielectricBxDF {
|
|||
let pdf: Float;
|
||||
if uc < pr / (pr + pt) {
|
||||
// Sample reflection at rough dielectric interface
|
||||
let wi = reflect(*wo, wm.into());
|
||||
if !same_hemisphere(*wo, wi) {
|
||||
let wi = reflect(wo, wm.into());
|
||||
if !same_hemisphere(wo, wi) {
|
||||
return None;
|
||||
}
|
||||
|
||||
pdf = self.mf_distrib.pdf(*wo, wm) / (4. * (*wo).dot(wm).abs()) * pr / (pr + pt);
|
||||
pdf = self.mf_distrib.pdf(wo, wm) / (4. * wo.dot(wm).abs()) * pr / (pr + pt);
|
||||
let f = SampledSpectrum::new(
|
||||
self.mf_distrib.d(wm) * self.mf_distrib.g(*wo, wi) * r
|
||||
/ (4. * cos_theta(wi) * cos_theta(*wo)),
|
||||
self.mf_distrib.d(wm) * self.mf_distrib.g(wo, wi) * r
|
||||
/ (4. * cos_theta(wi) * cos_theta(wo)),
|
||||
);
|
||||
let bsdf = BSDFSample {
|
||||
f,
|
||||
|
|
@ -1049,18 +1013,18 @@ impl BxDFTrait for DielectricBxDF {
|
|||
Some(bsdf)
|
||||
} else {
|
||||
// Sample transmission at rough dielectric interface
|
||||
if let Some((wi, etap)) = refract(*wo, wm.into(), self.eta) {
|
||||
if same_hemisphere(*wo, wi) || wi.z() == 0. {
|
||||
if let Some((wi, etap)) = refract(wo, wm.into(), self.eta) {
|
||||
if same_hemisphere(wo, wi) || wi.z() == 0. {
|
||||
None
|
||||
} else {
|
||||
let denom = square(wi.dot(wm) + wo.dot(wm) / etap);
|
||||
let dwm_mi = wi.dot(wm).abs() / denom;
|
||||
pdf = self.mf_distrib.pdf(*wo, wm) * dwm_mi * pt / (pr + pt);
|
||||
pdf = self.mf_distrib.pdf(wo, wm) * dwm_mi * pt / (pr + pt);
|
||||
let mut ft = SampledSpectrum::new(
|
||||
t * self.mf_distrib.d(wm)
|
||||
* self.mf_distrib.g(*wo, wi)
|
||||
* (wi.dot(wm) * (*wo).dot(wm)).abs()
|
||||
/ (cos_theta(wi) * cos_theta(*wo) * denom),
|
||||
* self.mf_distrib.g(wo, wi)
|
||||
* (wi.dot(wm) * wo.dot(wm)).abs()
|
||||
/ (cos_theta(wi) * cos_theta(wo) * denom),
|
||||
);
|
||||
if f_args.mode == TransportMode::Radiance {
|
||||
ft /= square(etap);
|
||||
|
|
@ -1096,16 +1060,16 @@ impl BxDFTrait for ThinDielectricBxDF {
|
|||
BxDFFlags::REFLECTION | BxDFFlags::TRANSMISSION | BxDFFlags::SPECULAR
|
||||
}
|
||||
|
||||
fn f(&self, _wo: &Vector3f, _wi: &Vector3f, _mode: TransportMode) -> SampledSpectrum {
|
||||
fn f(&self, _wo: Vector3f, _wi: Vector3f, _mode: TransportMode) -> SampledSpectrum {
|
||||
SampledSpectrum::new(0.)
|
||||
}
|
||||
|
||||
fn pdf(&self, _wo: &Vector3f, _wi: &Vector3f, _f_args: FArgs) -> Float {
|
||||
fn pdf(&self, _wo: Vector3f, _wi: Vector3f, _f_args: FArgs) -> Float {
|
||||
0.
|
||||
}
|
||||
|
||||
fn sample_f(&self, wo: &Vector3f, uc: Float, _u: Point2f, f_args: FArgs) -> Option<BSDFSample> {
|
||||
let mut r = fr_dielectric(abs_cos_theta(*wo), self.eta);
|
||||
fn sample_f(&self, wo: Vector3f, uc: Float, _u: Point2f, f_args: FArgs) -> Option<BSDFSample> {
|
||||
let mut r = fr_dielectric(abs_cos_theta(wo), self.eta);
|
||||
let mut t = 1. - r;
|
||||
if r < 1. {
|
||||
r += square(t) * r / (1. - square(r));
|
||||
|
|
@ -1140,7 +1104,7 @@ impl BxDFTrait for ThinDielectricBxDF {
|
|||
Some(bsdf)
|
||||
} else {
|
||||
// Perfect specular transmission
|
||||
let wi = -(*wo);
|
||||
let wi = -wo;
|
||||
let f = SampledSpectrum::new(t / abs_cos_theta(wi));
|
||||
let bsdf = BSDFSample {
|
||||
f,
|
||||
|
|
@ -1165,13 +1129,13 @@ 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) {
|
||||
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;
|
||||
let mut wo_curr = wo;
|
||||
let mut wi_curr = wi;
|
||||
if wo.z() < 0. {
|
||||
wo_curr = -wo_curr;
|
||||
wi_curr = -wi_curr;
|
||||
|
|
@ -1211,21 +1175,21 @@ impl BxDFTrait for MeasuredBxDF {
|
|||
});
|
||||
|
||||
fr * self.brdf.ndf.evaluate(u_wm, [])
|
||||
/ (4. * self.brdf.sigma.evaluate(u_wo, []) * cos_theta(*wi))
|
||||
/ (4. * self.brdf.sigma.evaluate(u_wo, []) * cos_theta(wi))
|
||||
}
|
||||
|
||||
fn pdf(&self, wo: &Vector3f, wi: &Vector3f, f_args: FArgs) -> Float {
|
||||
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) {
|
||||
if !same_hemisphere(wo, wi) {
|
||||
return 0.;
|
||||
}
|
||||
|
||||
let mut wo_curr = *wo;
|
||||
let mut wi_curr = *wi;
|
||||
let mut wo_curr = wo;
|
||||
let mut wi_curr = wi;
|
||||
if wo.z() < 0. {
|
||||
wo_curr = -wo_curr;
|
||||
wi_curr = -wi_curr;
|
||||
|
|
@ -1259,11 +1223,11 @@ impl BxDFTrait for MeasuredBxDF {
|
|||
|
||||
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);
|
||||
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> {
|
||||
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) {
|
||||
|
|
@ -1271,7 +1235,7 @@ impl BxDFTrait for MeasuredBxDF {
|
|||
}
|
||||
|
||||
let mut flip_w = false;
|
||||
let mut wo_curr = *wo;
|
||||
let mut wo_curr = wo;
|
||||
if wo.z() <= 0. {
|
||||
wo_curr = -wo_curr;
|
||||
flip_w = true;
|
||||
|
|
@ -1344,7 +1308,7 @@ impl BxDFTrait for HairBxDF {
|
|||
BxDFFlags::GLOSSY_REFLECTION
|
||||
}
|
||||
|
||||
fn f(&self, wo: &Vector3f, wi: &Vector3f, _mode: TransportMode) -> SampledSpectrum {
|
||||
fn f(&self, wo: Vector3f, wi: Vector3f, _mode: TransportMode) -> SampledSpectrum {
|
||||
// Compute hair coordinate system terms related to wo
|
||||
let sin_theta_o = wo.x();
|
||||
let cos_theta_o = safe_sqrt(1. - square(sin_theta_o));
|
||||
|
|
@ -1392,15 +1356,15 @@ impl BxDFTrait for HairBxDF {
|
|||
) * ap
|
||||
* Self::np(phi, p as i32, self.s, gamma_o, gamma_t);
|
||||
}
|
||||
if abs_cos_theta(*wi) > 0. {
|
||||
f_sum /= abs_cos_theta(*wi);
|
||||
if abs_cos_theta(wi) > 0. {
|
||||
f_sum /= abs_cos_theta(wi);
|
||||
}
|
||||
f_sum
|
||||
}
|
||||
|
||||
fn sample_f(
|
||||
&self,
|
||||
wo: &Vector3f,
|
||||
wo: Vector3f,
|
||||
mut uc: Float,
|
||||
u: Point2f,
|
||||
f_args: FArgs,
|
||||
|
|
@ -1489,7 +1453,7 @@ impl BxDFTrait for HairBxDF {
|
|||
* INV_2_PI;
|
||||
|
||||
let bsd = BSDFSample {
|
||||
f: self.f(wo, &wi, f_args.mode),
|
||||
f: self.f(wo, wi, f_args.mode),
|
||||
wi,
|
||||
pdf,
|
||||
flags: self.flags(),
|
||||
|
|
@ -1499,7 +1463,7 @@ impl BxDFTrait for HairBxDF {
|
|||
Some(bsd)
|
||||
}
|
||||
|
||||
fn pdf(&self, wo: &Vector3f, wi: &Vector3f, _f_args: FArgs) -> Float {
|
||||
fn pdf(&self, wo: Vector3f, wi: Vector3f, _f_args: FArgs) -> Float {
|
||||
let sin_theta_o = wo.x();
|
||||
let cos_theta_o = safe_sqrt(1. - square(sin_theta_o));
|
||||
let phi_o = wo.z().atan2(wo.y());
|
||||
|
|
|
|||
|
|
@ -239,7 +239,7 @@ impl PixelSensor {
|
|||
}
|
||||
|
||||
pub fn to_sensor_rgb(&self, l: SampledSpectrum, lambda: &SampledWavelengths) -> RGB {
|
||||
let l_norm = l.safe_div(lambda.pdf());
|
||||
let l_norm = SampledSpectrum::safe_div(&l, &lambda.pdf());
|
||||
self.imaging_ratio
|
||||
* RGB::new(
|
||||
(self.r_bar.sample(lambda) * l_norm).average(),
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use crate::camera::{Camera, CameraTrait};
|
||||
use crate::core::bxdf::{BSDF, BxDFFlags};
|
||||
use crate::core::material::MaterialTrait;
|
||||
use crate::core::material::Material;
|
||||
use crate::core::medium::{Medium, MediumInterface};
|
||||
use crate::core::pbrt::{Float, clamp_t};
|
||||
use crate::geometry::{
|
||||
|
|
@ -93,8 +93,8 @@ pub struct ShadingGeometry {
|
|||
pub dndv: Normal3f,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub struct SurfaceInteraction {
|
||||
#[derive(Default, Debug)]
|
||||
pub struct SurfaceInteraction<'a> {
|
||||
pub common: InteractionData,
|
||||
pub uv: Point2f,
|
||||
pub dpdu: Vector3f,
|
||||
|
|
@ -105,7 +105,7 @@ pub struct SurfaceInteraction {
|
|||
pub medium_interface: Option<Arc<MediumInterface>>,
|
||||
pub face_index: usize,
|
||||
pub area_light: Option<Arc<Light>>,
|
||||
pub material: Option<Arc<dyn MaterialTrait>>,
|
||||
pub material: Option<Arc<Material>>,
|
||||
pub dpdx: Vector3f,
|
||||
pub dpdy: Vector3f,
|
||||
pub dudx: Float,
|
||||
|
|
@ -113,10 +113,10 @@ pub struct SurfaceInteraction {
|
|||
pub dudy: Float,
|
||||
pub dvdy: Float,
|
||||
pub shape: Arc<Shape>,
|
||||
pub bsdf: Option<BSDF>,
|
||||
// pub bsdf: Option<BSDF<'a>>,
|
||||
}
|
||||
|
||||
impl SurfaceInteraction {
|
||||
impl<'a> SurfaceInteraction<'a> {
|
||||
pub fn compute_differentials(&mut self, r: &Ray, camera: Camera, samples_per_pixel: i32) {
|
||||
let computed = if let Some(diff) = &r.differential {
|
||||
let dot_rx = self.common.n.dot(diff.rx_direction.into());
|
||||
|
|
@ -506,7 +506,7 @@ impl SurfaceInteraction {
|
|||
|
||||
pub fn set_intersection_properties(
|
||||
&mut self,
|
||||
mtl: Arc<dyn MaterialTrait>,
|
||||
mtl: Arc<Material>,
|
||||
area: Arc<Light>,
|
||||
prim_medium_interface: Option<Arc<MediumInterface>>,
|
||||
ray_medium: Arc<Medium>,
|
||||
|
|
|
|||
|
|
@ -1,27 +1,56 @@
|
|||
#[derive(Clone, Debug)]
|
||||
pub struct CoatedDiffuseMaterial;
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CoatedConductorMaterial;
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ConductorMaterial;
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DielectricMaterial;
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DiffuseMaterial;
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DiffuseTransmissionMaterial;
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct HairMaterial;
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct MeasuredMaterial;
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SubsurfaceMaterial;
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ThinDielectricMaterial;
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct MixMaterial;
|
||||
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, DiffuseBxDF};
|
||||
use crate::core::texture::{FloatTexture, SpectrumTexture, TextureEvalContext, TextureEvaluator};
|
||||
use crate::geometry::{Normal3f, Vector3f};
|
||||
use crate::image::Image;
|
||||
use crate::spectra::SampledWavelengths;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct MaterialEvalContext {
|
||||
texture: TextureEvalContext,
|
||||
wo: Vector3f,
|
||||
ns: Normal3f,
|
||||
dpdus: Vector3f,
|
||||
}
|
||||
|
||||
impl Deref for MaterialEvalContext {
|
||||
type Target = TextureEvalContext;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.texture
|
||||
}
|
||||
}
|
||||
|
||||
#[enum_dispatch]
|
||||
pub trait MaterialTrait: Send + Sync + std::fmt::Debug {
|
||||
fn get_bsdf<'a, T: TextureEvaluator>(
|
||||
&self,
|
||||
tex_eval: &T,
|
||||
ctx: &MaterialEvalContext,
|
||||
lambda: &SampledWavelengths,
|
||||
scratch: &'a Bump,
|
||||
) -> BSDF<'a>;
|
||||
|
||||
fn get_bssrdf<'a, T: TextureEvaluator>(
|
||||
&self,
|
||||
tex_eval: &T,
|
||||
ctx: &MaterialEvalContext,
|
||||
lambda: &SampledWavelengths,
|
||||
) -> BSSRDF<'a>;
|
||||
|
||||
fn can_evaluate_textures(&self, tex_eval: &dyn TextureEvaluator) -> bool;
|
||||
fn get_normal_map(&self) -> Image;
|
||||
fn get_displacement(&self) -> FloatTexture;
|
||||
fn has_surface_scattering(&self) -> bool;
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[enum_dispatch(MaterialTrait)]
|
||||
pub enum Material {
|
||||
CoatedDiffuse(CoatedDiffuseMaterial),
|
||||
CoatedConductor(CoatedConductorMaterial),
|
||||
|
|
@ -36,6 +65,381 @@ pub enum Material {
|
|||
Mix(MixMaterial),
|
||||
}
|
||||
|
||||
impl Material {}
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CoatedDiffuseMaterial;
|
||||
impl MaterialTrait for CoatedDiffuseMaterial {
|
||||
fn get_bsdf<'a, T: TextureEvaluator>(
|
||||
&self,
|
||||
_tex_eval: &T,
|
||||
_ctx: &MaterialEvalContext,
|
||||
_lambda: &SampledWavelengths,
|
||||
_scratch: &'a Bump,
|
||||
) -> BSDF<'a> {
|
||||
todo!()
|
||||
}
|
||||
fn get_bssrdf<'a, T>(
|
||||
&self,
|
||||
_tex_eval: &T,
|
||||
_ctx: &MaterialEvalContext,
|
||||
_lambda: &SampledWavelengths,
|
||||
) -> BSSRDF<'a> {
|
||||
todo!()
|
||||
}
|
||||
fn can_evaluate_textures(&self, _tex_eval: &dyn TextureEvaluator) -> bool {
|
||||
todo!()
|
||||
}
|
||||
fn get_normal_map(&self) -> Image {
|
||||
todo!()
|
||||
}
|
||||
fn get_displacement(&self) -> FloatTexture {
|
||||
todo!()
|
||||
}
|
||||
fn has_surface_scattering(&self) -> bool {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CoatedConductorMaterial;
|
||||
impl MaterialTrait for CoatedConductorMaterial {
|
||||
fn get_bsdf<'a, T: TextureEvaluator>(
|
||||
&self,
|
||||
_tex_eval: &T,
|
||||
_ctx: &MaterialEvalContext,
|
||||
_lambda: &SampledWavelengths,
|
||||
_scratch: &'a Bump,
|
||||
) -> BSDF<'a> {
|
||||
todo!()
|
||||
}
|
||||
fn get_bssrdf<'a, T>(
|
||||
&self,
|
||||
_tex_eval: &T,
|
||||
_ctx: &MaterialEvalContext,
|
||||
_lambda: &SampledWavelengths,
|
||||
) -> BSSRDF<'a> {
|
||||
todo!()
|
||||
}
|
||||
fn can_evaluate_textures(&self, _tex_eval: &dyn TextureEvaluator) -> bool {
|
||||
todo!()
|
||||
}
|
||||
fn get_normal_map(&self) -> Image {
|
||||
todo!()
|
||||
}
|
||||
fn get_displacement(&self) -> FloatTexture {
|
||||
todo!()
|
||||
}
|
||||
fn has_surface_scattering(&self) -> bool {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ConductorMaterial;
|
||||
impl MaterialTrait for ConductorMaterial {
|
||||
fn get_bsdf<'a, T: TextureEvaluator>(
|
||||
&self,
|
||||
_tex_eval: &T,
|
||||
_ctx: &MaterialEvalContext,
|
||||
_lambda: &SampledWavelengths,
|
||||
_scratch: &'a Bump,
|
||||
) -> BSDF<'a> {
|
||||
todo!()
|
||||
}
|
||||
fn get_bssrdf<'a, T>(
|
||||
&self,
|
||||
_tex_eval: &T,
|
||||
_ctx: &MaterialEvalContext,
|
||||
_lambda: &SampledWavelengths,
|
||||
) -> BSSRDF<'a> {
|
||||
todo!()
|
||||
}
|
||||
fn can_evaluate_textures(&self, _tex_eval: &dyn TextureEvaluator) -> bool {
|
||||
todo!()
|
||||
}
|
||||
fn get_normal_map(&self) -> Image {
|
||||
todo!()
|
||||
}
|
||||
fn get_displacement(&self) -> FloatTexture {
|
||||
todo!()
|
||||
}
|
||||
fn has_surface_scattering(&self) -> bool {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DielectricMaterial;
|
||||
impl MaterialTrait for DielectricMaterial {
|
||||
fn get_bsdf<'a, T: TextureEvaluator>(
|
||||
&self,
|
||||
_tex_eval: &T,
|
||||
_ctx: &MaterialEvalContext,
|
||||
_lambda: &SampledWavelengths,
|
||||
_scratch: &'a Bump,
|
||||
) -> BSDF<'a> {
|
||||
todo!()
|
||||
}
|
||||
fn get_bssrdf<'a, T>(
|
||||
&self,
|
||||
_tex_eval: &T,
|
||||
_ctx: &MaterialEvalContext,
|
||||
_lambda: &SampledWavelengths,
|
||||
) -> BSSRDF<'a> {
|
||||
todo!()
|
||||
}
|
||||
fn can_evaluate_textures(&self, _tex_eval: &dyn TextureEvaluator) -> bool {
|
||||
todo!()
|
||||
}
|
||||
fn get_normal_map(&self) -> Image {
|
||||
todo!()
|
||||
}
|
||||
fn get_displacement(&self) -> FloatTexture {
|
||||
todo!()
|
||||
}
|
||||
fn has_surface_scattering(&self) -> bool {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DiffuseMaterial {
|
||||
normal_map: Option<Arc<Image>>,
|
||||
displacement: FloatTexture,
|
||||
reflectance: SpectrumTexture,
|
||||
}
|
||||
|
||||
pub trait MaterialTrait: Send + Sync + std::fmt::Debug {}
|
||||
impl MaterialTrait for DiffuseMaterial {
|
||||
fn get_bsdf<'a, T: TextureEvaluator>(
|
||||
&self,
|
||||
tex_eval: &T,
|
||||
ctx: &MaterialEvalContext,
|
||||
lambda: &SampledWavelengths,
|
||||
scratch: &'a Bump,
|
||||
) -> BSDF<'a> {
|
||||
let r = tex_eval.evaluate_spectrum(&self.reflectance, ctx, lambda);
|
||||
let bxdf = scratch.alloc(DiffuseBxDF::new(r));
|
||||
BSDF::new(ctx.ns, ctx.dpdus, Some(bxdf))
|
||||
}
|
||||
|
||||
fn get_bssrdf<'a, T>(
|
||||
&self,
|
||||
_tex_eval: &T,
|
||||
_ctx: &MaterialEvalContext,
|
||||
_lambda: &SampledWavelengths,
|
||||
) -> BSSRDF<'a> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn can_evaluate_textures(&self, _tex_eval: &dyn TextureEvaluator) -> bool {
|
||||
todo!()
|
||||
}
|
||||
fn get_normal_map(&self) -> Image {
|
||||
todo!()
|
||||
}
|
||||
fn get_displacement(&self) -> FloatTexture {
|
||||
todo!()
|
||||
}
|
||||
fn has_surface_scattering(&self) -> bool {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DiffuseTransmissionMaterial;
|
||||
impl MaterialTrait for DiffuseTransmissionMaterial {
|
||||
fn get_bsdf<'a, T: TextureEvaluator>(
|
||||
&self,
|
||||
_tex_eval: &T,
|
||||
_ctx: &MaterialEvalContext,
|
||||
_lambda: &SampledWavelengths,
|
||||
_scratch: &'a Bump,
|
||||
) -> BSDF<'a> {
|
||||
todo!()
|
||||
}
|
||||
fn get_bssrdf<'a, T>(
|
||||
&self,
|
||||
_tex_eval: &T,
|
||||
_ctx: &MaterialEvalContext,
|
||||
_lambda: &SampledWavelengths,
|
||||
) -> BSSRDF<'a> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn can_evaluate_textures(&self, _tex_eval: &dyn TextureEvaluator) -> bool {
|
||||
todo!()
|
||||
}
|
||||
fn get_normal_map(&self) -> Image {
|
||||
todo!()
|
||||
}
|
||||
fn get_displacement(&self) -> FloatTexture {
|
||||
todo!()
|
||||
}
|
||||
fn has_surface_scattering(&self) -> bool {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct HairMaterial;
|
||||
impl MaterialTrait for HairMaterial {
|
||||
fn get_bsdf<'a, T: TextureEvaluator>(
|
||||
&self,
|
||||
_tex_eval: &T,
|
||||
_ctx: &MaterialEvalContext,
|
||||
_lambda: &SampledWavelengths,
|
||||
_scratch: &'a Bump,
|
||||
) -> BSDF<'a> {
|
||||
todo!()
|
||||
}
|
||||
fn get_bssrdf<'a, T>(
|
||||
&self,
|
||||
_tex_eval: &T,
|
||||
_ctx: &MaterialEvalContext,
|
||||
_lambda: &SampledWavelengths,
|
||||
) -> BSSRDF<'a> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn can_evaluate_textures(&self, _tex_eval: &dyn TextureEvaluator) -> bool {
|
||||
todo!()
|
||||
}
|
||||
fn get_normal_map(&self) -> Image {
|
||||
todo!()
|
||||
}
|
||||
fn get_displacement(&self) -> FloatTexture {
|
||||
todo!()
|
||||
}
|
||||
fn has_surface_scattering(&self) -> bool {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct MeasuredMaterial;
|
||||
impl MaterialTrait for MeasuredMaterial {
|
||||
fn get_bsdf<'a, T: TextureEvaluator>(
|
||||
&self,
|
||||
_tex_eval: &T,
|
||||
_ctx: &MaterialEvalContext,
|
||||
_lambda: &SampledWavelengths,
|
||||
_scratch: &'a Bump,
|
||||
) -> BSDF<'a> {
|
||||
todo!()
|
||||
}
|
||||
fn get_bssrdf<'a, T>(
|
||||
&self,
|
||||
_tex_eval: &T,
|
||||
_ctx: &MaterialEvalContext,
|
||||
_lambda: &SampledWavelengths,
|
||||
) -> BSSRDF<'a> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn can_evaluate_textures(&self, _tex_eval: &dyn TextureEvaluator) -> bool {
|
||||
todo!()
|
||||
}
|
||||
fn get_normal_map(&self) -> Image {
|
||||
todo!()
|
||||
}
|
||||
fn get_displacement(&self) -> FloatTexture {
|
||||
todo!()
|
||||
}
|
||||
fn has_surface_scattering(&self) -> bool {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SubsurfaceMaterial;
|
||||
impl MaterialTrait for SubsurfaceMaterial {
|
||||
fn get_bsdf<'a, T: TextureEvaluator>(
|
||||
&self,
|
||||
_tex_eval: &T,
|
||||
_ctx: &MaterialEvalContext,
|
||||
_lambda: &SampledWavelengths,
|
||||
_scratch: &'a Bump,
|
||||
) -> BSDF<'a> {
|
||||
todo!()
|
||||
}
|
||||
fn get_bssrdf<'a, T>(
|
||||
&self,
|
||||
_tex_eval: &T,
|
||||
_ctx: &MaterialEvalContext,
|
||||
_lambda: &SampledWavelengths,
|
||||
) -> BSSRDF<'a> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn can_evaluate_textures(&self, _tex_eval: &dyn TextureEvaluator) -> bool {
|
||||
todo!()
|
||||
}
|
||||
fn get_normal_map(&self) -> Image {
|
||||
todo!()
|
||||
}
|
||||
fn get_displacement(&self) -> FloatTexture {
|
||||
todo!()
|
||||
}
|
||||
fn has_surface_scattering(&self) -> bool {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ThinDielectricMaterial;
|
||||
impl MaterialTrait for ThinDielectricMaterial {
|
||||
fn get_bsdf<'a, T: TextureEvaluator>(
|
||||
&self,
|
||||
_tex_eval: &T,
|
||||
_ctx: &MaterialEvalContext,
|
||||
_lambda: &SampledWavelengths,
|
||||
_scratch: &'a Bump,
|
||||
) -> BSDF<'a> {
|
||||
todo!()
|
||||
}
|
||||
fn get_bssrdf<'a, T>(
|
||||
&self,
|
||||
_tex_eval: &T,
|
||||
_ctx: &MaterialEvalContext,
|
||||
_lambda: &SampledWavelengths,
|
||||
) -> BSSRDF<'a> {
|
||||
todo!()
|
||||
}
|
||||
fn can_evaluate_textures(&self, _tex_eval: &dyn TextureEvaluator) -> bool {
|
||||
todo!()
|
||||
}
|
||||
fn get_normal_map(&self) -> Image {
|
||||
todo!()
|
||||
}
|
||||
fn get_displacement(&self) -> FloatTexture {
|
||||
todo!()
|
||||
}
|
||||
fn has_surface_scattering(&self) -> bool {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct MixMaterial;
|
||||
impl MaterialTrait for MixMaterial {
|
||||
fn get_bsdf<'a, T: TextureEvaluator>(
|
||||
&self,
|
||||
_tex_eval: &T,
|
||||
_ctx: &MaterialEvalContext,
|
||||
_lambda: &SampledWavelengths,
|
||||
_scratch: &'a Bump,
|
||||
) -> BSDF<'a> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn get_bssrdf<'a, T>(
|
||||
&self,
|
||||
_tex_eval: &T,
|
||||
_ctx: &MaterialEvalContext,
|
||||
_lambda: &SampledWavelengths,
|
||||
) -> BSSRDF<'a> {
|
||||
todo!()
|
||||
}
|
||||
fn can_evaluate_textures(&self, _tex_eval: &dyn TextureEvaluator) -> bool {
|
||||
todo!()
|
||||
}
|
||||
fn get_normal_map(&self) -> Image {
|
||||
todo!()
|
||||
}
|
||||
fn get_displacement(&self) -> FloatTexture {
|
||||
todo!()
|
||||
}
|
||||
fn has_surface_scattering(&self) -> bool {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
pub mod aggregates;
|
||||
pub mod bssrdf;
|
||||
pub mod bxdf;
|
||||
pub mod cie;
|
||||
pub mod film;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use crate::core::aggregates::LinearBVHNode;
|
||||
use crate::core::interaction::{Interaction, SurfaceInteraction};
|
||||
use crate::core::material::MaterialTrait;
|
||||
use crate::core::material::Material;
|
||||
use crate::core::medium::{Medium, MediumInterface};
|
||||
use crate::core::pbrt::Float;
|
||||
use crate::core::texture::{FloatTextureTrait, TextureEvalContext};
|
||||
|
|
@ -21,7 +21,7 @@ pub trait PrimitiveTrait: Send + Sync + std::fmt::Debug {
|
|||
#[derive(Debug, Clone)]
|
||||
pub struct GeometricPrimitive {
|
||||
shape: Arc<Shape>,
|
||||
material: Arc<dyn MaterialTrait>,
|
||||
material: Arc<Material>,
|
||||
area_light: Arc<Light>,
|
||||
medium_interface: Arc<MediumInterface>,
|
||||
alpha: Option<Arc<dyn FloatTextureTrait>>,
|
||||
|
|
@ -81,7 +81,7 @@ impl PrimitiveTrait for GeometricPrimitive {
|
|||
#[derive(Debug, Clone)]
|
||||
pub struct SimplePrimitiv {
|
||||
shape: Arc<Shape>,
|
||||
material: Arc<dyn MaterialTrait>,
|
||||
material: Arc<Material>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
use crate::core::interaction::SurfaceInteraction;
|
||||
use crate::core::pbrt::Float;
|
||||
use crate::core::pbrt::{INV_2_PI, INV_PI, PI};
|
||||
use crate::geometry::{Normal3f, Point2f, Point3f, Vector3f, VectorLike};
|
||||
use crate::geometry::{Normal3f, Point2f, Point3f, Vector2f, Vector3f, VectorLike};
|
||||
use crate::geometry::{spherical_phi, spherical_theta};
|
||||
use crate::image::WrapMode;
|
||||
use crate::spectra::{SampledSpectrum, Spectrum};
|
||||
use crate::spectra::{
|
||||
RGBAlbedoSpectrum, RGBIlluminantSpectrum, RGBUnboundedSpectrum, SampledSpectrum, Spectrum,
|
||||
};
|
||||
use crate::spectra::{SampledWavelengths, SpectrumTrait};
|
||||
use crate::utils::color::ColorEncoding;
|
||||
use crate::utils::color::{ColorEncoding, RGB};
|
||||
use crate::utils::math::square;
|
||||
use crate::utils::mipmap::MIPMap;
|
||||
use crate::utils::mipmap::{MIPMapFilterOptions, MIPMapSample};
|
||||
|
|
@ -16,6 +18,7 @@ use std::collections::HashMap;
|
|||
use std::sync::{Arc, Mutex, OnceLock};
|
||||
|
||||
use enum_dispatch::enum_dispatch;
|
||||
use std::path::Path;
|
||||
|
||||
struct TexCoord2D {
|
||||
st: Point2f,
|
||||
|
|
@ -27,10 +30,11 @@ struct TexCoord2D {
|
|||
|
||||
#[enum_dispatch]
|
||||
trait TextureMapping2DTrait {
|
||||
fn map(&self, ctx: TextureEvalContext) -> TexCoord2D;
|
||||
fn map(&self, ctx: &TextureEvalContext) -> TexCoord2D;
|
||||
}
|
||||
|
||||
#[enum_dispatch(TextureMapping2DTrait)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum TextureMapping2D {
|
||||
UV(UVMapping),
|
||||
Spherical(SphericalMapping),
|
||||
|
|
@ -38,6 +42,7 @@ pub enum TextureMapping2D {
|
|||
Planar(PlanarMapping),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct UVMapping {
|
||||
su: Float,
|
||||
sv: Float,
|
||||
|
|
@ -57,7 +62,7 @@ impl Default for UVMapping {
|
|||
}
|
||||
|
||||
impl TextureMapping2DTrait for UVMapping {
|
||||
fn map(&self, ctx: TextureEvalContext) -> TexCoord2D {
|
||||
fn map(&self, ctx: &TextureEvalContext) -> TexCoord2D {
|
||||
let dsdx = self.su * ctx.dudx;
|
||||
let dsdy = self.su * ctx.dudy;
|
||||
let dtdx = self.sv * ctx.dvdx;
|
||||
|
|
@ -72,6 +77,8 @@ impl TextureMapping2DTrait for UVMapping {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SphericalMapping {
|
||||
texture_from_render: Transform<Float>,
|
||||
}
|
||||
|
|
@ -85,7 +92,7 @@ impl SphericalMapping {
|
|||
}
|
||||
|
||||
impl TextureMapping2DTrait for SphericalMapping {
|
||||
fn map(&self, ctx: TextureEvalContext) -> TexCoord2D {
|
||||
fn map(&self, ctx: &TextureEvalContext) -> TexCoord2D {
|
||||
let pt = self.texture_from_render.apply_to_point(ctx.p);
|
||||
let x2y2 = square(pt.x()) + square(pt.y());
|
||||
let sqrtx2y2 = x2y2.sqrt();
|
||||
|
|
@ -114,6 +121,7 @@ impl TextureMapping2DTrait for SphericalMapping {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CylindricalMapping {
|
||||
texture_from_render: Transform<Float>,
|
||||
}
|
||||
|
|
@ -127,7 +135,7 @@ impl CylindricalMapping {
|
|||
}
|
||||
|
||||
impl TextureMapping2DTrait for CylindricalMapping {
|
||||
fn map(&self, ctx: TextureEvalContext) -> TexCoord2D {
|
||||
fn map(&self, ctx: &TextureEvalContext) -> TexCoord2D {
|
||||
let pt = self.texture_from_render.apply_to_point(ctx.p);
|
||||
let x2y2 = square(pt.x()) + square(pt.y());
|
||||
let dsdp = Vector3f::new(-pt.y(), pt.x(), 0.) / (2. * PI * x2y2);
|
||||
|
|
@ -149,6 +157,7 @@ impl TextureMapping2DTrait for CylindricalMapping {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PlanarMapping {
|
||||
texture_from_render: Transform<Float>,
|
||||
vs: Vector3f,
|
||||
|
|
@ -176,7 +185,7 @@ impl PlanarMapping {
|
|||
}
|
||||
|
||||
impl TextureMapping2DTrait for PlanarMapping {
|
||||
fn map(&self, ctx: TextureEvalContext) -> TexCoord2D {
|
||||
fn map(&self, ctx: &TextureEvalContext) -> TexCoord2D {
|
||||
let vec: Vector3f = self.texture_from_render.apply_to_point(ctx.p).into();
|
||||
let dpdx = self.texture_from_render.apply_to_vector(ctx.dpdx);
|
||||
let dpdy = self.texture_from_render.apply_to_vector(ctx.dpdy);
|
||||
|
|
@ -234,6 +243,7 @@ impl TextureMapping3DTrait for PointTransformMapping {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct TextureEvalContext {
|
||||
p: Point3f,
|
||||
dpdx: Vector3f,
|
||||
|
|
@ -493,10 +503,68 @@ impl SpectrumTextureTrait for SpectrumCheckerboardTexture {
|
|||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SpectrumImageTexture;
|
||||
pub enum SpectrumType {
|
||||
Illuminant,
|
||||
Albedo,
|
||||
Unbounded,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SpectrumImageTexture {
|
||||
base: ImageTextureBase,
|
||||
spectrum_type: SpectrumType,
|
||||
}
|
||||
|
||||
impl SpectrumImageTexture {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(
|
||||
mapping: TextureMapping2D,
|
||||
filename: String,
|
||||
filter_options: MIPMapFilterOptions,
|
||||
wrap_mode: WrapMode,
|
||||
scale: Float,
|
||||
invert: bool,
|
||||
encoding: ColorEncoding,
|
||||
spectrum_type: SpectrumType,
|
||||
) -> Self {
|
||||
let base = ImageTextureBase::new(
|
||||
mapping,
|
||||
filename,
|
||||
filter_options,
|
||||
wrap_mode,
|
||||
scale,
|
||||
invert,
|
||||
encoding,
|
||||
);
|
||||
|
||||
Self {
|
||||
base,
|
||||
spectrum_type,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SpectrumTextureTrait for SpectrumImageTexture {
|
||||
fn evaluate(&self, _ctx: &TextureEvalContext, _lambda: &SampledWavelengths) -> SampledSpectrum {
|
||||
todo!()
|
||||
fn evaluate(&self, ctx: &TextureEvalContext, lambda: &SampledWavelengths) -> SampledSpectrum {
|
||||
let mut c = self.base.mapping.map(ctx);
|
||||
c.st[1] = 1. - c.st[1];
|
||||
let dst0 = Vector2f::new(c.dsdx, c.dtdx);
|
||||
let dst1 = Vector2f::new(c.dsdy, c.dtdy);
|
||||
let rgb_unclamp = self.base.scale * self.base.mipmap.filter::<RGB>(c.st, dst0, dst1);
|
||||
let rgb = RGB::clamp_zero(rgb_unclamp);
|
||||
if let Some(cs) = self.base.mipmap.get_rgb_colorspace() {
|
||||
match self.spectrum_type {
|
||||
SpectrumType::Unbounded => {
|
||||
return RGBUnboundedSpectrum::new(&cs, rgb).sample(lambda);
|
||||
}
|
||||
SpectrumType::Albedo => {
|
||||
return RGBAlbedoSpectrum::new(&cs, rgb).sample(lambda);
|
||||
}
|
||||
_ => return RGBIlluminantSpectrum::new(&cs, rgb).sample(lambda),
|
||||
}
|
||||
}
|
||||
assert!(rgb[0] == rgb[1] && rgb[1] == rgb[2]);
|
||||
SampledSpectrum::new(rgb[0])
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -595,4 +663,116 @@ struct TexInfo {
|
|||
|
||||
static TEXTURE_CACHE: OnceLock<Mutex<HashMap<TexInfo, Arc<MIPMap>>>> = OnceLock::new();
|
||||
|
||||
pub struct ImageTextureBase {}
|
||||
fn get_texture_cache() -> &'static Mutex<HashMap<TexInfo, Arc<MIPMap>>> {
|
||||
TEXTURE_CACHE.get_or_init(|| Mutex::new(HashMap::new()))
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ImageTextureBase {
|
||||
mapping: TextureMapping2D,
|
||||
filename: String,
|
||||
scale: Float,
|
||||
invert: bool,
|
||||
mipmap: Arc<MIPMap>,
|
||||
}
|
||||
|
||||
impl ImageTextureBase {
|
||||
pub fn new(
|
||||
mapping: TextureMapping2D,
|
||||
filename: String,
|
||||
filter_options: MIPMapFilterOptions,
|
||||
wrap_mode: WrapMode,
|
||||
scale: Float,
|
||||
invert: bool,
|
||||
encoding: ColorEncoding,
|
||||
) -> Self {
|
||||
let tex_info = TexInfo {
|
||||
filename: filename.clone(),
|
||||
filter_options,
|
||||
wrap_mode,
|
||||
encoding,
|
||||
};
|
||||
|
||||
let cache_mutex = get_texture_cache();
|
||||
|
||||
{
|
||||
let cache = cache_mutex.lock().unwrap();
|
||||
if let Some(mipmap) = cache.get(&tex_info) {
|
||||
return Self {
|
||||
mapping,
|
||||
filename,
|
||||
scale,
|
||||
invert,
|
||||
mipmap: mipmap.clone(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
let path = Path::new(&filename);
|
||||
let mipmap_raw = MIPMap::create_from_file(&path, filter_options, wrap_mode, encoding)
|
||||
.expect("Failed to create MIPMap from file");
|
||||
|
||||
let mipmap_arc = Arc::new(mipmap_raw);
|
||||
|
||||
{
|
||||
let mut cache = cache_mutex.lock().unwrap();
|
||||
|
||||
let stored_mipmap = cache.entry(tex_info).or_insert(mipmap_arc);
|
||||
|
||||
Self {
|
||||
mapping,
|
||||
filename,
|
||||
scale,
|
||||
invert,
|
||||
mipmap: stored_mipmap.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear_cache() {
|
||||
let mut cache = get_texture_cache().lock().unwrap();
|
||||
cache.clear();
|
||||
}
|
||||
|
||||
pub fn multiply_scale(&mut self, s: Float) {
|
||||
self.scale *= s;
|
||||
}
|
||||
}
|
||||
|
||||
pub trait TextureEvaluator: Send + Sync {
|
||||
fn evaluate_float(&self, tex: &FloatTexture, ctx: &TextureEvalContext) -> Float;
|
||||
fn evaluate_spectrum(
|
||||
&self,
|
||||
tex: &SpectrumTexture,
|
||||
ctx: &TextureEvalContext,
|
||||
lambda: &SampledWavelengths,
|
||||
) -> SampledSpectrum;
|
||||
|
||||
fn can_evaluate(&self, _ftex: &[&FloatTexture], _stex: &[&SpectrumTexture]) -> bool;
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Default)]
|
||||
pub struct UniversalTextureEvaluator;
|
||||
|
||||
impl TextureEvaluator for UniversalTextureEvaluator {
|
||||
fn evaluate_float(&self, tex: &FloatTexture, ctx: &TextureEvalContext) -> Float {
|
||||
tex.evaluate(ctx)
|
||||
}
|
||||
|
||||
fn evaluate_spectrum(
|
||||
&self,
|
||||
tex: &SpectrumTexture,
|
||||
ctx: &TextureEvalContext,
|
||||
lambda: &SampledWavelengths,
|
||||
) -> SampledSpectrum {
|
||||
tex.evaluate(ctx, lambda)
|
||||
}
|
||||
|
||||
fn can_evaluate(
|
||||
&self,
|
||||
_float_textures: &[&FloatTexture],
|
||||
_spectrum_textures: &[&SpectrumTexture],
|
||||
) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
use crate::core::pbrt::Float;
|
||||
use crate::image::{Image, ImageMetadata, PixelData, PixelFormat, Point2i, WrapMode};
|
||||
use crate::image::{
|
||||
Image, ImageAndMetadata, ImageMetadata, PixelData, PixelFormat, Point2i, WrapMode,
|
||||
};
|
||||
use crate::utils::color::{ColorEncoding, LINEAR, SRGB};
|
||||
use crate::utils::error::ImageError;
|
||||
use anyhow::{Context, Result, bail};
|
||||
|
|
@ -11,7 +13,7 @@ use std::io::{BufRead, BufReader, BufWriter, Read, Write};
|
|||
use std::path::Path;
|
||||
|
||||
impl Image {
|
||||
pub fn read(path: &Path, encoding: Option<ColorEncoding>) -> Result<Self> {
|
||||
pub fn read(path: &Path, encoding: Option<ColorEncoding>) -> Result<ImageAndMetadata> {
|
||||
let ext = path
|
||||
.extension()
|
||||
.and_then(|s| s.to_str())
|
||||
|
|
@ -179,7 +181,7 @@ impl Image {
|
|||
}
|
||||
}
|
||||
|
||||
fn read_generic(path: &Path, encoding: Option<ColorEncoding>) -> Result<Image> {
|
||||
fn read_generic(path: &Path, encoding: Option<ColorEncoding>) -> Result<ImageAndMetadata> {
|
||||
// 1. Load via 'image' crate
|
||||
let dyn_img = ImageReader::open(path)
|
||||
.with_context(|| format!("Failed to open image: {:?}", path))?
|
||||
|
|
@ -189,50 +191,51 @@ fn read_generic(path: &Path, encoding: Option<ColorEncoding>) -> Result<Image> {
|
|||
let h = dyn_img.height() as i32;
|
||||
let res = Point2i::new(w, h);
|
||||
|
||||
// 2. Convert to PBRT Image
|
||||
// Check if it was loaded as high precision (HDR/EXR via image crate) or standard
|
||||
match dyn_img {
|
||||
DynamicImage::ImageRgb32F(buf) => Ok(Image {
|
||||
let image = match dyn_img {
|
||||
DynamicImage::ImageRgb32F(buf) => Image {
|
||||
format: PixelFormat::F32,
|
||||
resolution: res,
|
||||
channel_names: vec!["R".into(), "G".into(), "B".into()],
|
||||
encoding: LINEAR,
|
||||
pixels: PixelData::F32(buf.into_raw()),
|
||||
}),
|
||||
DynamicImage::ImageRgba32F(buf) => Ok(Image {
|
||||
},
|
||||
DynamicImage::ImageRgba32F(buf) => Image {
|
||||
format: PixelFormat::F32,
|
||||
resolution: res,
|
||||
channel_names: vec!["R".into(), "G".into(), "B".into(), "A".into()],
|
||||
encoding: LINEAR,
|
||||
pixels: PixelData::F32(buf.into_raw()),
|
||||
}),
|
||||
},
|
||||
_ => {
|
||||
// Default to RGB8 for everything else (PNG, JPG)
|
||||
// Note: PBRT handles alpha, so we check if the source had alpha
|
||||
if dyn_img.color().has_alpha() {
|
||||
let buf = dyn_img.to_rgba8();
|
||||
Ok(Image {
|
||||
Image {
|
||||
format: PixelFormat::U8,
|
||||
resolution: res,
|
||||
channel_names: vec!["R".into(), "G".into(), "B".into(), "A".into()],
|
||||
encoding: encoding.unwrap_or(SRGB),
|
||||
pixels: PixelData::U8(buf.into_raw()),
|
||||
})
|
||||
}
|
||||
} else {
|
||||
let buf = dyn_img.to_rgb8();
|
||||
Ok(Image {
|
||||
Image {
|
||||
format: PixelFormat::U8,
|
||||
resolution: res,
|
||||
channel_names: vec!["R".into(), "G".into(), "B".into()],
|
||||
encoding: encoding.unwrap_or(SRGB),
|
||||
pixels: PixelData::U8(buf.into_raw()),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let metadata = ImageMetadata::default();
|
||||
Ok(ImageAndMetadata { image, metadata })
|
||||
}
|
||||
|
||||
fn read_exr(path: &Path) -> Result<Image> {
|
||||
fn read_exr(path: &Path) -> Result<ImageAndMetadata> {
|
||||
let image = read_first_rgba_layer_from_file(
|
||||
path,
|
||||
|resolution, _| {
|
||||
|
|
@ -254,16 +257,19 @@ fn read_exr(path: &Path) -> Result<Image> {
|
|||
let w = image.layer_data.size.width() as i32;
|
||||
let h = image.layer_data.size.height() as i32;
|
||||
|
||||
Ok(Image {
|
||||
let image = Image {
|
||||
format: PixelFormat::F32,
|
||||
resolution: Point2i::new(w, h),
|
||||
channel_names: vec!["R".into(), "G".into(), "B".into(), "A".into()],
|
||||
encoding: LINEAR,
|
||||
pixels: PixelData::F32(image.layer_data.channel_data.pixels),
|
||||
})
|
||||
};
|
||||
|
||||
let metadata = ImageMetadata::default();
|
||||
Ok(ImageAndMetadata { image, metadata })
|
||||
}
|
||||
|
||||
fn read_pfm(path: &Path) -> Result<Image> {
|
||||
fn read_pfm(path: &Path) -> Result<ImageAndMetadata> {
|
||||
let file = File::open(path)?;
|
||||
let mut reader = BufReader::new(file);
|
||||
|
||||
|
|
@ -335,11 +341,14 @@ fn read_pfm(path: &Path) -> Result<Image> {
|
|||
vec!["R".into(), "G".into(), "B".into()]
|
||||
};
|
||||
|
||||
Ok(Image {
|
||||
let image = Image {
|
||||
format: PixelFormat::F32,
|
||||
resolution: Point2i::new(w, h),
|
||||
channel_names: names,
|
||||
encoding: LINEAR,
|
||||
pixels: PixelData::F32(pixels),
|
||||
})
|
||||
};
|
||||
|
||||
let metadata = ImageMetadata::default();
|
||||
Ok(ImageAndMetadata { image, metadata })
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ pub struct ImageMetadata {
|
|||
pub full_resolution: Option<Point2i>,
|
||||
pub samples_per_pixel: Option<i32>,
|
||||
pub mse: Option<Float>,
|
||||
pub color_space: Option<&'static RGBColorSpace>,
|
||||
pub colorspace: Option<RGBColorSpace>,
|
||||
pub strings: HashMap<String, String>,
|
||||
pub string_vectors: HashMap<String, Vec<String>>,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ pub mod sphere;
|
|||
pub mod triangle;
|
||||
|
||||
use crate::core::interaction::{Interaction, MediumInteraction, SurfaceInteraction};
|
||||
use crate::core::material::MaterialTrait;
|
||||
use crate::core::material::Material;
|
||||
use crate::core::medium::{Medium, MediumInterface};
|
||||
use crate::core::pbrt::{Float, PI};
|
||||
use crate::geometry::{
|
||||
|
|
@ -177,7 +177,7 @@ impl ShapeIntersection {
|
|||
|
||||
pub fn set_intersection_properties(
|
||||
&mut self,
|
||||
mtl: Arc<dyn MaterialTrait>,
|
||||
mtl: Arc<Material>,
|
||||
area: Arc<Light>,
|
||||
prim_medium_interface: Option<Arc<MediumInterface>>,
|
||||
ray_medium: Option<Arc<Medium>>,
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ impl RGBAlbedoSpectrum {
|
|||
}
|
||||
}
|
||||
|
||||
fn sample(&self, lambda: &SampledWavelengths) -> SampledSpectrum {
|
||||
pub fn sample(&self, lambda: &SampledWavelengths) -> SampledSpectrum {
|
||||
let mut s = SampledSpectrum::default();
|
||||
for i in 0..N_SPECTRUM_SAMPLES {
|
||||
s[i] = self.rsp.evaluate(lambda[i]);
|
||||
|
|
@ -78,7 +78,7 @@ pub struct RGBIlluminantSpectrum {
|
|||
}
|
||||
|
||||
impl RGBIlluminantSpectrum {
|
||||
pub fn new(cs: RGBColorSpace, rgb: RGB) -> Self {
|
||||
pub fn new(cs: &RGBColorSpace, rgb: RGB) -> Self {
|
||||
let illuminant = &cs.illuminant;
|
||||
let densely_sampled =
|
||||
DenselySampledSpectrum::from_spectrum(illuminant, LAMBDA_MIN, LAMBDA_MAX);
|
||||
|
|
@ -95,6 +95,13 @@ impl RGBIlluminantSpectrum {
|
|||
illuminant: Some(Arc::new(densely_sampled)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sample(&self, lambda: &SampledWavelengths) -> SampledSpectrum {
|
||||
if self.illuminant.is_none() {
|
||||
return SampledSpectrum::new(0.);
|
||||
}
|
||||
SampledSpectrum::from_fn(|i| self.scale * self.rsp.evaluate(lambda[i]))
|
||||
}
|
||||
}
|
||||
|
||||
impl SpectrumTrait for RGBIlluminantSpectrum {
|
||||
|
|
@ -153,11 +160,7 @@ impl RGBUnboundedSpectrum {
|
|||
}
|
||||
|
||||
pub fn sample(&self, lambda: &SampledWavelengths) -> SampledSpectrum {
|
||||
let mut s = SampledSpectrum::default();
|
||||
for i in 0..N_SPECTRUM_SAMPLES {
|
||||
s[i] = self.scale * self.rsp.evaluate(lambda[i]);
|
||||
}
|
||||
s
|
||||
SampledSpectrum::from_fn(|i| self.scale * self.rsp.evaluate(lambda[i]))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -68,18 +68,6 @@ impl SampledSpectrum {
|
|||
self.values.iter().sum::<Float>() / (N_SPECTRUM_SAMPLES as Float)
|
||||
}
|
||||
|
||||
pub fn safe_div(&self, rhs: SampledSpectrum) -> Self {
|
||||
let mut r = SampledSpectrum::new(0.0);
|
||||
for i in 0..N_SPECTRUM_SAMPLES {
|
||||
r.values[i] = if rhs[i] != 0.0 {
|
||||
self.values[i] / rhs.values[i]
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
pub fn exp(&self) -> SampledSpectrum {
|
||||
let values = self.values.map(|v| v.exp());
|
||||
let ret = Self { values };
|
||||
|
|
@ -99,6 +87,16 @@ impl SampledSpectrum {
|
|||
}
|
||||
result
|
||||
}
|
||||
|
||||
pub fn clamp_zero(s: &SampledSpectrum) -> Self {
|
||||
let ret = SampledSpectrum::from_fn(|i| s[i].max(0.));
|
||||
assert!(!ret.has_nans());
|
||||
ret
|
||||
}
|
||||
|
||||
pub fn safe_div(a: &SampledSpectrum, b: &SampledSpectrum) -> SampledSpectrum {
|
||||
SampledSpectrum::from_fn(|i| if b[i] != 0. { a[i] / b[i] } else { 0. })
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a SampledSpectrum {
|
||||
|
|
|
|||
|
|
@ -286,6 +286,10 @@ impl RGB {
|
|||
pub fn max(&self) -> Float {
|
||||
self.r.max(self.g).max(self.b)
|
||||
}
|
||||
|
||||
pub fn clamp_zero(rgb: Self) -> Self {
|
||||
RGB::new(rgb.r.max(0.), rgb.b.max(0.), rgb.b.max(0.))
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<usize> for RGB {
|
||||
|
|
|
|||
|
|
@ -10,11 +10,6 @@ use std::cmp::{Eq, PartialEq};
|
|||
use std::error::Error;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub enum ColorEncoding {
|
||||
Linear,
|
||||
SRGB,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RGBColorSpace {
|
||||
pub r: Point2f,
|
||||
|
|
|
|||
|
|
@ -243,6 +243,47 @@ pub fn linear_least_squares<const R: usize, const N: usize>(
|
|||
Ok((at_ai * atb).transpose())
|
||||
}
|
||||
|
||||
pub fn newton_bisection<P>(mut x0: Float, mut x1: Float, mut f: P) -> Float
|
||||
where
|
||||
P: FnMut(Float) -> (Float, Float),
|
||||
{
|
||||
assert!(x0 < x1);
|
||||
let f_eps = 1e-6;
|
||||
let x_eps = 1e-6;
|
||||
|
||||
let (fx0, _) = f(x0);
|
||||
let (fx1, _) = f(x1);
|
||||
if fx0.abs() < f_eps {
|
||||
return x0;
|
||||
}
|
||||
if fx1.abs() < f_eps {
|
||||
return x1;
|
||||
}
|
||||
let start_is_negative = fx0 < 0.0;
|
||||
|
||||
let mut x_mid = x0 + (x1 - x0) * -fx0 / (fx1 - fx0);
|
||||
|
||||
loop {
|
||||
if !(x0 < x_mid && x_mid < x1) {
|
||||
x_mid = (x0 + x1) / 2.0;
|
||||
}
|
||||
|
||||
let (fx_mid, dfx_mid) = f(x_mid);
|
||||
assert!(!fx_mid.is_nan());
|
||||
if start_is_negative == (fx_mid < 0.0) {
|
||||
x0 = x_mid;
|
||||
} else {
|
||||
x1 = x_mid;
|
||||
}
|
||||
|
||||
if (x1 - x0) < x_eps || fx_mid.abs() < f_eps {
|
||||
return x_mid;
|
||||
}
|
||||
|
||||
x_mid -= fx_mid / dfx_mid;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn wrap_equal_area_square(uv: &mut Point2f) -> Point2f {
|
||||
if uv[0] < 0. {
|
||||
uv[0] = -uv[0];
|
||||
|
|
@ -261,6 +302,66 @@ pub fn wrap_equal_area_square(uv: &mut Point2f) -> Point2f {
|
|||
*uv
|
||||
}
|
||||
|
||||
pub fn catmull_rom_weights(nodes: &[Float], x: Float) -> Option<(usize, [Float; 4])> {
|
||||
if nodes.len() < 4 {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Return None if x is out of bounds
|
||||
if x < *nodes.first()? || x > *nodes.last()? {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Search for interval idx containing x
|
||||
// partition_point returns the first index where predicate is false (!= <= x means > x)
|
||||
// Equivalent to upper_bound. We subtract 1 to get the interval index.
|
||||
let idx = nodes.partition_point(|&n| n <= x).saturating_sub(1);
|
||||
|
||||
// Safety clamp (though bounds check above handles most cases)
|
||||
let idx = idx.min(nodes.len() - 2);
|
||||
|
||||
let offset = idx.saturating_sub(1); // The C++ code uses idx - 1 for the offset
|
||||
let x0 = nodes[idx];
|
||||
let x1 = nodes[idx + 1];
|
||||
|
||||
// Compute t parameter and powers
|
||||
let t = (x - x0) / (x1 - x0);
|
||||
let t2 = t * t;
|
||||
let t3 = t2 * t;
|
||||
|
||||
let mut weights = [0.0; 4];
|
||||
|
||||
// Compute initial node weights w1 and w2
|
||||
weights[1] = 2.0 * t3 - 3.0 * t2 + 1.0;
|
||||
weights[2] = -2.0 * t3 + 3.0 * t2;
|
||||
|
||||
// Compute first node weight w0
|
||||
if idx > 0 {
|
||||
let w0 = (t3 - 2.0 * t2 + t) * (x1 - x0) / (x1 - nodes[idx - 1]);
|
||||
weights[0] = -w0;
|
||||
weights[2] += w0;
|
||||
} else {
|
||||
let w0 = t3 - 2.0 * t2 + t;
|
||||
weights[0] = 0.0;
|
||||
weights[1] -= w0;
|
||||
weights[2] += w0;
|
||||
}
|
||||
|
||||
// Compute last node weight w3
|
||||
if idx + 2 < nodes.len() {
|
||||
let w3 = (t3 - t2) * (x1 - x0) / (nodes[idx + 2] - x0);
|
||||
weights[1] -= w3;
|
||||
weights[3] = w3;
|
||||
} else {
|
||||
let w3 = t3 - t2;
|
||||
weights[1] -= w3;
|
||||
weights[2] += w3;
|
||||
weights[3] = 0.0;
|
||||
}
|
||||
|
||||
Some((offset, weights))
|
||||
}
|
||||
|
||||
pub fn equal_area_square_to_sphere(p: Point2f) -> Vector3f {
|
||||
assert!(p.x() >= 0. && p.x() <= 1. && p.y() >= 0. && p.y() <= 1.);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
use crate::core::pbrt::{Float, lerp};
|
||||
use crate::geometry::{Lerp, Point2f, Point2i, Vector2f, VectorLike};
|
||||
use crate::image::{Image, PixelData, PixelFormat, WrapMode, WrapMode2D};
|
||||
use crate::utils::color::RGB;
|
||||
use crate::image::{Image, ImageAndMetadata, PixelData, PixelFormat, WrapMode, WrapMode2D};
|
||||
use crate::utils::color::{ColorEncoding, RGB};
|
||||
use crate::utils::colorspace::RGBColorSpace;
|
||||
use crate::utils::math::{safe_sqrt, square};
|
||||
use std::path::Path;
|
||||
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::ops::{Add, Mul, Sub};
|
||||
|
|
@ -117,7 +119,7 @@ impl MIPMapSample for RGB {
|
|||
#[derive(Debug)]
|
||||
pub struct MIPMap {
|
||||
pyramid: Vec<Image>,
|
||||
color_space: Arc<RGBColorSpace>,
|
||||
color_space: Option<RGBColorSpace>,
|
||||
wrap_mode: WrapMode,
|
||||
options: MIPMapFilterOptions,
|
||||
}
|
||||
|
|
@ -125,7 +127,7 @@ pub struct MIPMap {
|
|||
impl MIPMap {
|
||||
pub fn new(
|
||||
image: Image,
|
||||
color_space: Arc<RGBColorSpace>,
|
||||
color_space: Option<RGBColorSpace>,
|
||||
wrap_mode: WrapMode,
|
||||
options: MIPMapFilterOptions,
|
||||
) -> Self {
|
||||
|
|
@ -146,7 +148,7 @@ impl MIPMap {
|
|||
self.pyramid.len()
|
||||
}
|
||||
|
||||
pub fn get_rgb_colorspace(&self) -> Arc<RGBColorSpace> {
|
||||
pub fn get_rgb_colorspace(&self) -> Option<RGBColorSpace> {
|
||||
self.color_space.clone()
|
||||
}
|
||||
|
||||
|
|
@ -178,7 +180,6 @@ impl MIPMap {
|
|||
let n_levels = self.levels() as Float;
|
||||
let level = n_levels - 1.0 + width.max(1e-8).log2();
|
||||
|
||||
// Handle very wide filter (return the 1x1 average pixel)
|
||||
if level >= n_levels - 1.0 {
|
||||
return self.texel(self.levels() - 1, Point2i::new(0, 0));
|
||||
}
|
||||
|
|
@ -187,7 +188,6 @@ impl MIPMap {
|
|||
|
||||
return match self.options.filter {
|
||||
FilterFunction::Point => {
|
||||
// Nearest Neighbor
|
||||
let resolution = self.level_resolution(i_level);
|
||||
let sti = Point2i::new(
|
||||
(st.x() * resolution.x() as Float - 0.5).round() as i32,
|
||||
|
|
@ -236,8 +236,17 @@ impl MIPMap {
|
|||
lerp(lod - ilod as Float, v0, v1)
|
||||
}
|
||||
|
||||
fn texel<T: MIPMapSample>(&self, _level: usize, _st: Point2i) -> T {
|
||||
todo!()
|
||||
fn texel<T: MIPMapSample>(&self, level: usize, st: Point2i) -> T {
|
||||
if level >= self.levels() {
|
||||
panic!("MIPMap level out of bounds");
|
||||
}
|
||||
|
||||
let image = &self.pyramid[level];
|
||||
let wrap_2d = WrapMode2D {
|
||||
uv: [self.wrap_mode; 2],
|
||||
};
|
||||
|
||||
T::sample_texel(image, st, wrap_2d)
|
||||
}
|
||||
|
||||
fn bilerp<T: MIPMapSample>(&self, level: usize, st: Point2f) -> T {
|
||||
|
|
@ -250,11 +259,214 @@ impl MIPMap {
|
|||
|
||||
fn ewa<T: MIPMapSample>(
|
||||
&self,
|
||||
_scale: usize,
|
||||
_st: Point2f,
|
||||
_dst0: Vector2f,
|
||||
_dst1: Vector2f,
|
||||
level: usize,
|
||||
mut st: Point2f,
|
||||
mut dst0: Vector2f,
|
||||
mut dst1: Vector2f,
|
||||
) -> T {
|
||||
todo!()
|
||||
if level > self.levels() {
|
||||
return self.texel(self.levels() - 1, Point2i::new(0, 0));
|
||||
}
|
||||
let level_res = self.level_resolution(level);
|
||||
st[0] = st[0] * level_res[0] as Float - 0.5;
|
||||
st[1] = st[1] * level_res[1] as Float - 0.5;
|
||||
dst0[0] *= level_res[0] as Float;
|
||||
dst0[1] *= level_res[1] as Float;
|
||||
dst1[0] *= level_res[0] as Float;
|
||||
dst1[1] *= level_res[1] as Float;
|
||||
let mut a = square(dst0[1]) + square(dst1[1]) + 1.;
|
||||
let mut b = -2. * (dst0[0] + dst0[1] + dst1[1]);
|
||||
let mut c = square(dst0[0]) + square(dst1[0]) + 1.;
|
||||
let inv_f = 1. / (a * c - square(b) * 0.25);
|
||||
a *= inv_f;
|
||||
b *= inv_f;
|
||||
c *= inv_f;
|
||||
|
||||
let det = -square(b) + 4. * a * c;
|
||||
let inv_det = 1. / det;
|
||||
let u_sqrt = safe_sqrt(det * c);
|
||||
let v_sqrt = safe_sqrt(det * a);
|
||||
let s0: i32 = (st[0] - 2. * inv_det * u_sqrt).ceil() as i32;
|
||||
let s1: i32 = (st[0] + 2. * inv_det * u_sqrt).floor() as i32;
|
||||
let t0: i32 = (st[1] - 2. * inv_det * v_sqrt).ceil() as i32;
|
||||
let t1: i32 = (st[1] + 2. * inv_det * v_sqrt).floor() as i32;
|
||||
let mut sum = T::zero();
|
||||
let mut sum_wts = 0.;
|
||||
for it in t0..=t1 {
|
||||
let tt = it as Float - st[1];
|
||||
for is in s0..=s1 {
|
||||
let ss = is as Float - st[0];
|
||||
|
||||
// Compute squared radius and filter texel if inside ellipse
|
||||
let r2 = a * square(ss) + b * ss * tt + c * square(tt);
|
||||
|
||||
if r2 < 1.0 {
|
||||
// Map r2 to LUT index
|
||||
let index = (r2 * MIP_FILTER_LUT_SIZE as Float)
|
||||
.min((MIP_FILTER_LUT_SIZE - 1) as Float)
|
||||
as usize;
|
||||
|
||||
let weight = MIP_FILTER_LUT[index];
|
||||
|
||||
// Accumulate
|
||||
sum = sum + self.texel::<T>(level, Point2i::new(is, it)) * weight;
|
||||
sum_wts += weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sum * (1. / sum_wts)
|
||||
}
|
||||
|
||||
pub fn create_from_file(
|
||||
filename: &Path,
|
||||
options: MIPMapFilterOptions,
|
||||
wrap_mode: WrapMode,
|
||||
encoding: ColorEncoding,
|
||||
) -> Result<MIPMap, ()> {
|
||||
let image_and_metadata = Image::read(filename, Some(encoding)).unwrap();
|
||||
let image = image_and_metadata.image;
|
||||
// if image.n_channels() != 1 {
|
||||
// let rgba_dsc = image.all_channels_desc();
|
||||
// }
|
||||
Ok(MIPMap::new(
|
||||
image,
|
||||
image_and_metadata.metadata.colorspace,
|
||||
wrap_mode,
|
||||
options,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
static MIP_FILTER_LUT_SIZE: usize = 128;
|
||||
static MIP_FILTER_LUT: [Float; MIP_FILTER_LUT_SIZE] = [
|
||||
// MIPMap EWA Lookup Table Values
|
||||
0.864664733,
|
||||
0.849040031,
|
||||
0.83365953,
|
||||
0.818519294,
|
||||
0.80361563,
|
||||
0.788944781,
|
||||
0.774503231,
|
||||
0.760287285,
|
||||
0.746293485,
|
||||
0.732518315,
|
||||
0.718958378,
|
||||
0.705610275,
|
||||
0.692470789,
|
||||
0.679536581,
|
||||
0.666804492,
|
||||
0.654271305,
|
||||
0.641933978,
|
||||
0.629789352,
|
||||
0.617834508,
|
||||
0.606066525,
|
||||
0.594482362,
|
||||
0.583079159,
|
||||
0.571854174,
|
||||
0.560804546,
|
||||
0.549927592,
|
||||
0.539220572,
|
||||
0.528680861,
|
||||
0.518305838,
|
||||
0.50809288,
|
||||
0.498039544,
|
||||
0.488143265,
|
||||
0.478401601,
|
||||
0.468812168,
|
||||
0.45937258,
|
||||
0.450080454,
|
||||
0.440933526,
|
||||
0.431929469,
|
||||
0.423066139,
|
||||
0.414341331,
|
||||
0.405752778,
|
||||
0.397298455,
|
||||
0.388976216,
|
||||
0.380784035,
|
||||
0.372719884,
|
||||
0.364781618,
|
||||
0.356967449,
|
||||
0.34927541,
|
||||
0.341703475,
|
||||
0.334249914,
|
||||
0.32691282,
|
||||
0.319690347,
|
||||
0.312580705,
|
||||
0.305582166,
|
||||
0.298692942,
|
||||
0.291911423,
|
||||
0.285235822,
|
||||
0.278664529,
|
||||
0.272195935,
|
||||
0.265828371,
|
||||
0.259560347,
|
||||
0.253390193,
|
||||
0.247316495,
|
||||
0.241337672,
|
||||
0.235452279,
|
||||
0.229658857,
|
||||
0.223955944,
|
||||
0.21834214,
|
||||
0.212816045,
|
||||
0.207376286,
|
||||
0.202021524,
|
||||
0.196750447,
|
||||
0.191561714,
|
||||
0.186454013,
|
||||
0.181426153,
|
||||
0.176476851,
|
||||
0.171604887,
|
||||
0.166809067,
|
||||
0.162088141,
|
||||
0.157441005,
|
||||
0.152866468,
|
||||
0.148363426,
|
||||
0.143930718,
|
||||
0.139567271,
|
||||
0.135272011,
|
||||
0.131043866,
|
||||
0.126881793,
|
||||
0.122784719,
|
||||
0.11875169,
|
||||
0.114781633,
|
||||
0.11087364,
|
||||
0.107026696,
|
||||
0.103239879,
|
||||
0.0995122194,
|
||||
0.0958427936,
|
||||
0.0922307223,
|
||||
0.0886750817,
|
||||
0.0851749927,
|
||||
0.0817295909,
|
||||
0.0783380121,
|
||||
0.0749994367,
|
||||
0.0717130303,
|
||||
0.0684779733,
|
||||
0.0652934611,
|
||||
0.0621587038,
|
||||
0.0590728968,
|
||||
0.0560353249,
|
||||
0.0530452281,
|
||||
0.0501018465,
|
||||
0.0472044498,
|
||||
0.0443523228,
|
||||
0.0415447652,
|
||||
0.0387810767,
|
||||
0.0360605568,
|
||||
0.0333825648,
|
||||
0.0307464004,
|
||||
0.0281514227,
|
||||
0.0255970061,
|
||||
0.0230824798,
|
||||
0.0206072628,
|
||||
0.0181707144,
|
||||
0.0157722086,
|
||||
0.013411209,
|
||||
0.0110870898,
|
||||
0.0087992847,
|
||||
0.0065472275,
|
||||
0.00433036685,
|
||||
0.0021481365,
|
||||
0.,
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,15 +1,18 @@
|
|||
use super::math::safe_sqrt;
|
||||
use crate::check_rare;
|
||||
use crate::core::pbrt::{
|
||||
Float, INV_2_PI, INV_PI, ONE_MINUS_EPSILON, PI, PI_OVER_2, PI_OVER_4, clamp_t, find_interval,
|
||||
lerp,
|
||||
Float, INV_2_PI, INV_PI, ONE_MINUS_EPSILON, PI, PI_OVER_2, PI_OVER_4, clamp_t,
|
||||
evaluate_polynomial, find_interval, lerp,
|
||||
};
|
||||
use crate::core::pbrt::{RARE_EVENT_CONDITION_MET, RARE_EVENT_TOTAL_CALLS};
|
||||
use crate::geometry::{
|
||||
Bounds2f, Frame, Point2f, Point2i, Point3f, Vector2f, Vector2i, Vector3f, VectorLike,
|
||||
};
|
||||
use crate::utils::containers::Array2D;
|
||||
use crate::utils::math::{difference_of_products, logistic, square, sum_of_products};
|
||||
use crate::utils::math::{
|
||||
catmull_rom_weights, difference_of_products, logistic, newton_bisection, square,
|
||||
sum_of_products,
|
||||
};
|
||||
use std::sync::atomic::{AtomicU64, Ordering as SyncOrdering};
|
||||
|
||||
pub fn sample_linear(u: Float, a: Float, b: Float) -> Float {
|
||||
|
|
@ -428,6 +431,151 @@ pub fn invert_spherical_triangle_sample(
|
|||
Some(Point2f::new(clamp_t(u0, 0.0, 1.0), clamp_t(u1, 0.0, 1.0)))
|
||||
}
|
||||
|
||||
pub fn sample_catmull_rom(
|
||||
nodes: &[Float],
|
||||
f: &[Float],
|
||||
big_f: &[Float],
|
||||
mut u: Float,
|
||||
) -> (Float, Float, Float) {
|
||||
assert_eq!(nodes.len(), f.len());
|
||||
assert_eq!(f.len(), big_f.len());
|
||||
|
||||
u *= big_f.last().copied().unwrap_or(0.);
|
||||
let i = find_interval(big_f.len(), |i| big_f[i] <= u);
|
||||
let x0 = nodes[i];
|
||||
let x1 = nodes[i + 1];
|
||||
let f0 = f[i];
|
||||
let f1 = f[i + 1];
|
||||
let width = x1 - x0;
|
||||
|
||||
// Approximate derivatives using finite differences
|
||||
let d0 = if i > 0 {
|
||||
width * (f1 - f[i - 1]) / (x1 - nodes[i - 1])
|
||||
} else {
|
||||
f1 - f0
|
||||
};
|
||||
let d1 = if i + 2 < nodes.len() {
|
||||
width * (f[i + 2] - f0) / (nodes[i + 2] - x0)
|
||||
} else {
|
||||
f1 - f0
|
||||
};
|
||||
|
||||
u = (u - big_f[i]) / width;
|
||||
|
||||
let fhat_coeffs = [
|
||||
f0,
|
||||
d0,
|
||||
-2.0 * d0 - d1 + 3.0 * (f1 - f0),
|
||||
d0 + d1 + 2.0 * (f0 - f1),
|
||||
];
|
||||
let fhat_coeffs_ref = &fhat_coeffs;
|
||||
|
||||
let big_fhat_coeffs = [
|
||||
0.0,
|
||||
f0,
|
||||
0.5 * d0,
|
||||
(1.0 / 3.0) * (-2.0 * d0 - d1) + f1 - f0,
|
||||
0.25 * (d0 + d1) + 0.5 * (f0 - f1),
|
||||
];
|
||||
let big_fhat_coeffs_ref = &big_fhat_coeffs;
|
||||
|
||||
let mut fhat = 0.;
|
||||
let mut big_fhat = 0.;
|
||||
|
||||
let eval = |t: Float| -> (Float, Float) {
|
||||
big_fhat = evaluate_polynomial(t, big_fhat_coeffs_ref).unwrap_or(0.);
|
||||
fhat = evaluate_polynomial(t, fhat_coeffs_ref).unwrap_or(0.);
|
||||
(big_fhat - u, fhat)
|
||||
};
|
||||
let t = newton_bisection(0., 1., eval);
|
||||
(
|
||||
x0 + width * t,
|
||||
fhat,
|
||||
fhat / big_f.last().copied().unwrap_or(1.0),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn sample_catmull_rom_2d(
|
||||
nodes1: &[Float],
|
||||
nodes2: &[Float],
|
||||
values: &[Float],
|
||||
cdf: &[Float],
|
||||
alpha: Float,
|
||||
mut u: Float,
|
||||
) -> (Float, Float, Float) {
|
||||
let (offset, weights) = match catmull_rom_weights(nodes1, alpha) {
|
||||
Some(res) => res,
|
||||
None => return (0., 0., 0.),
|
||||
};
|
||||
|
||||
let n2 = nodes2.len();
|
||||
let interpolate = |array: &[Float], idx: usize| -> Float {
|
||||
let mut v = 0.;
|
||||
for i in 0..4 {
|
||||
if weights[i] != 0. {
|
||||
v += array[(offset + i) * n2 + idx] * weights[i];
|
||||
}
|
||||
}
|
||||
v
|
||||
};
|
||||
let maximum = interpolate(cdf, n2 - 1);
|
||||
if maximum == 0. {
|
||||
return (0., 0., 0.);
|
||||
}
|
||||
u *= maximum;
|
||||
let idx = find_interval(n2, |i| interpolate(cdf, i) <= u);
|
||||
let f0 = interpolate(values, idx);
|
||||
let f1 = interpolate(values, idx + 1);
|
||||
let x0 = nodes2[idx];
|
||||
let x1 = nodes2[idx + 1];
|
||||
let width = x1 - x0;
|
||||
let d0 = if idx > 0 {
|
||||
width * (f1 - interpolate(values, idx - 1)) / (x1 - nodes2[idx - 1])
|
||||
} else {
|
||||
f1 - f0
|
||||
};
|
||||
let d1 = if idx + 2 < n2 {
|
||||
width * (interpolate(values, idx + 2) - f0) / (nodes2[idx + 2] - x0)
|
||||
} else {
|
||||
f1 - f0
|
||||
};
|
||||
u = (u - interpolate(cdf, idx)) / width;
|
||||
|
||||
let fhat_coeffs = [
|
||||
f0,
|
||||
d0,
|
||||
-2.0 * d0 - d1 + 3.0 * (f1 - f0),
|
||||
d0 + d1 + 2.0 * (f0 - f1),
|
||||
];
|
||||
let fhat_coeffs_ref = &fhat_coeffs;
|
||||
|
||||
let big_fhat_coeffs = [
|
||||
0.0,
|
||||
f0,
|
||||
0.5 * d0,
|
||||
(1.0 / 3.0) * (-2.0 * d0 - d1) + f1 - f0,
|
||||
0.25 * (d0 + d1) + 0.5 * (f0 - f1),
|
||||
];
|
||||
let big_fhat_coeffs_ref = &big_fhat_coeffs;
|
||||
|
||||
let mut big_fhat = 0.0;
|
||||
let mut fhat = 0.0;
|
||||
|
||||
let eval = |t: Float| -> (Float, Float) {
|
||||
big_fhat = evaluate_polynomial(t, big_fhat_coeffs_ref).unwrap_or(0.);
|
||||
fhat = evaluate_polynomial(t, fhat_coeffs_ref).unwrap_or(0.);
|
||||
(big_fhat - u, fhat)
|
||||
};
|
||||
|
||||
let t = newton_bisection(0.0, 1.0, eval);
|
||||
|
||||
let sample = x0 + width * t;
|
||||
let fval = fhat;
|
||||
let pdf = fhat / maximum;
|
||||
|
||||
(sample, fval, pdf)
|
||||
}
|
||||
|
||||
pub fn sample_logistic(u: Float, s: Float) -> Float {
|
||||
-s * (1. / u - 1.).ln()
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue