// VolPathIntegrator Method Definitions Spectrum VolPathIntegrator::Li(const RayDifferential &r, const Scene &scene, Sampler &sampler, MemoryArena &arena, int depth) const { ProfilePhase p(Prof::SamplerIntegratorLi); Spectrum L(0.f), alpha(1.f); RayDifferential ray(r); bool specularBounce = false; for (int bounces = 0;; ++bounces) { // Store intersection into _isect_ SurfaceInteraction isect; bool foundIntersection = scene.Intersect(ray, &isect); // Sample the participating medium, if present MediumInteraction mi; if (ray.medium) alpha *= ray.medium->Sample(ray, sampler, arena, &mi); if (alpha.IsBlack()) break; // Handle an interaction with a medium or a surface if (mi.IsValid()) { // Handle medium scattering case Vector3f wo = -ray.d, wi; L += alpha * UniformSampleOneLight(mi, scene, sampler, arena, true); Point2f phaseSample = sampler.Get2D(); mi.phase->Sample_p(wo, &wi, phaseSample); ray = mi.SpawnRay(wi); } else { // Handle surface scattering case // Possibly add emitted light and terminate if (bounces == 0 || specularBounce) { // Add emitted light at path vertex or from the environment if (foundIntersection) L += alpha * isect.Le(-ray.d); else for (const auto &light : scene.lights) L += alpha * light->Le(ray); } if (!foundIntersection || bounces >= maxDepth) break; // Compute scattering functions and skip over medium boundaries isect.ComputeScatteringFunctions(ray, arena, true); if (!isect.bsdf) { ray = isect.SpawnRay(ray.d); bounces--; continue; } // Sample illumination from lights to find attenuated path // contribution L += alpha * UniformSampleOneLight(isect, scene, sampler, arena, true); // Sample BSDF to get new path direction Vector3f wo = -ray.d, wi; Float pdf; BxDFType flags; Spectrum f = isect.bsdf->Sample_f(wo, &wi, sampler.Get2D(), &pdf, BSDF_ALL, &flags); if (f.IsBlack() || pdf == 0.f) break; alpha *= f * AbsDot(wi, isect.shading.n) / pdf; Assert(std::isinf(alpha.y()) == false); specularBounce = (flags & BSDF_SPECULAR) != 0; ray = isect.SpawnRay(wi); // Account for attenuated subsurface scattering, if applicable if (isect.bssrdf && (flags & BSDF_TRANSMISSION)) { // Importance sample the BSSRDF SurfaceInteraction pi; Spectrum S = isect.bssrdf->Sample_S( scene, sampler.Get1D(), sampler.Get2D(), arena, &pi, &pdf); #ifndef NDEBUG Assert(std::isinf(alpha.y()) == false); #endif if (S.IsBlack() || pdf == 0) break; alpha *= S / pdf; // Account for the attenuated direct subsurface scattering // component L += alpha * UniformSampleOneLight(pi, scene, sampler, arena, true); // Account for the indirect subsurface scattering component Spectrum f = pi.bsdf->Sample_f(pi.wo, &wi, sampler.Get2D(), &pdf, BSDF_ALL, &flags); if (f.IsBlack() || pdf == 0.f) break; alpha *= f * AbsDot(wi, pi.shading.n) / pdf; #ifndef NDEBUG Assert(std::isinf(alpha.y()) == false); #endif specularBounce = (flags & BSDF_SPECULAR) != 0; ray = pi.SpawnRay(wi); } } // Possibly terminate the path if (bounces > 3) { Float continueProbability = std::min((Float).5, alpha.y()); if (sampler.Get1D() > continueProbability) break; alpha /= continueProbability; Assert(std::isinf(alpha.y()) == false); } } return L; }
int RandomWalk(const Scene &scene, RayDifferential ray, Sampler &sampler, MemoryArena &arena, Spectrum beta, Float pdf, int maxDepth, TransportMode mode, Vertex *path) { if (maxDepth == 0) return 0; int bounces = 0; // Declare variables for forward and reverse probability densities Float pdfFwd = pdf, pdfRev = 0; while (true) { // Attempt to create the next subpath vertex in _path_ MediumInteraction mi; VLOG(2) << "Random walk. Bounces " << bounces << ", beta " << beta << ", pdfFwd " << pdfFwd << ", pdfRev " << pdfRev; // Trace a ray and sample the medium, if any SurfaceInteraction isect; bool foundIntersection = scene.Intersect(ray, &isect); if (ray.medium) beta *= ray.medium->Sample(ray, sampler, arena, &mi); if (beta.IsBlack()) break; Vertex &vertex = path[bounces], &prev = path[bounces - 1]; if (mi.IsValid()) { // Record medium interaction in _path_ and compute forward density vertex = Vertex::CreateMedium(mi, beta, pdfFwd, prev); if (++bounces >= maxDepth) break; // Sample direction and compute reverse density at preceding vertex Vector3f wi; pdfFwd = pdfRev = mi.phase->Sample_p(-ray.d, &wi, sampler.Get2D()); ray = mi.SpawnRay(wi); } else { // Handle surface interaction for path generation if (!foundIntersection) { // Capture escaped rays when tracing from the camera if (mode == TransportMode::Radiance) { vertex = Vertex::CreateLight(EndpointInteraction(ray), beta, pdfFwd); ++bounces; } break; } // Compute scattering functions for _mode_ and skip over medium // boundaries isect.ComputeScatteringFunctions(ray, arena, true, mode); if (!isect.bsdf) { ray = isect.SpawnRay(ray.d); continue; } // Initialize _vertex_ with surface intersection information vertex = Vertex::CreateSurface(isect, beta, pdfFwd, prev); if (++bounces >= maxDepth) break; // Sample BSDF at current vertex and compute reverse probability Vector3f wi, wo = isect.wo; BxDFType type; Spectrum f = isect.bsdf->Sample_f(wo, &wi, sampler.Get2D(), &pdfFwd, BSDF_ALL, &type); VLOG(2) << "Random walk sampled dir " << wi << " f: " << f << ", pdfFwd: " << pdfFwd; if (f.IsBlack() || pdfFwd == 0.f) break; beta *= f * AbsDot(wi, isect.shading.n) / pdfFwd; VLOG(2) << "Random walk beta now " << beta; pdfRev = isect.bsdf->Pdf(wi, wo, BSDF_ALL); if (type & BSDF_SPECULAR) { vertex.delta = true; pdfRev = pdfFwd = 0; } beta *= CorrectShadingNormal(isect, wo, wi, mode); VLOG(2) << "Random walk beta after shading normal correction " << beta; ray = isect.SpawnRay(wi); } // Compute reverse area density at preceding vertex prev.pdfRev = vertex.ConvertDensity(pdfRev, prev); } return bounces; }
Spectrum VolPathIntegrator::Li(const RayDifferential &r, const Scene &scene, Sampler &sampler, MemoryArena &arena, int depth) const { ProfilePhase p(Prof::SamplerIntegratorLi); Spectrum L(0.f), beta(1.f); RayDifferential ray(r); bool specularBounce = false; int bounces; // Added after book publication: etaScale tracks the accumulated effect // of radiance scaling due to rays passing through refractive // boundaries (see the derivation on p. 527 of the third edition). We // track this value in order to remove it from beta when we apply // Russian roulette; this is worthwhile, since it lets us sometimes // avoid terminating refracted rays that are about to be refracted back // out of a medium and thus have their beta value increased. Float etaScale = 1; for (bounces = 0;; ++bounces) { // Intersect _ray_ with scene and store intersection in _isect_ SurfaceInteraction isect; bool foundIntersection = scene.Intersect(ray, &isect); // Sample the participating medium, if present MediumInteraction mi; if (ray.medium) beta *= ray.medium->Sample(ray, sampler, arena, &mi); if (beta.IsBlack()) break; // Handle an interaction with a medium or a surface if (mi.IsValid()) { // Terminate path if ray escaped or _maxDepth_ was reached if (bounces >= maxDepth) break; ++volumeInteractions; // Handle scattering at point in medium for volumetric path tracer const Distribution1D *lightDistrib = lightDistribution->Lookup(mi.p); L += beta * UniformSampleOneLight(mi, scene, arena, sampler, true, lightDistrib); Vector3f wo = -ray.d, wi; mi.phase->Sample_p(wo, &wi, sampler.Get2D()); ray = mi.SpawnRay(wi); } else { ++surfaceInteractions; // Handle scattering at point on surface for volumetric path tracer // Possibly add emitted light at intersection if (bounces == 0 || specularBounce) { // Add emitted light at path vertex or from the environment if (foundIntersection) L += beta * isect.Le(-ray.d); else for (const auto &light : scene.infiniteLights) L += beta * light->Le(ray); } // Terminate path if ray escaped or _maxDepth_ was reached if (!foundIntersection || bounces >= maxDepth) break; // Compute scattering functions and skip over medium boundaries isect.ComputeScatteringFunctions(ray, arena, true); if (!isect.bsdf) { ray = isect.SpawnRay(ray.d); bounces--; continue; } // Sample illumination from lights to find attenuated path // contribution const Distribution1D *lightDistrib = lightDistribution->Lookup(isect.p); L += beta * UniformSampleOneLight(isect, scene, arena, sampler, true, lightDistrib); // Sample BSDF to get new path direction Vector3f wo = -ray.d, wi; Float pdf; BxDFType flags; Spectrum f = isect.bsdf->Sample_f(wo, &wi, sampler.Get2D(), &pdf, BSDF_ALL, &flags); if (f.IsBlack() || pdf == 0.f) break; beta *= f * AbsDot(wi, isect.shading.n) / pdf; DCHECK(std::isinf(beta.y()) == false); specularBounce = (flags & BSDF_SPECULAR) != 0; if ((flags & BSDF_SPECULAR) && (flags & BSDF_TRANSMISSION)) { Float eta = isect.bsdf->eta; // Update the term that tracks radiance scaling for refraction // depending on whether the ray is entering or leaving the // medium. etaScale *= (Dot(wo, isect.n) > 0) ? (eta * eta) : 1 / (eta * eta); } ray = isect.SpawnRay(ray, wi, flags, isect.bsdf->eta); // Account for attenuated subsurface scattering, if applicable if (isect.bssrdf && (flags & BSDF_TRANSMISSION)) { // Importance sample the BSSRDF SurfaceInteraction pi; Spectrum S = isect.bssrdf->Sample_S( scene, sampler.Get1D(), sampler.Get2D(), arena, &pi, &pdf); DCHECK(std::isinf(beta.y()) == false); if (S.IsBlack() || pdf == 0) break; beta *= S / pdf; // Account for the attenuated direct subsurface scattering // component L += beta * UniformSampleOneLight(pi, scene, arena, sampler, true, lightDistribution->Lookup(pi.p)); // Account for the indirect subsurface scattering component Spectrum f = pi.bsdf->Sample_f(pi.wo, &wi, sampler.Get2D(), &pdf, BSDF_ALL, &flags); if (f.IsBlack() || pdf == 0) break; beta *= f * AbsDot(wi, pi.shading.n) / pdf; DCHECK(std::isinf(beta.y()) == false); specularBounce = (flags & BSDF_SPECULAR) != 0; ray = pi.SpawnRay(wi); } } // Possibly terminate the path with Russian roulette // Factor out radiance scaling due to refraction in rrBeta. Spectrum rrBeta = beta * etaScale; if (rrBeta.MaxComponentValue() < rrThreshold && bounces > 3) { Float q = std::max((Float).05, 1 - rrBeta.MaxComponentValue()); if (sampler.Get1D() < q) break; beta /= 1 - q; DCHECK(std::isinf(beta.y()) == false); } } ReportValue(pathLength, bounces); return L; }
int RandomWalk(const Scene &scene, RayDifferential ray, Sampler &sampler, MemoryArena &arena, Spectrum weight, Float pdfFwd, int maxdepth, TransportMode mode, Vertex *path) { int bounces = 0; if (maxdepth == 0) return 0; SurfaceInteraction isect; MediumInteraction mi; while (true) { // Trace a ray and sample the medium, if any bool foundIntersection = scene.Intersect(ray, &isect); if (ray.medium) weight *= ray.medium->Sample(ray, sampler, arena, &mi); if (weight.IsBlack()) break; Vertex &vertex = path[bounces], &prev = path[bounces - 1]; Float pdfRev; if (mi.IsValid()) { // Handle the medium case // Record medium interaction in _path_ and compute forward density vertex = Vertex(mi, weight); vertex.pdfFwd = ConvertDensity(prev, pdfFwd, vertex); if (++bounces >= maxdepth) break; // Sample direction and compute reverse density at preceding vertex Vector3f wi; pdfFwd = pdfRev = mi.phase->Sample_p(-ray.d, &wi, sampler.Get2D()); ray = mi.SpawnRay(wi); } else { // Handle the surface case if (!foundIntersection) { // Capture escaped rays when tracing from the camera if (mode == TransportMode::Radiance) { vertex = Vertex(VertexType::Light, EndpointInteraction(ray), weight); vertex.pdfFwd = pdfFwd; ++bounces; } break; } // Compute scattering functions for _mode_ and skip over medium // boundaries isect.ComputeScatteringFunctions(ray, arena, true, mode); if (!isect.bsdf) { ray = isect.SpawnRay(ray.d); continue; } // Fill _vertex_ with intersection information vertex = Vertex(isect, weight); vertex.pdfFwd = ConvertDensity(prev, pdfFwd, vertex); if (++bounces >= maxdepth) break; // Sample BSDF at current vertex and compute reverse probability Vector3f wi, wo = isect.wo; BxDFType flags; Spectrum f = isect.bsdf->Sample_f(wo, &wi, sampler.Get2D(), &pdfFwd, BSDF_ALL, &flags); if (f.IsBlack() || pdfFwd == 0.f) break; weight *= f * AbsDot(wi, isect.shading.n) / pdfFwd; pdfRev = isect.bsdf->Pdf(wi, wo, BSDF_ALL); if (flags & BSDF_SPECULAR) { vertex.delta = true; pdfRev = pdfFwd = 0; } weight *= ShadingNormalCorrection(isect, wo, wi, mode); ray = isect.SpawnRay(wi); } // Compute reverse area density at preceding vertex prev.pdfRev = ConvertDensity(vertex, pdfRev, prev); } return bounces; }