Spectrum IrradianceCacheIntegrator::indirectLo(const Point &p, const Normal &ng, float pixelSpacing, const Vector &wo, float rayEpsilon, BSDF *bsdf, BxDFType flags, RNG &rng, const Scene *scene, const Renderer *renderer, MemoryArena &arena) const { if (bsdf->NumComponents(flags) == 0) return Spectrum(0.); Spectrum E; Vector wi; // Get irradiance _E_ and average incident direction _wi_ at point _p_ if (!interpolateE(scene, p, ng, &E, &wi)) { // Compute irradiance at current point PBRT_IRRADIANCE_CACHE_STARTED_COMPUTING_IRRADIANCE(const_cast<Point *>(&p), const_cast<Normal *>(&ng)); uint32_t scramble[2] = { rng.RandomUInt(), rng.RandomUInt() }; float minHitDistance = INFINITY; Vector wAvg(0,0,0); Spectrum LiSum = 0.f; for (int i = 0; i < nSamples; ++i) { // Sample direction for irradiance estimate ray float u[2]; Sample02(i, scramble, u); Vector w = CosineSampleHemisphere(u[0], u[1]); RayDifferential r(p, bsdf->LocalToWorld(w), rayEpsilon); r.d = Faceforward(r.d, ng); // Trace ray to sample radiance for irradiance estimate PBRT_IRRADIANCE_CACHE_STARTED_RAY(&r); Spectrum L = pathL(r, scene, renderer, rng, arena); LiSum += L; wAvg += r.d * L.y(); minHitDistance = min(minHitDistance, r.maxt); PBRT_IRRADIANCE_CACHE_FINISHED_RAY(&r, r.maxt, &L); } E = (M_PI / float(nSamples)) * LiSum; PBRT_IRRADIANCE_CACHE_FINISHED_COMPUTING_IRRADIANCE(const_cast<Point *>(&p), const_cast<Normal *>(&ng)); // Add computed irradiance value to cache // Compute irradiance sample's contribution extent and bounding box float maxDist = maxSamplePixelSpacing * pixelSpacing; float minDist = minSamplePixelSpacing * pixelSpacing; float contribExtent = Clamp(minHitDistance / 2.f, minDist, maxDist); BBox sampleExtent(p); sampleExtent.Expand(contribExtent); PBRT_IRRADIANCE_CACHE_ADDED_NEW_SAMPLE(const_cast<Point *>(&p), const_cast<Normal *>(&ng), contribExtent, &E, &wAvg, pixelSpacing); // Allocate _IrradianceSample_, get write lock, add to octree IrradianceSample *sample = new IrradianceSample(E, p, ng, wAvg, contribExtent); RWMutexLock lock(*mutex, WRITE); octree->Add(sample, sampleExtent); wi = wAvg; } // Compute reflected radiance due to irradiance and BSDF if (wi.LengthSquared() == 0.f) return Spectrum(0.); return bsdf->f(wo, Normalize(wi), flags) * E; }
Spectrum IrradianceCache::IndirectLo(const Point &p, const Normal &n, const Vector &wo, BSDF *bsdf, BxDFType flags, const Sample *sample, const Scene *scene) const { if (bsdf->NumComponents(flags) == 0) return Spectrum(0.); Spectrum E; if (!InterpolateIrradiance(scene, p, n, &E)) { // Compute irradiance at current point u_int scramble[2] = { RandomUInt(), RandomUInt() }; float sumInvDists = 0.; for (int i = 0; i < nSamples; ++i) { // Trace ray to sample radiance for irradiance estimate // Update irradiance statistics for rays traced static StatsCounter nIrradiancePaths("Irradiance Cache", "Paths followed for irradiance estimates"); ++nIrradiancePaths; float u[2]; Sample02(i, scramble, u); Vector w = CosineSampleHemisphere(u[0], u[1]); RayDifferential r(p, bsdf->LocalToWorld(w)); if (Dot(r.d, n) < 0) r.d = -r.d; Spectrum L(0.); // Do path tracing to compute radiance along ray for estimate { // Declare common path integration variables Spectrum pathThroughput = 1.; RayDifferential ray(r); bool specularBounce = false; for (int pathLength = 0; ; ++pathLength) { // Find next vertex of path Intersection isect; if (!scene->Intersect(ray, &isect)) break; if (pathLength == 0) r.maxt = ray.maxt; pathThroughput *= scene->Transmittance(ray); // Possibly add emitted light at path vertex if (specularBounce) L += pathThroughput * isect.Le(-ray.d); // Evaluate BSDF at hit point BSDF *bsdf = isect.GetBSDF(ray); // Sample illumination from lights to find path contribution const Point &p = bsdf->dgShading.p; const Normal &n = bsdf->dgShading.nn; Vector wo = -ray.d; L += pathThroughput * UniformSampleOneLight(scene, p, n, wo, bsdf, sample); if (pathLength+1 == maxIndirectDepth) break; // Sample BSDF to get new path direction // Get random numbers for sampling new direction, \mono{bs1}, \mono{bs2}, and \mono{bcs} float bs1 = RandomFloat(), bs2 = RandomFloat(), bcs = RandomFloat(); Vector wi; float pdf; BxDFType flags; Spectrum f = bsdf->Sample_f(wo, &wi, bs1, bs2, bcs, &pdf, BSDF_ALL, &flags); if (f.Black() || pdf == 0.) break; specularBounce = (flags & BSDF_SPECULAR) != 0; pathThroughput *= f * AbsDot(wi, n) / pdf; ray = RayDifferential(p, wi); // Possibly terminate the path if (pathLength > 3) { float continueProbability = .5f; if (RandomFloat() > continueProbability) break; pathThroughput /= continueProbability; } } } E += L; float dist = r.maxt * r.d.Length(); sumInvDists += 1.f / dist; } E *= M_PI / float(nSamples); // Add computed irradiance value to cache // Update statistics for new irradiance sample static StatsCounter nSamplesComputed("Irradiance Cache", "Irradiance estimates computed"); ++nSamplesComputed; // Compute bounding box of irradiance sample's contribution region static float minMaxDist = .001f * powf(scene->WorldBound().Volume(), 1.f/3.f); static float maxMaxDist = .125f * powf(scene->WorldBound().Volume(), 1.f/3.f); float maxDist = nSamples / sumInvDists; if (minMaxDist > 0.f) maxDist = Clamp(maxDist, minMaxDist, maxMaxDist); maxDist *= maxError; BBox sampleExtent(p); sampleExtent.Expand(maxDist); octree->Add(IrradianceSample(E, p, n, maxDist), sampleExtent); } return .5f * bsdf->rho(wo, flags) * E; }