diff --git a/src/integrators/mod.rs b/src/integrators/mod.rs index 40546e4..f36f01a 100644 --- a/src/integrators/mod.rs +++ b/src/integrators/mod.rs @@ -66,8 +66,17 @@ impl CreateIntegrator for PathIntegrator { config: PathConfig, arena: &Arena, ) -> Result { - let _max_depth = parameters.get_one_int("maxdepth", 5)?; - let _regularize = parameters.get_one_bool("regularize", false)?; + // pbrt (`cpu/integrators.cpp`): `maxdepth` defaults to 5, `regularize` to false. + // These were parsed and then discarded, so the hardcoded PathConfig won instead + // -- PathConfig::FULL is maxdepth 8 with regularize on, which made the path + // integrator ~2.8% brighter than the wavefront (which reads them properly). + let max_depth = parameters.get_one_int("maxdepth", 5)?; + let regularize = parameters.get_one_bool("regularize", false)?; + let config = PathConfig { + max_depth: max_depth as usize, + regularize, + ..config + }; let light_sampler = create_light_sampler("power", &lights, arena); let integrator = PathIntegrator::new(aggregate, lights, camera, light_sampler, config, materials); diff --git a/src/integrators/path.rs b/src/integrators/path.rs index 856ebfd..3940480 100644 --- a/src/integrators/path.rs +++ b/src/integrators/path.rs @@ -8,7 +8,7 @@ use shared::core::bsdf::{BSDF, BSDFSample}; use shared::core::bxdf::{BxDFFlags, FArgs, TransportMode}; use shared::core::camera::Camera; use shared::core::film::VisibleSurface; -use shared::core::geometry::{Point2i, Ray, Vector3f, VectorLike}; +use shared::core::geometry::{Point2i, Point3fi, Ray, Vector3f, VectorLike}; use shared::core::interaction::{Interaction, InteractionTrait, SurfaceInteraction}; use shared::core::light::LightTrait; use shared::core::light::{Light, LightSampleContext}; @@ -99,7 +99,22 @@ impl PathIntegrator { lambda: &SampledWavelengths, sampler: &mut Sampler, ) -> SampledSpectrum { - let ctx = LightSampleContext::from(intr); + let mut ctx = LightSampleContext::from(intr); + // pbrt `PathIntegrator::SampleLd`: nudge the light-sampling position to the + // correct side of the surface, otherwise light leaks at grazing angles. + // if (IsReflective(flags) && !IsTransmissive(flags)) + // ctx.pi = intr.OffsetRayOrigin(intr.wo); + // else if (IsTransmissive(flags) && !IsReflective(flags)) + // ctx.pi = intr.OffsetRayOrigin(-intr.wo); + // The wavefront integrator already did this; omitting it here made the path + // integrator ~2.8% brighter than the wavefront on killeroo-gold. + let flags = bsdf.flags(); + let wo_nudge = intr.wo(); + if flags.is_reflective() && !flags.is_transmissive() { + ctx.pi = Point3fi::new_from_point(intr.offset_ray_vector(wo_nudge)); + } else if flags.is_transmissive() && !flags.is_reflective() { + ctx.pi = Point3fi::new_from_point(intr.offset_ray_vector(-wo_nudge)); + } let Some(sampled) = self.sampler.sample_with_context(&ctx, sampler.get1d()) else { return SampledSpectrum::zero();