Fixed logic issues in BSDF sampling and pdf calculations, moving on to GPU integrators

This commit is contained in:
Wito Wiala 2026-05-26 19:07:45 +01:00
parent 3cb2086f6d
commit 0c62fbc3b5
25 changed files with 202 additions and 163 deletions

View file

@ -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) {

View file

@ -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)
}
}

View file

@ -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.;
}

View file

@ -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();

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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(

View file

@ -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};

View file

@ -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]);

View file

@ -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 {

View file

@ -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;

View file

@ -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};

View file

@ -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
View 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>;
}

View file

@ -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 = {

View file

@ -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(

View file

@ -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,

View file

@ -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")
}

View file

@ -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!()
}
}

View file

@ -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;

View file

@ -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;

View file

@ -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 {

View 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);
}

View 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
View file

@ -0,0 +1,4 @@
pub mod integrator;
pub mod aggregate;
pub use aggregate::WavefrontAggregate;