Fixed logic issues in BSDF sampling and pdf calculations, moving on to GPU integrators
This commit is contained in:
parent
3cb2086f6d
commit
0c62fbc3b5
25 changed files with 202 additions and 163 deletions
|
|
@ -1,9 +1,9 @@
|
|||
use crate::core::bsdf::BSDFSample;
|
||||
use crate::core::bxdf::{BxDFFlags, BxDFReflTransFlags, BxDFTrait, FArgs, TransportMode};
|
||||
use crate::core::geometry::{
|
||||
Normal3f, Point2f, Vector3f, VectorLike, abs_cos_theta, same_hemisphere,
|
||||
abs_cos_theta, same_hemisphere, Normal3f, Point2f, Vector3f, VectorLike,
|
||||
};
|
||||
use crate::core::scattering::{TrowbridgeReitzDistribution, fr_complex_from_spectrum, reflect};
|
||||
use crate::core::scattering::{fr_complex_from_spectrum, reflect, TrowbridgeReitzDistribution};
|
||||
use crate::spectra::SampledSpectrum;
|
||||
use crate::utils::sampling::{cosine_hemisphere_pdf, sample_cosine_hemisphere};
|
||||
use crate::{Float, INV_PI};
|
||||
|
|
@ -84,7 +84,7 @@ impl BxDFTrait for ConductorBxDF {
|
|||
return None;
|
||||
}
|
||||
|
||||
let f_spectrum = fr_complex_from_spectrum(wo.dot(wi).abs(), self.eta, self.k);
|
||||
let f_spectrum = fr_complex_from_spectrum(wo.dot(wm).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);
|
||||
|
||||
|
|
@ -118,8 +118,7 @@ impl BxDFTrait for ConductorBxDF {
|
|||
return SampledSpectrum::new(0.);
|
||||
}
|
||||
let wm_norm = wm.normalize();
|
||||
|
||||
let f_spectrum = fr_complex_from_spectrum(wo.dot(wm).abs(), self.eta, self.k);
|
||||
let f_spectrum = fr_complex_from_spectrum(wo.dot(wm_norm).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)
|
||||
}
|
||||
|
|
@ -140,8 +139,9 @@ impl BxDFTrait for ConductorBxDF {
|
|||
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())
|
||||
let wm_norm = Normal3f::from(wm.normalize());
|
||||
let wm_corr = wm_norm.face_forward(Vector3f::new(0., 0., 1.));
|
||||
self.mf_distrib.pdf(wo, wm_corr.into()) / (4. * wo.dot(Vector3f::from(wm_norm)).abs())
|
||||
}
|
||||
|
||||
fn regularize(&mut self) {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
use crate::core::bsdf::BSDFSample;
|
||||
use crate::core::bxdf::{BxDFFlags, BxDFReflTransFlags, BxDFTrait, FArgs, TransportMode};
|
||||
use crate::core::geometry::{
|
||||
Normal3f, Point2f, Vector3f, VectorLike, abs_cos_theta, cos_theta, same_hemisphere,
|
||||
abs_cos_theta, cos_theta, same_hemisphere, Normal3f, Point2f, Vector3f, VectorLike,
|
||||
};
|
||||
use crate::core::scattering::{
|
||||
TrowbridgeReitzDistribution, fr_complex_from_spectrum, fr_dielectric, reflect, refract,
|
||||
fr_complex_from_spectrum, fr_dielectric, reflect, refract, TrowbridgeReitzDistribution,
|
||||
};
|
||||
use crate::spectra::SampledSpectrum;
|
||||
use crate::utils::math::square;
|
||||
|
|
@ -141,14 +141,11 @@ impl BxDFTrait for DielectricBxDF {
|
|||
}
|
||||
|
||||
if reflect {
|
||||
self.mf_distrib.pdf(
|
||||
wo,
|
||||
Vector3f::from(wm) / (4. * wo.dot(wm.into()).abs()) * pr / (pt + pr),
|
||||
)
|
||||
self.mf_distrib.pdf(wo, wm.into()) / (4. * wo.dot(wm.into()).abs()) * pr / (pr + pt)
|
||||
} 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)
|
||||
self.mf_distrib.pdf(wo, wm.into()) * dwm_dwi * pt / (pr + pt)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ impl BxDFTrait for DiffuseBxDF {
|
|||
|
||||
fn pdf(&self, wo: Vector3f, wi: Vector3f, f_args: FArgs) -> Float {
|
||||
let reflection_flags =
|
||||
BxDFReflTransFlags::from_bits_truncate(BxDFReflTransFlags::ALL.bits());
|
||||
BxDFReflTransFlags::from_bits_truncate(BxDFReflTransFlags::REFLECTION.bits());
|
||||
if !f_args.sample_flags.contains(reflection_flags) || !same_hemisphere(wo, wi) {
|
||||
return 0.;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,17 +5,17 @@ use crate::core::bsdf::BSDFSample;
|
|||
use crate::core::bxdf::{BxDFFlags, BxDFReflTransFlags, BxDFTrait, FArgs, TransportMode};
|
||||
use crate::core::color::RGB;
|
||||
use crate::core::geometry::{
|
||||
Frame, Normal3f, Point2f, Vector3f, VectorLike, abs_cos_theta, cos_theta, same_hemisphere,
|
||||
spherical_direction, spherical_theta,
|
||||
abs_cos_theta, cos_theta, same_hemisphere, spherical_direction, spherical_theta, Frame,
|
||||
Normal3f, Point2f, Vector3f, VectorLike,
|
||||
};
|
||||
use crate::core::medium::{HGPhaseFunction, PhaseFunctionTrait};
|
||||
use crate::core::scattering::{
|
||||
TrowbridgeReitzDistribution, fr_complex, fr_complex_from_spectrum, fr_dielectric, reflect,
|
||||
refract,
|
||||
fr_complex, fr_complex_from_spectrum, fr_dielectric, reflect, refract,
|
||||
TrowbridgeReitzDistribution,
|
||||
};
|
||||
use crate::spectra::{
|
||||
DeviceStandardColorSpaces, N_SPECTRUM_SAMPLES, RGBColorSpace, RGBUnboundedSpectrum,
|
||||
SampledSpectrum, SampledWavelengths,
|
||||
DeviceStandardColorSpaces, RGBColorSpace, RGBUnboundedSpectrum, SampledSpectrum,
|
||||
SampledWavelengths, N_SPECTRUM_SAMPLES,
|
||||
};
|
||||
use crate::utils::hash::hash_buffer;
|
||||
use crate::utils::math::{
|
||||
|
|
@ -24,8 +24,8 @@ use crate::utils::math::{
|
|||
};
|
||||
use crate::utils::rng::Rng;
|
||||
use crate::utils::sampling::{
|
||||
PiecewiseLinear2D, cosine_hemisphere_pdf, power_heuristic, sample_cosine_hemisphere,
|
||||
sample_exponential, sample_trimmed_logistic, sample_uniform_hemisphere, uniform_hemisphere_pdf,
|
||||
cosine_hemisphere_pdf, power_heuristic, sample_cosine_hemisphere, sample_exponential,
|
||||
sample_trimmed_logistic, sample_uniform_hemisphere, uniform_hemisphere_pdf, PiecewiseLinear2D,
|
||||
};
|
||||
use crate::{Float, INV_2_PI, INV_4_PI, INV_PI, ONE_MINUS_EPSILON, PI, PI_OVER_2};
|
||||
use core::any::Any;
|
||||
|
|
@ -173,6 +173,12 @@ where
|
|||
mode,
|
||||
sample_flags: BxDFReflTransFlags::TRANSMISSION,
|
||||
};
|
||||
|
||||
let reverse_trans_args = FArgs {
|
||||
mode: !mode,
|
||||
sample_flags: BxDFReflTransFlags::TRANSMISSION,
|
||||
};
|
||||
|
||||
let refl_args = FArgs {
|
||||
mode,
|
||||
sample_flags: BxDFReflTransFlags::REFLECTION,
|
||||
|
|
@ -188,7 +194,7 @@ where
|
|||
};
|
||||
|
||||
let Some(wis) = exit_interface
|
||||
.sample_f(wi, r(), Point2f::new(r(), r()), trans_args)
|
||||
.sample_f(wi, r(), Point2f::new(r(), r()), reverse_trans_args)
|
||||
.filter(|s| !s.f.is_black() && s.pdf > 0.0 && s.wi.z() != 0.0)
|
||||
else {
|
||||
return SampledSpectrum::new(0.0);
|
||||
|
|
@ -226,18 +232,21 @@ where
|
|||
let sigma_t = 1.0;
|
||||
let dz = sample_exponential(r(), sigma_t / w.z().abs());
|
||||
let zp = if w.z() > 0.0 { z + dz } else { z - dz };
|
||||
if zp == z {
|
||||
continue;
|
||||
}
|
||||
|
||||
if zp > 0.0 && zp < self.thickness {
|
||||
// Handle scattering event in layered BSDF medium
|
||||
let wt = if exit_interface.flags().is_specular() {
|
||||
power_heuristic(1, wis.pdf, 1, phase.pdf(-w, wis.wi))
|
||||
let wt = if !exit_interface.flags().is_specular() {
|
||||
power_heuristic(1, wis.pdf, 1, phase.pdf(-w, -wis.wi))
|
||||
} else {
|
||||
1.0
|
||||
};
|
||||
|
||||
f += beta
|
||||
* self.albedo
|
||||
* phase.p(-wi, -wis.wi)
|
||||
* phase.p(-w, -wis.wi)
|
||||
* wt
|
||||
* self.tr(zp - exit_z, wis.wi)
|
||||
* wis.f
|
||||
|
|
@ -257,7 +266,7 @@ where
|
|||
|
||||
// Account for scattering through exit
|
||||
if (z < exit_z && w.z() > 0.0) || (z > exit_z && w.z() < 0.0) {
|
||||
let f_exit = exit_interface.f(-w, -wi, mode);
|
||||
let f_exit = exit_interface.f(-w, wi, mode);
|
||||
if !f_exit.is_black() {
|
||||
let exit_pdf = exit_interface.pdf(-w, wi, trans_args);
|
||||
let wt = power_heuristic(1, ps.pdf, 1, exit_pdf);
|
||||
|
|
@ -283,7 +292,7 @@ where
|
|||
} else {
|
||||
// Hitting the non-exit surface -> Reflection
|
||||
if !non_exit_interface.flags().is_specular() {
|
||||
let wt = if exit_interface.flags().is_specular() {
|
||||
let wt = if !exit_interface.flags().is_specular() {
|
||||
power_heuristic(
|
||||
1,
|
||||
wis.pdf,
|
||||
|
|
@ -308,7 +317,7 @@ where
|
|||
.sample_f(-w, r(), Point2f::new(r(), r()), refl_args)
|
||||
.filter(|s| !s.f.is_black() && s.pdf > 0.0 && s.wi.z() != 0.0)
|
||||
else {
|
||||
continue;
|
||||
break;
|
||||
};
|
||||
|
||||
beta *= bs.f * abs_cos_theta(bs.wi) / bs.pdf;
|
||||
|
|
@ -319,7 +328,7 @@ where
|
|||
let f_exit = exit_interface.f(-w, wi, mode);
|
||||
if !f_exit.is_black() {
|
||||
let mut wt = 1.0;
|
||||
if non_exit_interface.flags().is_specular() {
|
||||
if !non_exit_interface.flags().is_specular() {
|
||||
wt = power_heuristic(
|
||||
1,
|
||||
bs.pdf,
|
||||
|
|
@ -476,7 +485,7 @@ where
|
|||
}
|
||||
pdf *= 1. - q;
|
||||
}
|
||||
if w.z() < 0. {
|
||||
if w.z() == 0. {
|
||||
return None;
|
||||
}
|
||||
|
||||
|
|
@ -484,12 +493,15 @@ where
|
|||
let sigma_t = 1.;
|
||||
let dz = sample_exponential(r(), sigma_t / abs_cos_theta(w));
|
||||
let zp = if w.z() > 0. { z + dz } else { z - dz };
|
||||
if zp == z {
|
||||
return None;
|
||||
}
|
||||
if zp > 0. && zp < self.thickness {
|
||||
let Some(ps) = phase
|
||||
.sample_p(-wo, Point2f::new(r(), r()))
|
||||
.filter(|s| s.pdf == 0. && s.wi.z() == 0.)
|
||||
.sample_p(-w, Point2f::new(r(), r()))
|
||||
.filter(|s| s.pdf != 0. && s.wi.z() != 0.)
|
||||
else {
|
||||
continue;
|
||||
return None;
|
||||
};
|
||||
f *= self.albedo * ps.p;
|
||||
pdf *= ps.pdf;
|
||||
|
|
@ -518,7 +530,7 @@ where
|
|||
// Sample interface BSDF to determine new path direction
|
||||
let bs = interface
|
||||
.sample_f(-w, r(), Point2f::new(r(), r()), f_args)
|
||||
.filter(|s| s.f.is_black() && s.pdf == 0. && s.wi.z() == 0.)?;
|
||||
.filter(|s| !s.f.is_black() && s.pdf != 0. && s.wi.z() != 0.)?;
|
||||
f *= bs.f;
|
||||
pdf *= bs.pdf;
|
||||
specular_path &= bs.is_specular();
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ impl BSDF {
|
|||
|
||||
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) {
|
||||
if wo.z() == 0.0 || !bxdf.flags().intersects(sampling_flags) {
|
||||
return None;
|
||||
}
|
||||
|
||||
|
|
@ -93,7 +93,7 @@ impl BSDF {
|
|||
let wo = self.render_to_local(wo_render);
|
||||
let wi = self.render_to_local(wi_render);
|
||||
|
||||
if wo.z() == 0.0 || !self.bxdf.flags().contains(sample_flags) {
|
||||
if wo.z() == 0.0 || !self.bxdf.flags().intersects(sample_flags) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
use super::{Normal3f, Point3f, Point3fi, Vector3f, VectorLike};
|
||||
use crate::core::medium::Medium;
|
||||
use crate::core::pbrt::Float;
|
||||
use crate::utils::math::{next_float_down, next_float_up};
|
||||
use crate::utils::ptr::Ptr;
|
||||
use crate::{gvec_with_capacity, Float, GVec, Ptr, SOA};
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
|
|
@ -124,3 +123,56 @@ pub struct RayDifferential {
|
|||
pub rx_direction: Vector3f,
|
||||
pub ry_direction: Vector3f,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RaySoA {
|
||||
pub o: GVec<Point3f>,
|
||||
pub d: GVec<Vector3f>,
|
||||
pub time: GVec<Float>,
|
||||
pub medium: GVec<Ptr<Medium>>,
|
||||
pub has_differentials: GVec<bool>,
|
||||
pub differential: GVec<RayDifferential>,
|
||||
}
|
||||
|
||||
impl SoA for RaySoA {
|
||||
type Item = Ray;
|
||||
|
||||
fn with_capacity(n: usize) -> Self {
|
||||
Self {
|
||||
o: gvec_with_capacity(n),
|
||||
d: gvec_with_capacity(n),
|
||||
time: gvec_with_capacity(n),
|
||||
medium: gvec_with_capacity(n),
|
||||
has_differentials: gvec_with_capacity(n),
|
||||
differential: gvec_with_capacity(n),
|
||||
}
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.o.len()
|
||||
}
|
||||
|
||||
unsafe fn get_unchecked(&self, i: usize) -> Ray {
|
||||
Ray {
|
||||
o: *self.o.get_unchecked(i),
|
||||
d: *self.d.get_unchecked(i),
|
||||
time: *self.time.get_unchecked(i),
|
||||
medium: *self.medium.get_unchecked(i),
|
||||
has_differentials: *self.has_differentials.get_unchecked(i),
|
||||
differential: *self.differential.get_unchecked(i),
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn set_unchecked(&mut self, i: usize, v: Ray) {
|
||||
*self.o.get_unchecked_mut(i) = v.o;
|
||||
*self.d.get_unchecked_mut(i) = v.d;
|
||||
*self.time.get_unchecked_mut(i) = v.time;
|
||||
*self.medium.get_unchecked_mut(i) = v.medium;
|
||||
*self.has_differentials.get_unchecked_mut(i) = v.has_differentials;
|
||||
*self.differential.get_unchecked_mut(i) = v.differential;
|
||||
}
|
||||
}
|
||||
|
||||
impl SoAElement for Ray {
|
||||
type SoA = RaySoA;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
use crate::core::geometry::{
|
||||
Normal3f, Point2f, Vector2f, Vector3f, VectorLike, abs_cos_theta, cos_phi, cos2_theta, sin_phi,
|
||||
tan2_theta,
|
||||
abs_cos_theta, cos2_theta, cos_phi, sin_phi, tan2_theta, Normal3f, Point2f, Vector2f, Vector3f,
|
||||
VectorLike,
|
||||
};
|
||||
use crate::core::pbrt::{Float, PI};
|
||||
use crate::spectra::{N_SPECTRUM_SAMPLES, SampledSpectrum};
|
||||
use crate::spectra::{SampledSpectrum, N_SPECTRUM_SAMPLES};
|
||||
use crate::utils::math::{clamp, lerp, safe_sqrt, square};
|
||||
use crate::utils::sampling::sample_uniform_disk_polar;
|
||||
use num_traits::Float as NumFloat;
|
||||
|
|
@ -18,7 +18,11 @@ pub struct TrowbridgeReitzDistribution {
|
|||
}
|
||||
|
||||
impl TrowbridgeReitzDistribution {
|
||||
pub fn new(alpha_x: Float, alpha_y: Float) -> Self {
|
||||
pub fn new(mut alpha_x: Float, mut alpha_y: Float) -> Self {
|
||||
if alpha_x.max(alpha_y) >= 1e-3 {
|
||||
alpha_x = alpha_x.max(1e-4);
|
||||
alpha_y = alpha_y.max(1e-4);
|
||||
}
|
||||
Self { alpha_x, alpha_y }
|
||||
}
|
||||
|
||||
|
|
@ -28,8 +32,13 @@ impl TrowbridgeReitzDistribution {
|
|||
return 0.;
|
||||
}
|
||||
let cos4_theta = square(cos2_theta(wm));
|
||||
|
||||
if cos4_theta < 1e-16 {
|
||||
return 0.;
|
||||
}
|
||||
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))
|
||||
}
|
||||
|
||||
|
|
@ -51,7 +60,7 @@ impl TrowbridgeReitzDistribution {
|
|||
}
|
||||
|
||||
pub fn g1(&self, w: Vector3f) -> Float {
|
||||
1. / (1. / self.lambda(w))
|
||||
1. / (1. + self.lambda(w))
|
||||
}
|
||||
|
||||
pub fn d_from_w(&self, w: Vector3f, wm: Vector3f) -> Float {
|
||||
|
|
@ -76,7 +85,7 @@ impl TrowbridgeReitzDistribution {
|
|||
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 pz = (1. - Vector2f::from(p).norm_squared()).max(0.).sqrt();
|
||||
let nh = p.x() * t1 + p.y() * t2 + pz * wh;
|
||||
Vector3f::new(
|
||||
self.alpha_x * nh.x(),
|
||||
|
|
@ -158,7 +167,7 @@ pub fn fr_complex(cos_theta_i: Float, eta: Complex) -> Float {
|
|||
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.
|
||||
(square(r_parl.norm()) + square(r_perp.norm())) / 2.
|
||||
}
|
||||
|
||||
pub fn fr_complex_from_spectrum(
|
||||
|
|
|
|||
|
|
@ -16,7 +16,9 @@ pub mod shapes;
|
|||
pub mod spectra;
|
||||
pub mod textures;
|
||||
pub mod utils;
|
||||
pub mod wavefront;
|
||||
|
||||
pub use core::pbrt::*;
|
||||
pub use utils::alloc::{gbox, gvec, gvec_from_slice, gvec_with_capacity, leak, GBox, GVec};
|
||||
pub use utils::{Array2D, PBRTOptions, Ptr, Transform};
|
||||
pub use utils::{Array2D, PBRTOptions, Ptr, Transform, SOA};
|
||||
pub use wavefront::{WavefrontAggregate, WorkQueue};
|
||||
|
|
|
|||
|
|
@ -320,7 +320,6 @@ impl TriangleShape {
|
|||
determinant: Float,
|
||||
degenerate_uv: bool,
|
||||
) {
|
||||
// Interpolate vertex normals if they exist
|
||||
let ns = if let Some(normals) = self.get_shading_normals() {
|
||||
let n = ti.b0 * normals[0] + ti.b1 * normals[1] + ti.b2 * normals[2];
|
||||
if n.norm_squared() > 0.0 {
|
||||
|
|
@ -332,7 +331,6 @@ impl TriangleShape {
|
|||
isect.n()
|
||||
};
|
||||
|
||||
// Interpolate tangents if they exist
|
||||
let mut ss = if let Some(tangents) = self.get_tangents() {
|
||||
let s = ti.b0 * tangents[0] + ti.b1 * tangents[1] + ti.b2 * tangents[2];
|
||||
if s.norm_squared() > 0.0 {
|
||||
|
|
@ -344,7 +342,6 @@ impl TriangleShape {
|
|||
dpdu_geom
|
||||
};
|
||||
|
||||
// Ensure shading tangent (ss) is perpendicular to shading normal (ns)
|
||||
let mut ts = ns.cross(ss.into());
|
||||
if ts.norm_squared() > 0.0 {
|
||||
ss = ts.cross(ns.into()).into();
|
||||
|
|
@ -354,7 +351,6 @@ impl TriangleShape {
|
|||
ts = t.into();
|
||||
}
|
||||
|
||||
// How does the normal change as we move across UVs?
|
||||
let (dndu, dndv) = if let Some(normals) = self.get_shading_normals() {
|
||||
if degenerate_uv {
|
||||
let dn = (normals[2] - normals[0]).cross(normals[1] - normals[0]);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use crate::core::pbrt::Float;
|
||||
use crate::core::spectrum::{SpectrumTrait, StandardSpectra};
|
||||
use crate::utils::gpu_array_from_fn;
|
||||
use crate::utils::math::{clamp, lerp};
|
||||
use crate::utils::math::{clamp, lerp, square};
|
||||
use core::ops::{
|
||||
Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub, SubAssign,
|
||||
};
|
||||
|
|
@ -342,7 +342,7 @@ impl SampledWavelengths {
|
|||
|
||||
pub fn sample_uniform(u: Float, lambda_min: Float, lambda_max: Float) -> Self {
|
||||
let mut lambda = [0.0; N_SPECTRUM_SAMPLES];
|
||||
lambda[0] = lerp(u, lambda_min, lambda_min);
|
||||
lambda[0] = lerp(u, lambda_min, lambda_max);
|
||||
let delta = (lambda_max - lambda_min) / N_SPECTRUM_SAMPLES as Float;
|
||||
for i in 1..N_SPECTRUM_SAMPLES {
|
||||
lambda[i] = lambda[i - 1] + delta;
|
||||
|
|
@ -357,14 +357,14 @@ impl SampledWavelengths {
|
|||
}
|
||||
|
||||
pub fn sample_visible_wavelengths(u: Float) -> Float {
|
||||
538.0 - 138.888889 * Float::atanh(0.85691062 - 1.82750197 * u)
|
||||
(538.0_f64 - 138.888889_f64 * (0.85691062_f64 - 1.82750197_f64 * u as f64).atanh()) as Float
|
||||
}
|
||||
|
||||
pub fn visible_wavelengths_pdf(lambda: Float) -> Float {
|
||||
if !(360.0..830.0).contains(&lambda) {
|
||||
return 0.0;
|
||||
}
|
||||
0.0039398042 / (Float::cosh(0.0072 * (lambda - 538.0))).sqrt()
|
||||
(0.0039398042_f64 / (0.0072_f64 * (lambda as f64 - 538.0)).cosh().powi(2)) as Float
|
||||
}
|
||||
|
||||
pub fn sample_visible(u: Float) -> Self {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use alloc::vec::Vec;
|
|||
use core::alloc::{AllocError, Allocator, Layout};
|
||||
use core::ptr::NonNull;
|
||||
|
||||
// CPU fallback, delegates to Global
|
||||
// CPU fallback to GlobalAllocator
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct SystemAlloc;
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ pub use options::PBRTOptions;
|
|||
pub use ptr::Ptr;
|
||||
pub use transform::{AnimatedTransform, Transform, TransformGeneric};
|
||||
pub use containers::Array2D;
|
||||
pub use soa::SOA:
|
||||
|
||||
use crate::Float;
|
||||
use core::sync::atomic::{AtomicU32, Ordering};
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ impl<T: ?Sized> PartialEq for Ptr<T> {
|
|||
|
||||
impl<T: ?Sized> PartialOrd for Ptr<T> {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
|
||||
Some(self.ptr.cmp(&other.ptr))
|
||||
Some(self.ptr.cast::<()>().cmp(&other.ptr.cast::<()>()))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
15
shared/src/utils/soa.rs
Normal file
15
shared/src/utils/soa.rs
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
pub trait SoA: Clone {
|
||||
type Item: Copy;
|
||||
fn with_capacity(n: usize) -> Self;
|
||||
fn len(&self) -> usize;
|
||||
fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
unsafe fn get_unchecked(&self, i: usize) -> Self::Item;
|
||||
unsafe fn set_unchecked(&mut self, i: usize, v: Self::Item);
|
||||
}
|
||||
|
||||
pub trait SoAElement: Copy {
|
||||
type SoA: SoA<Item = Self>;
|
||||
}
|
||||
|
|
@ -17,9 +17,6 @@ pub fn render_scene(scene: &BasicScene, arena: &Arena) -> Result<()> {
|
|||
let media = scene.create_media();
|
||||
let textures = scene.create_textures(arena);
|
||||
let (named_materials, materials) = scene.create_materials(&textures, arena)?;
|
||||
for (i, m) in materials.iter().enumerate() {
|
||||
eprintln!("materials[{}]: {:?}", i, std::mem::discriminant(m));
|
||||
}
|
||||
let lights = scene.create_lights(&textures, arena);
|
||||
|
||||
let have_scattering = {
|
||||
|
|
|
|||
|
|
@ -701,7 +701,7 @@ impl BasicScene {
|
|||
sampler,
|
||||
aggregate,
|
||||
lights,
|
||||
PathConfig::SIMPLE,
|
||||
PathConfig::FULL,
|
||||
arena,
|
||||
)
|
||||
.expect("Integrator creation failed"),
|
||||
|
|
@ -779,6 +779,8 @@ impl BasicScene {
|
|||
continue;
|
||||
}
|
||||
|
||||
eprintln!("shape '{}' n={}", entity.base.name, created_shapes.len());
|
||||
|
||||
let mtl = resolve_material(
|
||||
&entity.material,
|
||||
named_materials,
|
||||
|
|
@ -967,10 +969,6 @@ impl BasicScene {
|
|||
primitives
|
||||
}
|
||||
|
||||
// =======================================================================
|
||||
// Private — texture helper
|
||||
// =======================================================================
|
||||
|
||||
fn add_texture_generic<T, F>(
|
||||
&self,
|
||||
name: String,
|
||||
|
|
@ -1105,9 +1103,6 @@ impl BasicScene {
|
|||
Err(anyhow!("{} requested but not initialized!", name))
|
||||
}
|
||||
|
||||
// =======================================================================
|
||||
// GPU path stubs — to be implemented with wavefront integrator
|
||||
// =======================================================================
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn upload_shapes(
|
||||
|
|
|
|||
|
|
@ -120,31 +120,6 @@ pub enum SpectrumTexture {
|
|||
DirectionMix(SpectrumDirectionMixTexture),
|
||||
}
|
||||
|
||||
impl SpectrumTexture {
|
||||
fn upload_spectrum_image(
|
||||
inner: &SpectrumImageTexture,
|
||||
arena: &Arena,
|
||||
) -> GPUSpectrumImageTexture {
|
||||
let tex_obj = arena.get_texture_object(&inner.base.mipmap);
|
||||
GPUSpectrumImageTexture {
|
||||
mapping: inner.base.mapping,
|
||||
tex_obj,
|
||||
scale: inner.base.scale,
|
||||
invert: inner.base.invert,
|
||||
is_single_channel: inner.base.mipmap.is_single_channel(),
|
||||
color_space: arena.alloc(
|
||||
inner
|
||||
.base
|
||||
.mipmap
|
||||
.color_space
|
||||
.clone()
|
||||
.unwrap_or_else(crate::spectra::default_colorspace),
|
||||
),
|
||||
spectrum_type: inner.spectrum_type,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CreateSpectrumTexture {
|
||||
fn create(
|
||||
render_from_texture: Transform,
|
||||
|
|
|
|||
|
|
@ -1,21 +0,0 @@
|
|||
pub mod context;
|
||||
|
||||
pub use context::{
|
||||
GPU_STATE, GpuContext, GpuState, gpu_init, gpu_state, gpu_state_or_panic, gpu_thread_init,
|
||||
};
|
||||
|
||||
pub mod wavefront;
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum GpuError {
|
||||
#[error("CUDA driver error: {0}")]
|
||||
Driver(#[from] cudarc::driver::DriverError),
|
||||
#[error("No GPU context initialized")]
|
||||
NoContext,
|
||||
}
|
||||
|
||||
pub fn gpu_unwrap() -> &'static GpuContext {
|
||||
context::GPU_STATE.get().expect("GPU not initialized")
|
||||
}
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
// use crate::core::scene::BasicScene;
|
||||
// use crate::{
|
||||
// EscapedRayQueue, GetBSSRDFAndProbeRayQueue, HitAreaLightQueue, MaterialEvalQueue,
|
||||
// MediumSampleQueue, MediumScatterQueue, PixelSampleStateStorage, RayQueue, ShadowRayQueue,
|
||||
// SubsurfaceScatterQueue,
|
||||
// };
|
||||
// use shared::core::camera::Camera;
|
||||
// use shared::core::film::Film;
|
||||
// use shared::core::filter::Filter;
|
||||
// use shared::core::light::Light;
|
||||
// use shared::core::sampler::Sampler;
|
||||
// use shared::lights::sampler::LightSampler;
|
||||
// use std::sync::Arc;
|
||||
//
|
||||
// pub struct WavefrontPathIntegrator {
|
||||
// pub film: Film,
|
||||
// pub filter: Filter,
|
||||
// pub sampler: Sampler,
|
||||
// pub camera: Arc<Camera>,
|
||||
// pub light_sampler: LightSampler,
|
||||
// pub infinite_lights: Option<Vec<Arc<Light>>>,
|
||||
// pub max_depth: i32,
|
||||
// pub samples_per_pixel: i32,
|
||||
// pub regularize: bool,
|
||||
// pub scanlines_per_pixel: i32,
|
||||
// pub max_queue_size: i32,
|
||||
// pub pixel_sample_state: PixelSampleStateStorage,
|
||||
// pub ray_queue: [RayQueue; 2],
|
||||
// pub hit_area_light_queue: HitAreaLightQueue,
|
||||
// pub shadow_ray_queue: ShadowRayQueue,
|
||||
// pub escaped_ray_queue: Option<EscapedRayQueue>,
|
||||
// pub basic_material_queue: Option<MaterialEvalQueue>,
|
||||
// pub universal_material_queue: Option<MaterialEvalQueue>,
|
||||
// pub medium_sample_queue: Option<MediumSampleQueue>,
|
||||
// pub medium_scatter_queue: Option<MediumScatterQueue>,
|
||||
// pub bssrf_queue: Option<GetBSSRDFAndProbeRayQueue>,
|
||||
// pub subsurface_queue: Option<SubsurfaceScatterQueue>,
|
||||
// }
|
||||
|
||||
#[cfg(feature = "use_gpu")]
|
||||
impl WavefrontPathIntegrator {
|
||||
pub fn new(scene: BasicScene) -> Self {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
|
@ -82,7 +82,6 @@ pub fn render<T>(
|
|||
) where
|
||||
T: RayIntegratorTrait + Sync,
|
||||
{
|
||||
println!("RENDER CALLED");
|
||||
let options = get_options();
|
||||
if let Some((p_pixel, sample_index)) = options.debug_start {
|
||||
let s_index = sample_index as usize;
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ pub mod shapes;
|
|||
pub mod spectra;
|
||||
pub mod textures;
|
||||
pub mod utils;
|
||||
pub mod wavefront;
|
||||
|
||||
#[cfg(feature = "cuda")]
|
||||
pub mod gpu;
|
||||
pub use utils::{Arena, FileLoc, ParameterDictionary, Upload, ArenaUpload};
|
||||
pub const MAX_TAGS = 16;
|
||||
|
|
|
|||
|
|
@ -695,6 +695,7 @@ impl ParameterDictionary {
|
|||
}
|
||||
|
||||
fn extract_file_spectrum(&self, param: &ParsedParameter) -> Vec<Spectrum> {
|
||||
eprintln!("extract_file_spectrum: param='{}' files={:?}", param.name, param.strings);
|
||||
param
|
||||
.strings
|
||||
.iter()
|
||||
|
|
@ -894,6 +895,7 @@ impl TextureParameterDictionary {
|
|||
|
||||
p.looked_up.store(true, Ordering::Relaxed);
|
||||
let tex_name = &p.strings[0];
|
||||
eprintln!("looking up texture '{}'", tex_name);
|
||||
|
||||
if let Some(nt) = &self.textures {
|
||||
let map = match stype {
|
||||
|
|
|
|||
11
src/wavefront/aggregate.rs
Normal file
11
src/wavefront/aggregate.rs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
use shared::core::geometry::Bounds3f;
|
||||
use super::{RayQueue, EscapedRayQueue, HitAreaLightQueue, MaterialEvalQueue, MediumSampleQueue, SubsurfaceScatterQueue};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub trait WavefrontAggregate {
|
||||
fn bounds(&self) -> Bounds3f;
|
||||
fn intersect_closest(max_rays: usize, ray_q: &mut RayQueue, hit_area_light_q: &mut HitAreaLightQueue, basic_mlt_q: &mut MaterialEvalQueue, universal_mtl_q: &mut MaterialEvalQueue, medium_sample_q: &mut MediumSampleQueue);
|
||||
fn intersect_shadow(max_rays: usize, shadow_ray_q: &mut ShadowRayQueue, pixel_sample_state: &mut SOA<PixelSampleState>);
|
||||
fn intersect_shadow_tr(max_rays: usize, shadow_ray_q: &mut ShadowRayQueue, pixel_sample_state: &mut SOA<PixelSampleState>);
|
||||
fn intersect_one_random(max_rays: usize, subsurface_scatte_q: &mut SubsurfaceScatterQueue);
|
||||
}
|
||||
38
src/wavefront/integrator.rs
Normal file
38
src/wavefront/integrator.rs
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
use crate::MAX_TAGS;
|
||||
use shared::{Ptr, GVec};
|
||||
use shared::core::film::Film;
|
||||
use shared::core::color::RGB;
|
||||
use shared::core::filter::Filter;
|
||||
use shared::core::light::Light;
|
||||
use shared::core::sampler::Filter;
|
||||
use shared::wavefront::{WavefrontAggregate, RayQueue, MediumSampleQueue, EscapedRayQueue, HitAreaLightQueue, MaterialEvalQueue, ShadowRayQueue, GetBSSRDFAndProbeRayQueue, SubsurfaceScatterQueue};
|
||||
|
||||
pub struct WavefrontPathIntegrator {
|
||||
pub init_visible_surface: bool,
|
||||
pub have_subsurface: bool,
|
||||
pub have_media: bool,
|
||||
pub have_basic_eval_material: [bool; MAX_TAGS + 1],
|
||||
pub have_universal_eval_material: [bool; MAX_TAGS + 1],
|
||||
pub filter: Filter,
|
||||
pub film: Film,
|
||||
pub sampler: Sampler,
|
||||
pub camera: Camera,
|
||||
pub infinite_lights: GVec<Light>,
|
||||
pub max_depth: usize,
|
||||
pub sampler_per_pixel: usize,
|
||||
pub regularize: bool,
|
||||
pub scanlines_per_pixel: usize,
|
||||
pub max_queue_size: usize,
|
||||
pub medium_sample_queue: Ptr<MediumSampleQueue>,
|
||||
pub medium_scatter_queue: Ptr<MediumScatterQueue>,
|
||||
pub escaped_ray_queue: Ptr<EscapedRayQueue>,
|
||||
pub hit_area_light_queue: Ptr<HitAreaLightQueue>,
|
||||
pub basic_eval_material_queue: Ptr<MaterialEvalQueue>,
|
||||
pub universal_eval_material_queue: Ptr<MaterialEvalQueue>,
|
||||
pub shadow_ray_queue: Ptr<ShadowRayQueue>,
|
||||
pub bssrdf_eval_queue: PTr<GetBSSRDFAndProbeRayQueue>,
|
||||
pub subsurface_scatter_queue: Ptr<SubsurfaceScatterQueue>,
|
||||
pub display_rgb: Ptr<RGB>,
|
||||
pub display_rgb_host: Ptr<RGB,
|
||||
|
||||
}
|
||||
4
src/wavefront/mod.rs
Normal file
4
src/wavefront/mod.rs
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
pub mod integrator;
|
||||
pub mod aggregate;
|
||||
|
||||
pub use aggregate::WavefrontAggregate;
|
||||
Loading…
Reference in a new issue