diff --git a/src/pbrt/gpu/aggregate.cpp b/src/pbrt/gpu/aggregate.cpp index b0a6204a19c28c3f3171eedd855f437347953fe4..9db1f8395d342535ffce7232dd0652b9bbd022f4 100644 --- a/src/pbrt/gpu/aggregate.cpp +++ b/src/pbrt/gpu/aggregate.cpp @@ -1153,11 +1153,12 @@ OptiXAggregate::ParamBufferState &OptiXAggregate::getParamBuffer( } void OptiXAggregate::IntersectClosest( - int maxRays, EscapedRayQueue *escapedRayQueue, HitAreaLightQueue *hitAreaLightQueue, + int maxRays, const RayQueue *rayQueue, + EscapedRayQueue *escapedRayQueue, HitAreaLightQueue *hitAreaLightQueue, MaterialEvalQueue *basicEvalMaterialQueue, MaterialEvalQueue *universalEvalMaterialQueue, MediumSampleQueue *mediumSampleQueue, - RayQueue *rayQueue, RayQueue *nextRayQueue) const { + RayQueue *nextRayQueue) const { std::pair events = GetProfilerEvents("Tracing closest hit rays"); diff --git a/src/pbrt/gpu/aggregate.h b/src/pbrt/gpu/aggregate.h index 6ced39df556c768bc4afe3604d5969f1605aa055..15a4d4b3d78c4fdc003aad298e7a4b1a7f44aefd 100644 --- a/src/pbrt/gpu/aggregate.h +++ b/src/pbrt/gpu/aggregate.h @@ -34,10 +34,10 @@ class OptiXAggregate : public WavefrontAggregate { Bounds3f Bounds() const { return bounds; } void IntersectClosest( - int maxRays, EscapedRayQueue *escapedRayQueue, + int maxRays, const RayQueue *rayQueue, EscapedRayQueue *escapedRayQueue, HitAreaLightQueue *hitAreaLightQueue, MaterialEvalQueue *basicEvalMaterialQueue, MaterialEvalQueue *universalEvalMaterialQueue, - MediumSampleQueue *mediumSampleQueue, RayQueue *rayQueue, RayQueue *nextRayQueue) const; + MediumSampleQueue *mediumSampleQueue, RayQueue *nextRayQueue) const; void IntersectShadow(int maxRays, ShadowRayQueue *shadowRayQueue, SOA *pixelSampleState) const; diff --git a/src/pbrt/gpu/optix.h b/src/pbrt/gpu/optix.h index 38fa51831734b4c48805044c41ccb84c92099f98..4c72110ceac09565581a1f9851ea83659a73a836 100644 --- a/src/pbrt/gpu/optix.h +++ b/src/pbrt/gpu/optix.h @@ -50,7 +50,7 @@ struct QuadricRecord { struct RayIntersectParameters { OptixTraversableHandle traversable; - RayQueue *rayQueue; + const RayQueue *rayQueue; // closest hit RayQueue *nextRayQueue; diff --git a/src/pbrt/util/pstd.h b/src/pbrt/util/pstd.h index 463e7b1a516562f10994b330cc19d1d031ad3eb5..ac3f32ec611944b0ea0f7e4c4a9ad3f8f699af15 100644 --- a/src/pbrt/util/pstd.h +++ b/src/pbrt/util/pstd.h @@ -1004,7 +1004,7 @@ struct tuple : tuple { }; template -tuple(Ts &&...)->tuple...>; +tuple(Ts &&...) -> tuple...>; template PBRT_CPU_GPU auto &get(tuple &t) { diff --git a/src/pbrt/util/scattering.h b/src/pbrt/util/scattering.h index 2fdcc17286b3712d4556e4119d451499fb792e87..b6ed66b398908b020497eff5922eca24d9c5debe 100644 --- a/src/pbrt/util/scattering.h +++ b/src/pbrt/util/scattering.h @@ -41,29 +41,6 @@ PBRT_CPU_GPU inline Float HenyeyGreenstein(Float cosTheta, Float g) { } // Fresnel Inline Functions -PBRT_CPU_GPU -inline Float FrComplex(Float cosTheta_i, pstd::complex eta) { - cosTheta_i = Clamp(cosTheta_i, 0, 1); - using Complex = pstd::complex; - - // Compute _cosTheta_t_ using Snell's law - Complex sinTheta_i = pstd::sqrt(1.f - cosTheta_i * cosTheta_i); - Complex sinTheta_t = sinTheta_i / eta; - Complex cosTheta_t = pstd::sqrt(1.f - sinTheta_t * sinTheta_t); - Complex r_parl = (eta * cosTheta_i - cosTheta_t) / (eta * cosTheta_i + cosTheta_t); - Complex r_perp = (eta * cosTheta_t - cosTheta_i) / (eta * cosTheta_t + cosTheta_i); - return (pstd::norm(r_parl) + pstd::norm(r_perp)) * .5f; -} - -PBRT_CPU_GPU -inline SampledSpectrum FrComplex(Float cosTheta_i, SampledSpectrum eta, - SampledSpectrum k) { - SampledSpectrum result; - for (int i = 0; i < NSpectrumSamples; ++i) - result[i] = FrComplex(cosTheta_i, pstd::complex(eta[i], k[i])); - return result; -} - PBRT_CPU_GPU inline Float FrDielectric(Float cosTheta_i, Float eta) { cosTheta_i = Clamp(cosTheta_i, -1, 1); @@ -88,6 +65,29 @@ inline Float FrDielectric(Float cosTheta_i, Float eta) { return (r_parl * r_parl + r_perp * r_perp) / 2; } +PBRT_CPU_GPU +inline Float FrComplex(Float cosTheta_i, pstd::complex eta) { + cosTheta_i = Clamp(cosTheta_i, 0, 1); + using Complex = pstd::complex; + + // Compute _cosTheta_t_ using Snell's law + Complex sinTheta_i = pstd::sqrt(1.f - cosTheta_i * cosTheta_i); + Complex sinTheta_t = sinTheta_i / eta; + Complex cosTheta_t = pstd::sqrt(1.f - sinTheta_t * sinTheta_t); + Complex r_parl = (eta * cosTheta_i - cosTheta_t) / (eta * cosTheta_i + cosTheta_t); + Complex r_perp = (eta * cosTheta_t - cosTheta_i) / (eta * cosTheta_t + cosTheta_i); + return (pstd::norm(r_parl) + pstd::norm(r_perp)) * .5f; +} + +PBRT_CPU_GPU +inline SampledSpectrum FrComplex(Float cosTheta_i, SampledSpectrum eta, + SampledSpectrum k) { + SampledSpectrum result; + for (int i = 0; i < NSpectrumSamples; ++i) + result[i] = FrComplex(cosTheta_i, pstd::complex(eta[i], k[i])); + return result; +} + // BSSRDF Utility Declarations PBRT_CPU_GPU Float FresnelMoment1(Float invEta); diff --git a/src/pbrt/wavefront/aggregate.cpp b/src/pbrt/wavefront/aggregate.cpp index ec86f351ce9b3a55b08b2c58dfd05b1c95e9467f..3dddf2ff8c548bb81bedb64b9d1c14a6dab97b3c 100644 --- a/src/pbrt/wavefront/aggregate.cpp +++ b/src/pbrt/wavefront/aggregate.cpp @@ -20,100 +20,101 @@ namespace pbrt { -CPUAggregate::CPUAggregate(ParsedScene &scene, Allocator alloc, - NamedTextures &textures, - const std::map *> &shapeIndexToAreaLights, - const std::map &media, - const std::map &namedMaterials, - const std::vector &materials) { +CPUAggregate::CPUAggregate( + ParsedScene &scene, Allocator alloc, NamedTextures &textures, + const std::map *> &shapeIndexToAreaLights, + const std::map &media, + const std::map &namedMaterials, + const std::vector &materials) { aggregate = scene.CreateAggregate(alloc, textures, shapeIndexToAreaLights, media, namedMaterials, materials); } -void CPUAggregate::IntersectClosest(int maxRays, EscapedRayQueue *escapedRayQueue, - HitAreaLightQueue *hitAreaLightQueue, - MaterialEvalQueue *basicEvalMaterialQueue, - MaterialEvalQueue *universalEvalMaterialQueue, - MediumSampleQueue *mediumSampleQueue, - RayQueue *rayQueue, RayQueue *nextRayQueue) const { - ParallelFor(0, rayQueue->Size(), - [=](int index) { - const RayWorkItem r = (*rayQueue)[index]; - pstd::optional si = aggregate.Intersect(r.ray, Infinity); - if (!si) { - EnqueueWorkAfterMiss(r, mediumSampleQueue, escapedRayQueue); - } else { - const SurfaceInteraction &intr = si->intr; - Float tHit = Distance(r.ray.o, intr.p()) / Length(r.ray.d); - MediumInterface mediumInterface = intr.mediumInterface ? *intr.mediumInterface : - MediumInterface(r.ray.medium); - - EnqueueWorkAfterIntersection(r, r.ray.medium /* FIXME DIFF THAN OPTIX WOT */, tHit, - si->intr, mediumSampleQueue, - nextRayQueue, hitAreaLightQueue, - basicEvalMaterialQueue, universalEvalMaterialQueue, - mediumInterface); - } - }); +// CPUAggregate Method Definitions +void CPUAggregate::IntersectClosest(int maxRays, const RayQueue *rayQueue, + EscapedRayQueue *escapedRayQueue, + HitAreaLightQueue *hitAreaLightQueue, + MaterialEvalQueue *basicEvalMaterialQueue, + MaterialEvalQueue *universalEvalMaterialQueue, + MediumSampleQueue *mediumSampleQueue, + RayQueue *nextRayQueue) const { + // _CPUAggregate::IntersectClosest()_ method implementation + ParallelFor(0, rayQueue->Size(), [=](int index) { + const RayWorkItem r = (*rayQueue)[index]; + // Intersect _r_'s ray with the scene and enqueue resulting work + pstd::optional si = aggregate.Intersect(r.ray, Infinity); + if (!si) + EnqueueWorkAfterMiss(r, mediumSampleQueue, escapedRayQueue); + else { + // Process queued ray intersected a surface + const SurfaceInteraction &intr = si->intr; + Float tHit = Distance(r.ray.o, intr.p()) / Length(r.ray.d); + MediumInterface mi = intr.mediumInterface ? *intr.mediumInterface + : MediumInterface(r.ray.medium); + // FIXME? Second arg r.ray.medium doesn't match OptiX path + EnqueueWorkAfterIntersection(r, r.ray.medium, tHit, si->intr, + mediumSampleQueue, nextRayQueue, + hitAreaLightQueue, basicEvalMaterialQueue, + universalEvalMaterialQueue, mi); + } + }); } void CPUAggregate::IntersectShadow(int maxRays, ShadowRayQueue *shadowRayQueue, - SOA *pixelSampleState) const { - ParallelFor(0, shadowRayQueue->Size(), - [=](int index) { - const ShadowRayWorkItem w = (*shadowRayQueue)[index]; - RecordShadowRayIntersection(w, pixelSampleState, - aggregate.IntersectP(w.ray, w.tMax)); - }); + SOA *pixelSampleState) const { + ParallelFor(0, shadowRayQueue->Size(), [=](int index) { + const ShadowRayWorkItem w = (*shadowRayQueue)[index]; + bool hit = aggregate.IntersectP(w.ray, w.tMax); + RecordShadowRayIntersection(w, pixelSampleState, hit); + }); } void CPUAggregate::IntersectShadowTr(int maxRays, ShadowRayQueue *shadowRayQueue, - SOA *pixelSampleState) const { - ParallelFor(0, shadowRayQueue->Size(), - [=](int index) { - const ShadowRayWorkItem w = (*shadowRayQueue)[index]; - pstd::optional si; - TraceTransmittance(w, pixelSampleState, - [&](Ray ray, Float tMax) -> TransmittanceTraceResult { - si = aggregate.Intersect(ray, tMax); + SOA *pixelSampleState) const { + ParallelFor(0, shadowRayQueue->Size(), [=](int index) { + const ShadowRayWorkItem w = (*shadowRayQueue)[index]; + pstd::optional si; + TraceTransmittance( + w, pixelSampleState, + [&](Ray ray, Float tMax) -> TransmittanceTraceResult { + si = aggregate.Intersect(ray, tMax); - if (!si) - return TransmittanceTraceResult{false, Point3f(), Material()}; - else - return TransmittanceTraceResult{true, si->intr.p(), si->intr.material}; - }, - [&](Point3f p) -> Ray { - return si->intr.SpawnRayTo(p); - }); - }); + if (!si) + return TransmittanceTraceResult{false, Point3f(), Material()}; + else + return TransmittanceTraceResult{true, si->intr.p(), + si->intr.material}; + }, + [&](Point3f p) -> Ray { return si->intr.SpawnRayTo(p); }); + }); } -void CPUAggregate::IntersectOneRandom(int maxRays, SubsurfaceScatterQueue *subsurfaceScatterQueue) const { - ParallelFor(0, subsurfaceScatterQueue->Size(), - [=](int index) { - const SubsurfaceScatterWorkItem &w = (*subsurfaceScatterQueue)[index]; - uint64_t seed = Hash(w.p0, w.p1); +void CPUAggregate::IntersectOneRandom( + int maxRays, SubsurfaceScatterQueue *subsurfaceScatterQueue) const { + ParallelFor(0, subsurfaceScatterQueue->Size(), [=](int index) { + const SubsurfaceScatterWorkItem &w = (*subsurfaceScatterQueue)[index]; + uint64_t seed = Hash(w.p0, w.p1); - WeightedReservoirSampler wrs(seed); - Interaction base(w.p0, 0.f /* FIXME time */, Medium()); - while (true) { - Ray r = base.SpawnRayTo(w.p1); - if (r.d == Vector3f(0, 0, 0)) - break; - pstd::optional si = aggregate.Intersect(r, 1); - if (!si) - break; - base = si->intr; - if (si->intr.material == w.material) - wrs.Add(SubsurfaceInteraction(si->intr), 1.f); - } + WeightedReservoirSampler wrs(seed); + Interaction base(w.p0, 0.f /* FIXME time */, Medium()); + while (true) { + Ray r = base.SpawnRayTo(w.p1); + if (r.d == Vector3f(0, 0, 0)) + break; + pstd::optional si = aggregate.Intersect(r, 1); + if (!si) + break; + base = si->intr; + if (si->intr.material == w.material) + wrs.Add(SubsurfaceInteraction(si->intr), 1.f); + } - if (wrs.HasSample()) { - subsurfaceScatterQueue->reservoirPDF[index] = wrs.SamplePDF(); - subsurfaceScatterQueue->ssi[index] = wrs.GetSample(); - } else - subsurfaceScatterQueue->reservoirPDF[index] = 0; - }); + if (wrs.HasSample()) { + subsurfaceScatterQueue->reservoirPDF[index] = wrs.SamplePDF(); + subsurfaceScatterQueue->ssi[index] = wrs.GetSample(); + } else + subsurfaceScatterQueue->reservoirPDF[index] = 0; + }); } } // namespace pbrt diff --git a/src/pbrt/wavefront/aggregate.h b/src/pbrt/wavefront/aggregate.h index ad93b27fcef09171402a472a15c6cdbf761b6da1..ba4b65272410a1461868e487bf505df22727b4f2 100644 --- a/src/pbrt/wavefront/aggregate.h +++ b/src/pbrt/wavefront/aggregate.h @@ -33,10 +33,10 @@ class CPUAggregate : public WavefrontAggregate { Bounds3f Bounds() const { return aggregate.Bounds(); } void IntersectClosest( - int maxRays, EscapedRayQueue *escapedRayQueue, + int maxRays, const RayQueue *rayQueue, EscapedRayQueue *escapedRayQueue, HitAreaLightQueue *hitAreaLightQueue, MaterialEvalQueue *basicEvalMaterialQueue, MaterialEvalQueue *universalEvalMaterialQueue, - MediumSampleQueue *mediumSampleQueue, RayQueue *rayQueue, RayQueue *nextRayQueue) const; + MediumSampleQueue *mediumSampleQueue, RayQueue *nextRayQueue) const; void IntersectShadow(int maxRays, ShadowRayQueue *shadowRayQueue, SOA *pixelSampleState) const; diff --git a/src/pbrt/wavefront/camera.cpp b/src/pbrt/wavefront/camera.cpp index c08c3cec4a14fa9d33848644dcbac0a97ef3d491..7d399b2b9053135658c573699b5a125bf96439c5 100644 --- a/src/pbrt/wavefront/camera.cpp +++ b/src/pbrt/wavefront/camera.cpp @@ -18,16 +18,16 @@ namespace pbrt { void WavefrontPathIntegrator::GenerateCameraRays(int y0, int sampleIndex) { // Define _generateRays_ lambda function auto generateRays = [=](auto sampler) { - using Sampler = std::remove_reference_t; - if constexpr (!std::is_same_v && - !std::is_same_v) - GenerateCameraRays(y0, sampleIndex); + using ConcreteSampler = std::remove_reference_t; + if constexpr (!std::is_same_v && + !std::is_same_v) + GenerateCameraRays(y0, sampleIndex); }; sampler.DispatchCPU(generateRays); } -template +template void WavefrontPathIntegrator::GenerateCameraRays(int y0, int sampleIndex) { RayQueue *rayQueue = CurrentRayQueue(0); ParallelFor( @@ -45,7 +45,7 @@ void WavefrontPathIntegrator::GenerateCameraRays(int y0, int sampleIndex) { return; // Initialize _Sampler_ for current pixel and sample - Sampler pixelSampler = *sampler.Cast(); + ConcreteSampler pixelSampler = *sampler.Cast(); pixelSampler.StartPixelSample(pPixel, sampleIndex, 0); // Sample wavelengths for ray path @@ -54,7 +54,7 @@ void WavefrontPathIntegrator::GenerateCameraRays(int y0, int sampleIndex) { lu = 0.5f; SampledWavelengths lambda = film.SampleWavelengths(lu); - // Compute _CameraSample_ and generate ray + // Generate _CameraSample_ and corresponding ray CameraSample cameraSample = GetCameraSample(pixelSampler, pPixel, filter); pstd::optional cameraRay = camera.GenerateRay(cameraSample, lambda); diff --git a/src/pbrt/wavefront/integrator.cpp b/src/pbrt/wavefront/integrator.cpp index 0eda82e6aedd779a27fa7cacb67c4d41eca734d5..32fee74811de70bea54c8a34e69c26687973ff65 100644 --- a/src/pbrt/wavefront/integrator.cpp +++ b/src/pbrt/wavefront/integrator.cpp @@ -127,7 +127,7 @@ WavefrontPathIntegrator::WavefrontPathIntegrator(Allocator alloc, ParsedScene &s pstd::vector allLights; - envLights = alloc.new_object>(alloc); + infiniteLights = alloc.new_object>(alloc); for (const auto &light : scene.lights) { Medium outsideMedium = findMedium(light.medium, &light.loc); if (light.renderFromObject.IsAnimated()) @@ -140,7 +140,7 @@ WavefrontPathIntegrator::WavefrontPathIntegrator(Allocator alloc, ParsedScene &s if (l.Is() || l.Is() || l.Is()) - envLights->push_back(l); + infiniteLights->push_back(l); allLights.push_back(l); } @@ -314,7 +314,7 @@ WavefrontPathIntegrator::WavefrontPathIntegrator(Allocator alloc, ParsedScene &s alloc.new_object(maxQueueSize, alloc); } - if (envLights->size()) + if (infiniteLights->size()) escapedRayQueue = alloc.new_object(maxQueueSize, alloc); hitAreaLightQueue = alloc.new_object(maxQueueSize, alloc); @@ -348,6 +348,9 @@ WavefrontPathIntegrator::WavefrontPathIntegrator(Allocator alloc, ParsedScene &s // WavefrontPathIntegrator Method Definitions Float WavefrontPathIntegrator::Render() { + Bounds2i pixelBounds = film.PixelBounds(); + Vector2i resolution = pixelBounds.Diagonal(); + Timer timer; // Prefetch allocations to GPU memory #ifdef PBRT_BUILD_GPU_RENDERER if (Options->useGPU) { @@ -387,9 +390,6 @@ Float WavefrontPathIntegrator::Render() { } #endif // PBRT_BUILD_GPU_RENDERER - Timer timer; - Vector2i resolution = film.PixelBounds().Diagonal(); - Bounds2i pixelBounds = film.PixelBounds(); // Launch thread to copy image for display server, if enabled RGB *displayRGB = nullptr, *displayRGBHost = nullptr; std::atomic exitCopyThread{false}; @@ -471,21 +471,31 @@ Float WavefrontPathIntegrator::Render() { }); } + // Loop over sample indices and evaluate pixel samples int firstSampleIndex = 0, lastSampleIndex = samplesPerPixel; // Update sample index range based on debug start, if provided if (!Options->debugStart.empty()) { std::vector values = SplitStringToInts(Options->debugStart, ','); - if (values.size() != 2) - ErrorExit("Expected two integer values for --debugstart."); + if (values.size() != 1 && values.size() != 2) + ErrorExit("Expected either one or two integer values for --debugstart."); firstSampleIndex = values[0]; - lastSampleIndex = firstSampleIndex + values[1]; + if (values.size() == 2) + lastSampleIndex = firstSampleIndex + values[1]; + else + lastSampleIndex = firstSampleIndex + 1; } ProgressReporter progress(lastSampleIndex - firstSampleIndex, "Rendering", Options->quiet, Options->useGPU); for (int sampleIndex = firstSampleIndex; sampleIndex < lastSampleIndex; ++sampleIndex) { + CheckCallbackScope _([&]() { + return StringPrintf("Wavefrontrendering failed at sample %d. Debug with " + "\"--debugstart %d\"\n", + sampleIndex, sampleIndex); + }); + // Render image for sample _sampleIndex_ LOG_VERBOSE("Starting to submit work for sample %d", sampleIndex); for (int y0 = pixelBounds.pMin.y; y0 < pixelBounds.pMax.y; @@ -531,11 +541,12 @@ Float WavefrontPathIntegrator::Render() { // Follow active ray paths and accumulate radiance estimates GenerateRaySamples(wavefrontDepth, sampleIndex); + // Find closest intersections along active rays aggregate->IntersectClosest( - maxQueueSize, escapedRayQueue, hitAreaLightQueue, - basicEvalMaterialQueue, universalEvalMaterialQueue, mediumSampleQueue, - CurrentRayQueue(wavefrontDepth), NextRayQueue(wavefrontDepth)); + maxQueueSize, CurrentRayQueue(wavefrontDepth), escapedRayQueue, + hitAreaLightQueue, basicEvalMaterialQueue, universalEvalMaterialQueue, + mediumSampleQueue, NextRayQueue(wavefrontDepth)); if (wavefrontDepth > 0) { // As above, with the indexing... @@ -545,18 +556,22 @@ Float WavefrontPathIntegrator::Render() { stats->indirectRays[wavefrontDepth] += statsQueue->Size(); }); } - if (haveMedia) - SampleMediumInteraction(wavefrontDepth); - if (escapedRayQueue) - HandleEscapedRays(); - HandleRayFoundEmission(); + + SampleMediumInteraction(wavefrontDepth); + + HandleEscapedRays(); + + HandleEmissiveIntersection(); + if (wavefrontDepth == maxDepth) break; + EvaluateMaterialsAndBSDFs(wavefrontDepth); + // Do immediately so that we have space for shadow rays for subsurface.. TraceShadowRays(wavefrontDepth); - if (haveSubsurface) - SampleSubsurface(wavefrontDepth); + + SampleSubsurface(wavefrontDepth); } UpdateFilm(); @@ -579,6 +594,7 @@ Float WavefrontPathIntegrator::Render() { progress.Update(); } progress.Done(); + #ifdef PBRT_BUILD_GPU_RENDERER if (Options->useGPU) GPUWait(); @@ -604,12 +620,15 @@ Float WavefrontPathIntegrator::Render() { } void WavefrontPathIntegrator::HandleEscapedRays() { + if (!escapedRayQueue) + return; + ForAllQueued( "Handle escaped rays", escapedRayQueue, maxQueueSize, PBRT_CPU_GPU_LAMBDA(const EscapedRayWorkItem w) { // Update pixel radiance for escaped ray SampledSpectrum L(0.f); - for (const auto &light : *envLights) { + for (const auto &light : *infiniteLights) { if (SampledSpectrum Le = light.Le(Ray(w.rayo, w.rayd), w.lambda); Le) { // Compute path radiance contribution from infinite light PBRT_DBG("L %f %f %f %f T_hat %f %f %f %f Le %f %f %f %f", L[0], L[1], @@ -643,7 +662,7 @@ void WavefrontPathIntegrator::HandleEscapedRays() { }); } -void WavefrontPathIntegrator::HandleRayFoundEmission() { +void WavefrontPathIntegrator::HandleEmissiveIntersection() { ForAllQueued( "Handle emitters hit by indirect rays", hitAreaLightQueue, maxQueueSize, PBRT_CPU_GPU_LAMBDA(const HitAreaLightWorkItem w) { diff --git a/src/pbrt/wavefront/integrator.h b/src/pbrt/wavefront/integrator.h index 16b9bd41fbdcc8424212100f56b1ec8767aa6ecc..cbb8d9116a08fba6a61b03ec3f24eb1c26eddd2d 100644 --- a/src/pbrt/wavefront/integrator.h +++ b/src/pbrt/wavefront/integrator.h @@ -30,16 +30,18 @@ class ParsedScene; // WavefrontAggregate Definition class WavefrontAggregate { public: + // WavefrontAggregate Interface virtual ~WavefrontAggregate() = default; virtual Bounds3f Bounds() const = 0; - virtual void IntersectClosest(int maxRays, EscapedRayQueue *escapedRayQueue, - HitAreaLightQueue *hitAreaLightQueue, - MaterialEvalQueue *basicEvalMaterialQueue, - MaterialEvalQueue *universalEvalMaterialQueue, - MediumSampleQueue *mediumSampleQueue, - RayQueue *rayQueue, RayQueue *nextRayQueue) const = 0; + virtual void IntersectClosest(int maxRays, const RayQueue *rayQ, + EscapedRayQueue *escapedRayQ, + HitAreaLightQueue *hitAreaLightQ, + MaterialEvalQueue *basicMtlQ, + MaterialEvalQueue *universalMtlQ, + MediumSampleQueue *mediumSampleQ, + RayQueue *nextRayQ) const = 0; virtual void IntersectShadow(int maxRays, ShadowRayQueue *shadowRayQueue, SOA *pixelSampleState) const = 0; @@ -72,7 +74,7 @@ class WavefrontPathIntegrator { void SampleSubsurface(int wavefrontDepth); void HandleEscapedRays(); - void HandleRayFoundEmission(); + void HandleEmissiveIntersection(); void EvaluateMaterialsAndBSDFs(int wavefrontDepth); template @@ -91,33 +93,32 @@ class WavefrontPathIntegrator { void UpdateFilm(); + WavefrontPathIntegrator(Allocator alloc, ParsedScene &scene); + template void ParallelFor(const char *description, int nItems, F &&func) { - if (Options->useGPU) { + if (Options->useGPU) #ifdef PBRT_BUILD_GPU_RENDERER GPUParallelFor(description, nItems, func); #else LOG_FATAL("Options->useGPU was set without PBRT_BUILD_GPU_RENDERER enabled"); #endif - } else + else pbrt::ParallelFor(0, nItems, func); } template void Do(const char *description, F &&func) { - if (Options->useGPU) { + if (Options->useGPU) #ifdef PBRT_BUILD_GPU_RENDERER - GPUParallelFor( - description, 1, PBRT_CPU_GPU_LAMBDA(int) { func(); }); + GPUParallelFor(description, 1, [=] PBRT_GPU(int) { func(); }); #else LOG_FATAL("Options->useGPU was set without PBRT_BUILD_GPU_RENDERER enabled"); #endif - } else + else func(); } - WavefrontPathIntegrator(Allocator alloc, ParsedScene &scene); - RayQueue *CurrentRayQueue(int wavefrontDepth) { return rayQueues[wavefrontDepth & 1]; } @@ -143,13 +144,11 @@ class WavefrontPathIntegrator { }; Stats *stats; - WavefrontAggregate *aggregate = nullptr; - Filter filter; Film film; Sampler sampler; Camera camera; - pstd::vector *envLights; + pstd::vector *infiniteLights; LightSampler lightSampler; int maxDepth, samplesPerPixel; @@ -161,6 +160,8 @@ class WavefrontPathIntegrator { RayQueue *rayQueues[2]; + WavefrontAggregate *aggregate = nullptr; + MediumSampleQueue *mediumSampleQueue = nullptr; MediumScatterQueue *mediumScatterQueue = nullptr; diff --git a/src/pbrt/wavefront/intersect.h b/src/pbrt/wavefront/intersect.h index bea30a9897e0a1d091faebbd0e5e985ce6e9c2e6..f66b2707590deb0121d9b0882abbe9a692d6ba71 100644 --- a/src/pbrt/wavefront/intersect.h +++ b/src/pbrt/wavefront/intersect.h @@ -1,73 +1,68 @@ +// pbrt is Copyright(c) 1998-2020 Matt Pharr, Wenzel Jakob, and Greg Humphreys. +// The pbrt source code is licensed under the Apache License, Version 2.0. +// SPDX: Apache-2.0 #ifndef PBRT_WAVEFRONT_INTERSECT_H #define PBRT_WAVEFRONT_INTERSECT_H #include -#include #include +#include namespace pbrt { -inline PBRT_CPU_GPU void -EnqueueWorkAfterMiss(RayWorkItem r, MediumSampleQueue *mediumSampleQueue, - EscapedRayQueue *escapedRayQueue) { +// Wavefront Ray Intersection Enqueuing Functions +inline PBRT_CPU_GPU void EnqueueWorkAfterMiss(RayWorkItem r, + MediumSampleQueue *mediumSampleQueue, + EscapedRayQueue *escapedRayQueue) { if (r.ray.medium) { PBRT_DBG("Adding miss ray to mediumSampleQueue. " "ray %f %f %f d %f %f %f T_hat %f %f %f %f\n", r.ray.o.x, r.ray.o.y, r.ray.o.z, r.ray.d.x, r.ray.d.y, r.ray.d.z, r.T_hat[0], r.T_hat[1], r.T_hat[2], r.T_hat[3]); - mediumSampleQueue->Push(r.ray, Infinity, r.lambda, r.T_hat, r.uniPathPDF, - r.lightPathPDF, r.pixelIndex, r.prevIntrCtx, - r.isSpecularBounce, - r.anyNonSpecularBounces, r.etaScale); + mediumSampleQueue->Push(r, Infinity); } else if (escapedRayQueue) { PBRT_DBG("Adding ray to escapedRayQueue pixel index %d\n", r.pixelIndex); - escapedRayQueue->Push( - EscapedRayWorkItem{r.ray.o, r.ray.d, r.depth, - r.lambda, r.pixelIndex, (int)r.isSpecularBounce, - r.T_hat, r.uniPathPDF, r.lightPathPDF, r.prevIntrCtx}); + escapedRayQueue->Push(r); } } -inline PBRT_CPU_GPU void -EnqueueWorkAfterIntersection(RayWorkItem r, Medium rayMedium, float tMax, SurfaceInteraction intr, - MediumSampleQueue *mediumSampleQueue, - RayQueue *nextRayQueue, - HitAreaLightQueue *hitAreaLightQueue, - MaterialEvalQueue *basicEvalMaterialQueue, - MaterialEvalQueue *universalEvalMaterialQueue, - MediumInterface mediumInterface) { +inline PBRT_CPU_GPU void EnqueueWorkAfterIntersection( + RayWorkItem r, Medium rayMedium, float tMax, SurfaceInteraction intr, + MediumSampleQueue *mediumSampleQueue, RayQueue *nextRayQueue, + HitAreaLightQueue *hitAreaLightQueue, MaterialEvalQueue *basicEvalMaterialQueue, + MaterialEvalQueue *universalEvalMaterialQueue, MediumInterface mediumInterface) { if (rayMedium) { assert(mediumSampleQueue); PBRT_DBG("Enqueuing into medium sample queue\n"); - mediumSampleQueue->Push( - MediumSampleWorkItem{r.ray, - r.depth, - tMax, - r.lambda, - r.T_hat, - r.uniPathPDF, - r.lightPathPDF, - r.pixelIndex, - r.prevIntrCtx, - r.isSpecularBounce, - r.anyNonSpecularBounces, - r.etaScale, - intr.areaLight, - intr.pi, - intr.n, - intr.dpdu, intr.dpdv, - -r.ray.d, - intr.uv, - intr.material, - intr.shading.n, - intr.shading.dpdu, - intr.shading.dpdv, - intr.shading.dndu, - intr.shading.dndv, - intr.faceIndex, - mediumInterface}); + mediumSampleQueue->Push(MediumSampleWorkItem{r.ray, + r.depth, + tMax, + r.lambda, + r.T_hat, + r.uniPathPDF, + r.lightPathPDF, + r.pixelIndex, + r.prevIntrCtx, + r.isSpecularBounce, + r.anyNonSpecularBounces, + r.etaScale, + intr.areaLight, + intr.pi, + intr.n, + intr.dpdu, + intr.dpdv, + -r.ray.d, + intr.uv, + intr.material, + intr.shading.n, + intr.shading.dpdu, + intr.shading.dpdv, + intr.shading.dndu, + intr.shading.dndv, + intr.faceIndex, + mediumInterface}); return; } @@ -76,18 +71,19 @@ EnqueueWorkAfterIntersection(RayWorkItem r, Medium rayMedium, float tMax, Surfac const MixMaterial *mix = material.CastOrNullptr(); while (mix) { - MaterialEvalContext ctx(intr); - material = mix->ChooseMaterial(BasicTextureEvaluator(), ctx); - mix = material.CastOrNullptr(); + MaterialEvalContext ctx(intr); + material = mix->ChooseMaterial(BasicTextureEvaluator(), ctx); + mix = material.CastOrNullptr(); } if (!material) { PBRT_DBG("Enqueuing into medium transition queue: pixel index %d \n", r.pixelIndex); Ray newRay = intr.SpawnRay(r.ray.d); - nextRayQueue->PushIndirectRay( - newRay, r.depth, r.prevIntrCtx, r.T_hat, r.uniPathPDF, r.lightPathPDF, r.lambda, - r.etaScale, r.isSpecularBounce, r.anyNonSpecularBounces, r.pixelIndex); + nextRayQueue->PushIndirectRay(newRay, r.depth, r.prevIntrCtx, r.T_hat, + r.uniPathPDF, r.lightPathPDF, r.lambda, r.etaScale, + r.isSpecularBounce, r.anyNonSpecularBounces, + r.pixelIndex); return; } @@ -96,10 +92,10 @@ EnqueueWorkAfterIntersection(RayWorkItem r, Medium rayMedium, float tMax, Surfac r.pixelIndex); Ray ray = r.ray; // TODO: intr.wo == -ray.d? - hitAreaLightQueue->Push(HitAreaLightWorkItem{ - intr.areaLight, intr.p(), intr.n, intr.uv, intr.wo, r.depth, r.lambda, - r.T_hat, r.uniPathPDF, r.lightPathPDF, r.prevIntrCtx, - (int)r.isSpecularBounce, r.pixelIndex}); + hitAreaLightQueue->Push( + HitAreaLightWorkItem{intr.areaLight, intr.p(), intr.n, intr.uv, intr.wo, + r.depth, r.lambda, r.T_hat, r.uniPathPDF, r.lightPathPDF, + r.prevIntrCtx, (int)r.isSpecularBounce, r.pixelIndex}); } FloatTexture displacement = material.GetDisplacement(); @@ -114,21 +110,37 @@ EnqueueWorkAfterIntersection(RayWorkItem r, Medium rayMedium, float tMax, Surfac auto enqueue = [=](auto ptr) { using Material = typename std::remove_reference_t; - q->Push(MaterialEvalWorkItem{ - ptr, intr.pi, intr.n, intr.dpdu, intr.dpdv, intr.shading.n, - intr.shading.dpdu, intr.shading.dpdv, intr.shading.dndu, intr.shading.dndv, - intr.uv, r.depth, intr.faceIndex, r.lambda, r.anyNonSpecularBounces, intr.wo, - r.pixelIndex, r.T_hat, r.uniPathPDF, r.etaScale, mediumInterface, intr.time}); + q->Push(MaterialEvalWorkItem{ptr, + intr.pi, + intr.n, + intr.dpdu, + intr.dpdv, + intr.shading.n, + intr.shading.dpdu, + intr.shading.dpdv, + intr.shading.dndu, + intr.shading.dndv, + intr.uv, + r.depth, + intr.faceIndex, + r.lambda, + r.anyNonSpecularBounces, + intr.wo, + r.pixelIndex, + r.T_hat, + r.uniPathPDF, + r.etaScale, + mediumInterface, + intr.time}); }; material.Dispatch(enqueue); PBRT_DBG("Closest hit found intersection at t %f\n", tMax); } - -inline PBRT_CPU_GPU void -RecordShadowRayIntersection(const ShadowRayWorkItem w, SOA *pixelSampleState, - bool foundIntersection) { +inline PBRT_CPU_GPU void RecordShadowRayIntersection( + const ShadowRayWorkItem w, SOA *pixelSampleState, + bool foundIntersection) { if (foundIntersection) { PBRT_DBG("Shadow ray was occluded\n"); return; @@ -137,8 +149,7 @@ RecordShadowRayIntersection(const ShadowRayWorkItem w, SOA *pi SampledSpectrum Ld = w.Ld / (w.uniPathPDF + w.lightPathPDF).Average(); PBRT_DBG("Unoccluded shadow ray. Final Ld %f %f %f %f " "(sr.Ld %f %f %f %f uniPathPDF %f %f %f %f lightPathPDF %f %f %f %f)\n", - Ld[0], Ld[1], Ld[2], Ld[3], - w.Ld[0], w.Ld[1], w.Ld[2], w.Ld[3], + Ld[0], Ld[1], Ld[2], Ld[3], w.Ld[0], w.Ld[1], w.Ld[2], w.Ld[3], w.uniPathPDF[0], w.uniPathPDF[1], w.uniPathPDF[2], w.uniPathPDF[3], w.lightPathPDF[0], w.lightPathPDF[1], w.lightPathPDF[2], w.lightPathPDF[3]); @@ -147,14 +158,13 @@ RecordShadowRayIntersection(const ShadowRayWorkItem w, SOA *pi } struct TransmittanceTraceResult { - bool hit; - Point3f pHit; - Material material; + bool hit; + Point3f pHit; + Material material; }; -inline PBRT_CPU_GPU -void rescale(SampledSpectrum &T_hat, SampledSpectrum &lightPathPDF, - SampledSpectrum &uniPathPDF) { +inline PBRT_CPU_GPU void rescale(SampledSpectrum &T_hat, SampledSpectrum &lightPathPDF, + SampledSpectrum &uniPathPDF) { if (T_hat.MaxComponentValue() > 0x1p24f || lightPathPDF.MaxComponentValue() > 0x1p24f || uniPathPDF.MaxComponentValue() > 0x1p24f) { @@ -170,10 +180,10 @@ void rescale(SampledSpectrum &T_hat, SampledSpectrum &lightPathPDF, } } - template -inline PBRT_CPU_GPU void -TraceTransmittance(ShadowRayWorkItem sr, SOA *pixelSampleState, - T trace, S spawnTo) { +template +inline PBRT_CPU_GPU void TraceTransmittance(ShadowRayWorkItem sr, + SOA *pixelSampleState, + T trace, S spawnTo) { SampledWavelengths lambda = sr.lambda; SampledSpectrum Ld = sr.Ld; @@ -187,9 +197,9 @@ TraceTransmittance(ShadowRayWorkItem sr, SOA *pixelSampleState SampledSpectrum uniPathPDF(1.f), lightPathPDF(1.f); while (ray.d != Vector3f(0, 0, 0)) { - PBRT_DBG("Tracing shadow tr shadow ray pixel index %d o %f %f %f d %f %f %f tMax %f\n", - sr.pixelIndex, ray.o.x, ray.o.y, ray.o.z, ray.d.x, ray.d.y, ray.d.z, - tMax); + PBRT_DBG( + "Tracing shadow tr shadow ray pixel index %d o %f %f %f d %f %f %f tMax %f\n", + sr.pixelIndex, ray.o.x, ray.o.y, ray.o.z, ray.d.x, ray.d.y, ray.d.z, tMax); TransmittanceTraceResult result = trace(ray, tMax); @@ -203,49 +213,51 @@ TraceTransmittance(ShadowRayWorkItem sr, SOA *pixelSampleState if (ray.medium) { PBRT_DBG("Ray medium %p. Will sample tmaj...\n", ray.medium.ptr()); - Float tEnd = - !result.hit ? tMax : (Distance(ray.o, Point3f(result.pHit)) / Length(ray.d)); - SampledSpectrum T_maj = - ray.medium.SampleT_maj(ray, tEnd, rng.Uniform(), rng, lambda, - [&](const MediumSample &mediumSample) { - const SampledSpectrum &T_maj = mediumSample.T_maj; - const MediumInteraction &intr = mediumSample.intr; - SampledSpectrum sigma_n = intr.sigma_n(); - - // ratio-tracking: only evaluate null scattering - T_ray *= T_maj * sigma_n; - lightPathPDF *= T_maj * intr.sigma_maj; - uniPathPDF *= T_maj * sigma_n; - - // Possibly terminate transmittance computation using Russian roulette - SampledSpectrum Tr = T_ray / (lightPathPDF + uniPathPDF).Average(); - if (Tr.MaxComponentValue() < 0.05f) { - Float q = 0.75f; - if (rng.Uniform() < q) - T_ray = SampledSpectrum(0.); - else { - lightPathPDF *= 1 - q; - uniPathPDF *= 1 - q; - } - } - - PBRT_DBG("T_maj %f %f %f %f sigma_n %f %f %f %f sigma_maj %f %f %f %f\n", - T_maj[0], T_maj[1], T_maj[2], T_maj[3], - sigma_n[0], sigma_n[1], sigma_n[2], sigma_n[3], - intr.sigma_maj[0], intr.sigma_maj[1], intr.sigma_maj[2], - intr.sigma_maj[3]); - PBRT_DBG("T_ray %f %f %f %f lightPathPDF %f %f %f %f uniPathPDF %f %f %f %f\n", - T_ray[0], T_ray[1], T_ray[2], T_ray[3], - lightPathPDF[0], lightPathPDF[1], lightPathPDF[2], lightPathPDF[3], - uniPathPDF[0], uniPathPDF[1], uniPathPDF[2], uniPathPDF[3]); - - if (!T_ray) - return false; - - rescale(T_ray, lightPathPDF, uniPathPDF); - - return true; - }); + Float tEnd = !result.hit + ? tMax + : (Distance(ray.o, Point3f(result.pHit)) / Length(ray.d)); + SampledSpectrum T_maj = ray.medium.SampleT_maj( + ray, tEnd, rng.Uniform(), rng, lambda, + [&](const MediumSample &mediumSample) { + const SampledSpectrum &T_maj = mediumSample.T_maj; + const MediumInteraction &intr = mediumSample.intr; + SampledSpectrum sigma_n = intr.sigma_n(); + + // ratio-tracking: only evaluate null scattering + T_ray *= T_maj * sigma_n; + lightPathPDF *= T_maj * intr.sigma_maj; + uniPathPDF *= T_maj * sigma_n; + + // Possibly terminate transmittance computation using Russian roulette + SampledSpectrum Tr = T_ray / (lightPathPDF + uniPathPDF).Average(); + if (Tr.MaxComponentValue() < 0.05f) { + Float q = 0.75f; + if (rng.Uniform() < q) + T_ray = SampledSpectrum(0.); + else { + lightPathPDF *= 1 - q; + uniPathPDF *= 1 - q; + } + } + + PBRT_DBG( + "T_maj %f %f %f %f sigma_n %f %f %f %f sigma_maj %f %f %f %f\n", + T_maj[0], T_maj[1], T_maj[2], T_maj[3], sigma_n[0], sigma_n[1], + sigma_n[2], sigma_n[3], intr.sigma_maj[0], intr.sigma_maj[1], + intr.sigma_maj[2], intr.sigma_maj[3]); + PBRT_DBG("T_ray %f %f %f %f lightPathPDF %f %f %f %f uniPathPDF %f " + "%f %f %f\n", + T_ray[0], T_ray[1], T_ray[2], T_ray[3], lightPathPDF[0], + lightPathPDF[1], lightPathPDF[2], lightPathPDF[3], + uniPathPDF[0], uniPathPDF[1], uniPathPDF[2], uniPathPDF[3]); + + if (!T_ray) + return false; + + rescale(T_ray, lightPathPDF, uniPathPDF); + + return true; + }); T_ray *= T_maj; lightPathPDF *= T_maj; uniPathPDF *= T_maj; @@ -258,23 +270,30 @@ TraceTransmittance(ShadowRayWorkItem sr, SOA *pixelSampleState ray = spawnTo(pLight); } - PBRT_DBG("Final T_ray %.9g %.9g %.9g %.9g sr.uniPathPDF %.9g %.9g %.9g %.9g uniPathPDF %.9g %.9g %.9g %.9g\n", - T_ray[0], T_ray[1], T_ray[2], T_ray[3], - sr.uniPathPDF[0], sr.uniPathPDF[1], sr.uniPathPDF[2], sr.uniPathPDF[3], - uniPathPDF[0], uniPathPDF[1], uniPathPDF[2], uniPathPDF[3]); + PBRT_DBG("Final T_ray %.9g %.9g %.9g %.9g sr.uniPathPDF %.9g %.9g %.9g %.9g " + "uniPathPDF %.9g %.9g %.9g %.9g\n", + T_ray[0], T_ray[1], T_ray[2], T_ray[3], sr.uniPathPDF[0], sr.uniPathPDF[1], + sr.uniPathPDF[2], sr.uniPathPDF[3], uniPathPDF[0], uniPathPDF[1], + uniPathPDF[2], uniPathPDF[3]); PBRT_DBG("sr.lightPathPDF %.9g %.9g %.9g %.9g lightPathPDF %.9g %.9g %.9g %.9g\n", - sr.lightPathPDF[0], sr.lightPathPDF[1], sr.lightPathPDF[2], sr.lightPathPDF[3], - lightPathPDF[0], lightPathPDF[1], lightPathPDF[2], lightPathPDF[3]); + sr.lightPathPDF[0], sr.lightPathPDF[1], sr.lightPathPDF[2], + sr.lightPathPDF[3], lightPathPDF[0], lightPathPDF[1], lightPathPDF[2], + lightPathPDF[3]); PBRT_DBG("scaled throughput %.9g %.9g %.9g %.9g\n", - T_ray[0] / (sr.uniPathPDF * uniPathPDF + sr.lightPathPDF * lightPathPDF).Average(), - T_ray[1] / (sr.uniPathPDF * uniPathPDF + sr.lightPathPDF * lightPathPDF).Average(), - T_ray[2] / (sr.uniPathPDF * uniPathPDF + sr.lightPathPDF * lightPathPDF).Average(), - T_ray[3] / (sr.uniPathPDF * uniPathPDF + sr.lightPathPDF * lightPathPDF).Average()); + T_ray[0] / + (sr.uniPathPDF * uniPathPDF + sr.lightPathPDF * lightPathPDF).Average(), + T_ray[1] / + (sr.uniPathPDF * uniPathPDF + sr.lightPathPDF * lightPathPDF).Average(), + T_ray[2] / + (sr.uniPathPDF * uniPathPDF + sr.lightPathPDF * lightPathPDF).Average(), + T_ray[3] / + (sr.uniPathPDF * uniPathPDF + sr.lightPathPDF * lightPathPDF).Average()); if (T_ray) { // FIXME/reconcile: this takes lightPathPDF as input while // e.g. VolPathIntegrator::SampleLd() does not... - Ld *= T_ray / (sr.uniPathPDF * uniPathPDF + sr.lightPathPDF * lightPathPDF).Average(); + Ld *= T_ray / + (sr.uniPathPDF * uniPathPDF + sr.lightPathPDF * lightPathPDF).Average(); PBRT_DBG("Setting final Ld for shadow ray pixel index %d = as %f %f %f %f\n", sr.pixelIndex, Ld[0], Ld[1], Ld[2], Ld[3]); @@ -284,6 +303,6 @@ TraceTransmittance(ShadowRayWorkItem sr, SOA *pixelSampleState } } -} // namespace pbrt +} // namespace pbrt -#endif // PBRT_WAVEFRONT_INTERSECT_H +#endif // PBRT_WAVEFRONT_INTERSECT_H diff --git a/src/pbrt/wavefront/media.cpp b/src/pbrt/wavefront/media.cpp index 550f99aceb2eb7441703f473f8d584aef630a4ee..7e6225f259af640618fd59e6654366ca2709e1cc 100644 --- a/src/pbrt/wavefront/media.cpp +++ b/src/pbrt/wavefront/media.cpp @@ -44,6 +44,9 @@ struct SampleMediumScatteringCallback { // WavefrontPathIntegrator Participating Media Methods void WavefrontPathIntegrator::SampleMediumInteraction(int wavefrontDepth) { + if (!haveMedia) + return; + RayQueue *nextRayQueue = NextRayQueue(wavefrontDepth); ForAllQueued( "Sample medium interaction", mediumSampleQueue, maxQueueSize, diff --git a/src/pbrt/wavefront/samples.cpp b/src/pbrt/wavefront/samples.cpp index 0a1df0a396fbcc445b88409fae53d7980d9336b8..43a1d2fb5a6b8239fa73369cfa1f6cdee0d5aeb8 100644 --- a/src/pbrt/wavefront/samples.cpp +++ b/src/pbrt/wavefront/samples.cpp @@ -14,18 +14,18 @@ namespace pbrt { // WavefrontPathIntegrator Sampler Methods void WavefrontPathIntegrator::GenerateRaySamples(int wavefrontDepth, int sampleIndex) { auto generateSamples = [=](auto sampler) { - using Sampler = std::remove_reference_t; - if constexpr (!std::is_same_v && - !std::is_same_v) - GenerateRaySamples(wavefrontDepth, sampleIndex); + using ConcreteSampler = std::remove_reference_t; + if constexpr (!std::is_same_v && + !std::is_same_v) + GenerateRaySamples(wavefrontDepth, sampleIndex); }; sampler.DispatchCPU(generateSamples); } -template +template void WavefrontPathIntegrator::GenerateRaySamples(int wavefrontDepth, int sampleIndex) { // Generate description string _desc_ for ray sample generation - std::string desc = std::string("Generate ray samples - ") + Sampler::Name(); + std::string desc = std::string("Generate ray samples - ") + ConcreteSampler::Name(); RayQueue *rayQueue = CurrentRayQueue(wavefrontDepth); ForAllQueued( @@ -39,7 +39,7 @@ void WavefrontPathIntegrator::GenerateRaySamples(int wavefrontDepth, int sampleI dimension += 2 * w.depth; // Initialize _Sampler_ for pixel, sample index, and dimension - Sampler pixelSampler = *sampler.Cast(); + ConcreteSampler pixelSampler = *sampler.Cast(); Point2i pPixel = pixelSampleState.pPixel[w.pixelIndex]; pixelSampler.StartPixelSample(pPixel, sampleIndex, dimension); @@ -47,7 +47,7 @@ void WavefrontPathIntegrator::GenerateRaySamples(int wavefrontDepth, int sampleI RaySamples rs; rs.direct.uc = pixelSampler.Get1D(); rs.direct.u = pixelSampler.Get2D(); - // Initialize indirect and possibly subsurface samples in _rs_ + // Initialize indirect and possibly subsurface and medium samples in _rs_ rs.indirect.uc = pixelSampler.Get1D(); rs.indirect.u = pixelSampler.Get2D(); rs.indirect.rr = pixelSampler.Get1D(); diff --git a/src/pbrt/wavefront/subsurface.cpp b/src/pbrt/wavefront/subsurface.cpp index 9702bb1fac46fbbc1696f2862785eeb13f72e3d7..72f64f14a7f339ecde88a0526eaa19ae043204ca 100644 --- a/src/pbrt/wavefront/subsurface.cpp +++ b/src/pbrt/wavefront/subsurface.cpp @@ -16,6 +16,9 @@ namespace pbrt { // WavefrontPathIntegrator Subsurface Scattering Methods void WavefrontPathIntegrator::SampleSubsurface(int wavefrontDepth) { + if (!haveSubsurface) + return; + RayQueue *rayQueue = CurrentRayQueue(wavefrontDepth); RayQueue *nextRayQueue = NextRayQueue(wavefrontDepth); diff --git a/src/pbrt/wavefront/workitems.h b/src/pbrt/wavefront/workitems.h index 9aa260a481e89938398ebb33dd8b74d3df659779..ebb85a1cba5a092410e2ee097360710aa144988d 100644 --- a/src/pbrt/wavefront/workitems.h +++ b/src/pbrt/wavefront/workitems.h @@ -156,9 +156,6 @@ struct EscapedRayWorkItem { LightSampleContext prevIntrCtx; }; -// EscapedRayQueue Definition -using EscapedRayQueue = WorkQueue; - // HitAreaLightWorkItem Definition struct HitAreaLightWorkItem { // HitAreaLightWorkItem Public Members @@ -412,6 +409,24 @@ class ShadowRayQueue : public WorkQueue { } }; +// EscapedRayQueue Definition +class EscapedRayQueue : public WorkQueue { + public: + // EscapedRayQueue Public Methods + PBRT_CPU_GPU + int Push(RayWorkItem r); + + using WorkQueue::WorkQueue; + + using WorkQueue::Push; +}; + +inline int EscapedRayQueue::Push(RayWorkItem r) { + return Push(EscapedRayWorkItem{r.ray.o, r.ray.d, r.depth, r.lambda, r.pixelIndex, + (int)r.isSpecularBounce, r.T_hat, r.uniPathPDF, + r.lightPathPDF, r.prevIntrCtx}); +} + // GetBSSRDFAndProbeRayQueue Definition class GetBSSRDFAndProbeRayQueue : public WorkQueue { public: @@ -492,6 +507,13 @@ class MediumSampleQueue : public WorkQueue { this->etaScale[index] = etaScale; return index; } + + PBRT_CPU_GPU + int Push(RayWorkItem r, Float tMax) { + return Push(r.ray, tMax, r.lambda, r.T_hat, r.uniPathPDF, r.lightPathPDF, + r.pixelIndex, r.prevIntrCtx, r.isSpecularBounce, + r.anyNonSpecularBounces, r.etaScale); + } }; // MediumScatterQueue Definition diff --git a/src/pbrt/wavefront/workqueue.h b/src/pbrt/wavefront/workqueue.h index 7fbd73793fecd744e7647dc45f28c9ab367a5b59..688160708e4843bd4d499ad84c07b1d70403189c 100644 --- a/src/pbrt/wavefront/workqueue.h +++ b/src/pbrt/wavefront/workqueue.h @@ -116,10 +116,12 @@ class WorkQueue : public SOA { // WorkQueue Inline Functions template -void ForAllQueued(const char *desc, WorkQueue *q, int maxQueued, F &&func) { - if (Options->useGPU) +void ForAllQueued(const char *desc, const WorkQueue *q, int maxQueued, + F &&func) { + if (Options->useGPU) { + // Launch GPU threads to process _q_ using _func_ #ifdef PBRT_BUILD_GPU_RENDERER - GPUParallelFor(desc, maxQueued, [=] PBRT_GPU(int index) mutable { + GPUParallelFor(desc, maxQueued, [=] PBRT_GPU(int index) { if (index >= q->Size()) return; func((*q)[index]); @@ -127,8 +129,11 @@ void ForAllQueued(const char *desc, WorkQueue *q, int maxQueued, F &&f #else LOG_FATAL("Options->useGPU was set without PBRT_BUILD_GPU_RENDERER enabled"); #endif - else + + } else { + // Process _q_ using _func_ with CPU threads ParallelFor(0, q->Size(), [&](int index) { func((*q)[index]); }); + } } // MultiWorkQueue Definition @@ -157,6 +162,7 @@ class MultiWorkQueue> { PBRT_CPU_GPU int Push(const T &value) { return Get()->Push(value); } + PBRT_CPU_GPU void Reset() { (Get()->Reset(), ...); }