Spectrum DirectLightingIntegrator::Li(const RayDifferential &ray, const Scene &scene, Sampler &sampler, MemoryArena &arena, int depth) const { ProfilePhase p(Prof::SamplerIntegratorLi); Spectrum L(0.f); // Find closest ray intersection or return background radiance SurfaceInteraction isect; if (!scene.Intersect(ray, &isect)) { for (const auto &light : scene.lights) L += light->Le(ray); return L; } // Compute scattering functions for surface interaction isect.ComputeScatteringFunctions(ray, arena); if (!isect.bsdf) return Li(isect.SpawnRay(ray.d), scene, sampler, arena, depth); Vector3f wo = isect.wo; // Compute emitted light if ray hit an area light source L += isect.Le(wo); if (scene.lights.size() > 0) { // Compute direct lighting for _DirectLightingIntegrator_ integrator if (strategy == LightStrategy::UniformSampleAll) L += UniformSampleAllLights(isect, scene, arena, sampler, nLightSamples); else L += UniformSampleOneLight(isect, scene, arena, sampler); } if (depth + 1 < maxDepth) { Vector3f wi; // Trace rays for specular reflection and refraction L += SpecularReflect(ray, isect, scene, sampler, arena, depth); L += SpecularTransmit(ray, isect, scene, sampler, arena, depth); } return L; }
// WhittedIntegrator Method Definitions Spectrum WhittedIntegrator::Li(const RayDifferential &ray, const Scene &scene, Sampler &sampler, MemoryArena &arena, int depth) const { Spectrum L(0.); // Find closest ray intersection or return background radiance SurfaceInteraction isect; if (!scene.Intersect(ray, &isect)) { for (const auto &light : scene.lights) L += light->Le(ray); return L; } // Compute emitted and reflected light at ray intersection point // Initialize common variables for Whitted integrator const Normal3f &n = isect.shading.n; Vector3f wo = isect.wo; // Compute scattering functions for surface interaction isect.ComputeScatteringFunctions(ray, arena); if (!isect.bsdf) return Li(isect.SpawnRay(ray.d), scene, sampler, arena, depth); // Compute emitted light if ray hit an area light source L += isect.Le(wo); // Add contribution of each light source for (const auto &light : scene.lights) { Vector3f wi; Float pdf; VisibilityTester visibility; Spectrum Li = light->Sample_Li(isect, sampler.Get2D(), &wi, &pdf, &visibility); if (Li.IsBlack() || pdf == 0) continue; Spectrum f = isect.bsdf->f(wo, wi); if (!f.IsBlack() && visibility.Unoccluded(scene)) L += f * Li * AbsDot(wi, n) / pdf; } if (depth + 1 < maxDepth) { // Trace rays for specular reflection and refraction L += SpecularReflect(ray, isect, scene, sampler, arena, depth); L += SpecularTransmit(ray, isect, scene, sampler, arena, depth); } return L; }
Spectrum VisibilityTester::Tr(const Scene &scene, Sampler &sampler) const { Ray ray(p0.SpawnRayTo(p1)); Spectrum Tr(1.f); while (true) { SurfaceInteraction isect; bool hitSurface = scene.Intersect(ray, &isect); // Handle opaque surface along ray's path if (hitSurface && isect.primitive->GetMaterial() != nullptr) return Spectrum(0.0f); // Update transmittance for current ray segment if (ray.medium) Tr *= ray.medium->Tr(ray, sampler); // Generate next ray segment or return final transmittance if (!hitSurface) break; ray = isect.SpawnRayTo(p1); } return Tr; }
Spectrum SamplerIntegrator::SpecularTransmit( const RayDifferential &ray, const SurfaceInteraction &isect, const Scene &scene, Sampler &sampler, MemoryArena &arena, int depth) const { Vector3f wo = isect.wo, wi; Float pdf; const Point3f &p = isect.p; const Normal3f &ns = isect.shading.n; const BSDF &bsdf = *isect.bsdf; Spectrum f = bsdf.Sample_f(wo, &wi, sampler.Get2D(), &pdf, BxDFType(BSDF_TRANSMISSION | BSDF_SPECULAR)); Spectrum L = Spectrum(0.f); if (pdf > 0.f && !f.IsBlack() && AbsDot(wi, ns) != 0.f) { // Compute ray differential _rd_ for specular transmission RayDifferential rd = isect.SpawnRay(wi); if (ray.hasDifferentials) { rd.hasDifferentials = true; rd.rxOrigin = p + isect.dpdx; rd.ryOrigin = p + isect.dpdy; Float eta = bsdf.eta; Vector3f w = -wo; if (Dot(wo, ns) < 0) eta = 1.f / eta; Normal3f dndx = isect.shading.dndu * isect.dudx + isect.shading.dndv * isect.dvdx; Normal3f dndy = isect.shading.dndu * isect.dudy + isect.shading.dndv * isect.dvdy; Vector3f dwodx = -ray.rxDirection - wo, dwody = -ray.ryDirection - wo; Float dDNdx = Dot(dwodx, ns) + Dot(wo, dndx); Float dDNdy = Dot(dwody, ns) + Dot(wo, dndy); Float mu = eta * Dot(w, ns) - Dot(wi, ns); Float dmudx = (eta - (eta * eta * Dot(w, ns)) / Dot(wi, ns)) * dDNdx; Float dmudy = (eta - (eta * eta * Dot(w, ns)) / Dot(wi, ns)) * dDNdy; rd.rxDirection = wi + eta * dwodx - Vector3f(mu * dndx + dmudx * ns); rd.ryDirection = wi + eta * dwody - Vector3f(mu * dndy + dmudy * ns); } L = f * Li(rd, scene, sampler, arena, depth + 1) * AbsDot(wi, ns) / pdf; } return L; }
Spectrum SamplerIntegrator::SpecularReflect( const RayDifferential &ray, const SurfaceInteraction &isect, const Scene &scene, Sampler &sampler, MemoryArena &arena, int depth) const { // Compute specular reflection direction _wi_ and BSDF value Vector3f wo = isect.wo, wi; Float pdf; BxDFType type = BxDFType(BSDF_REFLECTION | BSDF_SPECULAR); Spectrum f = isect.bsdf->Sample_f(wo, &wi, sampler.Get2D(), &pdf, type); // Return contribution of specular reflection const Normal3f &ns = isect.shading.n; if (pdf > 0.f && !f.IsBlack() && AbsDot(wi, ns) != 0.f) { // Compute ray differential _rd_ for specular reflection RayDifferential rd = isect.SpawnRay(wi); if (ray.hasDifferentials) { rd.hasDifferentials = true; rd.rxOrigin = isect.p + isect.dpdx; rd.ryOrigin = isect.p + isect.dpdy; // Compute differential reflected directions Normal3f dndx = isect.shading.dndu * isect.dudx + isect.shading.dndv * isect.dvdx; Normal3f dndy = isect.shading.dndu * isect.dudy + isect.shading.dndv * isect.dvdy; Vector3f dwodx = -ray.rxDirection - wo, dwody = -ray.ryDirection - wo; Float dDNdx = Dot(dwodx, ns) + Dot(wo, dndx); Float dDNdy = Dot(dwody, ns) + Dot(wo, dndy); rd.rxDirection = wi - dwodx + 2.f * Vector3f(Dot(wo, ns) * dndx + dDNdx * ns); rd.ryDirection = wi - dwody + 2.f * Vector3f(Dot(wo, ns) * dndy + dDNdy * ns); } return f * Li(rd, scene, sampler, arena, depth + 1) * AbsDot(wi, ns) / pdf; } else return Spectrum(0.f); }
// 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; }
rose::Spectrum rose::PathTracerIntegrator::IncomingRadiance(const rose::Ray &ray, const rose::RayInterval &interval, const rose::Scene &scene, uint32_t) const { // Final incoming radiance and accumulation value Spectrum L(0.f), beta(1.f); Ray current_ray(ray); RayInterval current_interval(interval); bool specular_bounce = false; uint32_t bounces; // Keep track of specular bounce for (bounces = 0;; ++bounces) { SurfaceInteraction interaction; bool found_interaction = scene.Intersect(current_ray, current_interval, &interaction); // Possibly add emission if (bounces == 0 || specular_bounce) { // Add emitted light if (found_interaction) { L += beta * interaction.Le(Normalize(-current_ray.Direction())); } else { for (const auto &light : scene.Lights()) { L += beta * light->Le(current_ray); } } } // Terminate path if ray escaped or max depth reached if (!found_interaction || bounces >= max_depth) { break; } // Compute scattering function std::unique_ptr<BSDF> bsdf = interaction.CreateBSDF(true); // Get shading variables const Vector3f n = interaction.sh_frame.N(); const Vector3f wo = Normalize(-current_ray.Direction()); // Loop over all lights and add direct contribution for (const auto &light : scene.Lights()) { // Sample light LightSample light_sample; OcclusionTester occlusion_tester; Spectrum Li = light->SampleLi(interaction, sampler->Next2D(), &light_sample, &occlusion_tester); // Check light return if (Li.IsBlack() || light_sample.pdf == 0.f) { continue; } // Evaluate BSDF Spectrum f = bsdf->F(wo, light_sample.wi); if (!f.IsBlack() && occlusion_tester.Unoccluded(scene)) { L += beta * f * Li * AbsDotProduct(n, light_sample.wi) / light_sample.pdf; } } // Sample BSDF to get new path BXDFSample bsdf_sample; Spectrum f = bsdf->SampleF(wo, sampler->Next2D(), &bsdf_sample); if (f.IsBlack() || bsdf_sample.pdf == 0.f) { break; } // Update beta beta *= f * AbsDotProduct(bsdf_sample.wi, n) / bsdf_sample.pdf; // Check if bounce is specular specular_bounce = (bsdf_sample.sampled_type & SPECULAR) != 0; // Compute new ray current_ray = interaction.SpawnRay(bsdf_sample.wi); // Possibly terminate path with Russian roulette if (bounces > 3) { float q = std::max(0.05f, 1.f - beta.Norm()); if (sampler->Next1D() < q) { break; } beta /= 1.f - q; } } return L; }
// PathIntegrator Method Definitions Spectrum PathIntegrator::Li(const RayDifferential &r, const Scene &scene, Sampler &sampler, MemoryArena &arena) const { Spectrum L(0.f); // Declare common path integration variables RayDifferential ray(r); Spectrum pathThroughput = Spectrum(1.f); bool specularBounce = false; for (int bounces = 0;; ++bounces) { // Store intersection into _isect_ SurfaceInteraction isect; bool foundIntersection = scene.Intersect(ray, &isect); // Possibly add emitted light and terminate if (bounces == 0 || specularBounce) { // Add emitted light at path vertex or from the environment if (foundIntersection) L += pathThroughput * isect.Le(-ray.d); else for (const auto &light : scene.lights) L += pathThroughput * 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 path contribution L += pathThroughput * UniformSampleOneLight(isect, scene, sampler, arena); // 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; pathThroughput *= f * AbsDot(wi, isect.shading.n) / pdf; #ifndef NDEBUG Assert(std::isinf(pathThroughput.y()) == false); #endif specularBounce = (flags & BSDF_SPECULAR) != 0; ray = isect.SpawnRay(wi); // Account for subsurface scattering, if applicable if (isect.bssrdf && (flags & BSDF_TRANSMISSION)) { // Importance sample the BSSRDF BSSRDFSample bssrdfSample; bssrdfSample.uDiscrete = sampler.Get1D(); bssrdfSample.pos = sampler.Get2D(); SurfaceInteraction isect_out = isect; pathThroughput *= isect.bssrdf->Sample_f( isect_out, scene, ray.time, bssrdfSample, arena, &isect, &pdf); #ifndef NDEBUG Assert(std::isinf(pathThroughput.y()) == false); #endif if (pathThroughput.IsBlack()) break; // Account for the direct subsurface scattering component isect.wo = Vector3f(isect.shading.n); // Sample illumination from lights to find path contribution L += pathThroughput * UniformSampleOneLight(isect, scene, sampler, arena); // Account for the indirect subsurface scattering component Spectrum f = isect.bsdf->Sample_f(isect.wo, &wi, sampler.Get2D(), &pdf, BSDF_ALL, &flags); if (f.IsBlack() || pdf == 0.f) break; pathThroughput *= f * AbsDot(wi, isect.shading.n) / pdf; #ifndef NDEBUG Assert(std::isinf(pathThroughput.y()) == false); #endif specularBounce = (flags & BSDF_SPECULAR) != 0; ray = isect.SpawnRay(wi); } // Possibly terminate the path if (bounces > 3) { Float continueProbability = std::min((Float).5, pathThroughput.y()); if (sampler.Get1D() > continueProbability) break; pathThroughput /= continueProbability; Assert(std::isinf(pathThroughput.y()) == false); } } return L; }
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 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; }
// PathIntegrator Method Definitions Spectrum PathIntegrator::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; for (int bounces = 0;; ++bounces) { // Find next path vertex and accumulate contribution // Intersect _ray_ with scene and store intersection in _isect_ SurfaceInteraction isect; bool foundIntersection = scene.Intersect(ray, &isect); // 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.lights) 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 path contribution L += beta * UniformSampleOneLight(isect, scene, arena, sampler); // 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; Assert(std::isinf(beta.y()) == false); specularBounce = (flags & BSDF_SPECULAR) != 0; ray = isect.SpawnRay(wi); // Account for 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(beta.y()) == false); #endif if (S.IsBlack() || pdf == 0) break; beta *= S / pdf; // Account for the direct subsurface scattering component L += beta * UniformSampleOneLight(pi, scene, arena, sampler); // 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; #ifndef NDEBUG Assert(std::isinf(beta.y()) == false); #endif specularBounce = (flags & BSDF_SPECULAR) != 0; ray = pi.SpawnRay(wi); } // Possibly terminate the path with Russian roulette if (bounces > 3) { Float continueProbability = std::min((Float).95, beta.y()); if (sampler.Get1D() > continueProbability) break; beta /= continueProbability; Assert(std::isinf(beta.y()) == false); } } return L; }
Spectrum EstimateDirect(const Interaction &it, const Point2f &uScattering, const Light &light, const Point2f &uLight, const Scene &scene, Sampler &sampler, MemoryArena &arena, bool handleMedia, bool specular) { BxDFType bsdfFlags = specular ? BSDF_ALL : BxDFType(BSDF_ALL & ~BSDF_SPECULAR); Spectrum Ld(0.f); // Sample light source with multiple importance sampling Vector3f wi; Float lightPdf = 0, scatteringPdf = 0; VisibilityTester visibility; Spectrum Li = light.Sample_Li(it, uLight, &wi, &lightPdf, &visibility); if (lightPdf > 0 && !Li.IsBlack()) { // Compute BSDF or phase function's value for light sample Spectrum f; if (it.IsSurfaceInteraction()) { // Evaluate BSDF for light sampling strategy const SurfaceInteraction &isect = (const SurfaceInteraction &)it; f = isect.bsdf->f(isect.wo, wi, bsdfFlags) * AbsDot(wi, isect.shading.n); scatteringPdf = isect.bsdf->Pdf(isect.wo, wi, bsdfFlags); } else { // Evaluate phase function for light sampling strategy const MediumInteraction &mi = (const MediumInteraction &)it; Float p = mi.phase->p(mi.wo, wi); f = Spectrum(p); scatteringPdf = p; } if (!f.IsBlack()) { // Compute effect of visibility for light source sample if (handleMedia) Li *= visibility.Tr(scene, sampler); else if (!visibility.Unoccluded(scene)) Li = Spectrum(0.f); // Add light's contribution to reflected radiance if (!Li.IsBlack()) { if (IsDeltaLight(light.flags)) Ld += f * Li / lightPdf; else { Float weight = PowerHeuristic(1, lightPdf, 1, scatteringPdf); Ld += f * Li * weight / lightPdf; } } } } // Sample BSDF with multiple importance sampling if (!IsDeltaLight(light.flags)) { Spectrum f; bool sampledSpecular = false; if (it.IsSurfaceInteraction()) { // Sample scattered direction for surface interactions BxDFType sampledType; const SurfaceInteraction &isect = (const SurfaceInteraction &)it; f = isect.bsdf->Sample_f(isect.wo, &wi, uScattering, &scatteringPdf, bsdfFlags, &sampledType); f *= AbsDot(wi, isect.shading.n); sampledSpecular = sampledType & BSDF_SPECULAR; } else { // Sample scattered direction for medium interactions const MediumInteraction &mi = (const MediumInteraction &)it; Float p = mi.phase->Sample_p(mi.wo, &wi, uScattering); f = Spectrum(p); scatteringPdf = p; } if (!f.IsBlack() && scatteringPdf > 0) { // Account for light contributions along sampled direction _wi_ Float weight = 1; if (!sampledSpecular) { lightPdf = light.Pdf_Li(it, wi); if (lightPdf == 0) return Ld; weight = PowerHeuristic(1, scatteringPdf, 1, lightPdf); } // Find intersection and compute transmittance SurfaceInteraction lightIsect; Ray ray = it.SpawnRay(wi); Spectrum Tr(1.f); bool foundSurfaceInteraction = handleMedia ? scene.IntersectTr(ray, sampler, &lightIsect, &Tr) : scene.Intersect(ray, &lightIsect); // Add light contribution from material sampling Spectrum Li(0.f); if (foundSurfaceInteraction) { if (lightIsect.primitive->GetAreaLight() == &light) Li = lightIsect.Le(-wi); } else Li = light.Le(ray); if (!Li.IsBlack()) Ld += f * Li * Tr * weight / scatteringPdf; } } return Ld; }
Spectrum PathIntegrator::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; for (bounces = 0;; ++bounces) { // Find next path vertex and accumulate contribution VLOG(2) << "Path tracer bounce " << bounces << ", current L = " << L << ", beta = " << beta; // Intersect _ray_ with scene and store intersection in _isect_ SurfaceInteraction isect; bool foundIntersection = scene.Intersect(ray, &isect); // 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); VLOG(2) << "Added Le -> L = " << L; } else { for (const auto &light : scene.infiniteLights) L += beta * light->Le(ray); VLOG(2) << "Added infinite area lights -> L = " << L; } } // 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) { VLOG(2) << "Skipping intersection due to null bsdf"; ray = isect.SpawnRay(ray.d); bounces--; continue; } const Distribution1D *distrib = lightDistribution->Lookup(isect.p); // Sample illumination from lights to find path contribution. // (But skip this for perfectly specular BSDFs.) if (isect.bsdf->NumComponents(BxDFType(BSDF_ALL & ~BSDF_SPECULAR)) > 0) { ++totalPaths; Spectrum Ld = beta * UniformSampleOneLight(isect, scene, arena, sampler, false, distrib); VLOG(2) << "Sampled direct lighting Ld = " << Ld; if (Ld.IsBlack()) ++zeroRadiancePaths; CHECK_GE(Ld.y(), 0.f); L += Ld; } // 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); VLOG(2) << "Sampled BSDF, f = " << f << ", pdf = " << pdf; if (f.IsBlack() || pdf == 0.f) break; beta *= f * AbsDot(wi, isect.shading.n) / pdf; VLOG(2) << "Updated beta = " << beta; CHECK_GE(beta.y(), 0.f); DCHECK(!std::isinf(beta.y())); specularBounce = (flags & BSDF_SPECULAR) != 0; ray = isect.SpawnRay(wi); // Account for 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())); if (S.IsBlack() || pdf == 0) break; beta *= S / pdf; // Account for the direct subsurface scattering component L += beta * UniformSampleOneLight(pi, scene, arena, sampler, false, 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())); specularBounce = (flags & BSDF_SPECULAR) != 0; ray = pi.SpawnRay(wi); } // Possibly terminate the path with Russian roulette if (beta.y() < rrThreshold && bounces > 3) { Float q = std::max((Float).05, 1 - beta.MaxComponentValue()); VLOG(2) << "RR termination probability q = " << q; if (sampler.Get1D() < q) break; beta /= 1 - q; VLOG(2) << "After RR survival, beta = " << beta; DCHECK(!std::isinf(beta.y())); } } 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; }