static Spectrum L(const Scene *scene, const Renderer *renderer, const Camera *camera, MemoryArena &arena, RNG &rng, int maxDepth, bool ignoreDirect, const MLTSample &sample) { // Generate camera ray from Metropolis sample RayDifferential ray; float cameraWeight = camera->GenerateRayDifferential(sample.cameraSample, &ray); Spectrum pathThroughput = cameraWeight, L = 0.; bool specularBounce = false, allSpecular = true; for (int pathLength = 0; pathLength < maxDepth; ++pathLength) { // Find next intersection in Metropolis light path Intersection isect; if (!scene->Intersect(ray, &isect)) { bool includeLe = ignoreDirect ? (specularBounce && !allSpecular) : (pathLength == 0 || specularBounce); if (includeLe) for (uint32_t i = 0; i < scene->lights.size(); ++i) L += pathThroughput * scene->lights[i]->Le(ray); break; } if (ignoreDirect ? (specularBounce && !allSpecular) : (specularBounce || pathLength == 0)) L += pathThroughput * isect.Le(-ray.d); BSDF *bsdf = isect.GetBSDF(ray, arena); const Point &p = bsdf->dgShading.p; const Normal &n = bsdf->dgShading.nn; Vector wo = -ray.d; const PathSample &ps = sample.pathSamples[pathLength]; // Sample direct illumination for Metropolis path vertex if (!ignoreDirect || pathLength > 0) { LightSample lightSample(ps.lightDir0, ps.lightDir1, ps.lightNum0); BSDFSample bsdfSample(ps.bsdfLightDir0, ps.bsdfLightDir1, ps.bsdfLightComponent); uint32_t lightNum = Floor2Int(ps.lightNum1 * scene->lights.size()); lightNum = min(lightNum, (uint32_t)(scene->lights.size()-1)); const Light *light = scene->lights[lightNum]; L += pathThroughput * EstimateDirect(scene, renderer, arena, light, p, n, wo, isect.rayEpsilon, sample.cameraSample.time, bsdf, rng, lightSample, bsdfSample); } // Sample direction for outgoing Metropolis path direction BSDFSample outgoingBSDFSample(ps.bsdfDir0, ps.bsdfDir1, ps.bsdfComponent); Vector wi; float pdf; BxDFType flags; Spectrum f = bsdf->Sample_f(wo, &wi, outgoingBSDFSample, &pdf, BSDF_ALL, &flags); if (f.IsBlack() || pdf == 0.) break; specularBounce = (flags & BSDF_SPECULAR) != 0; allSpecular &= specularBounce; pathThroughput *= f * AbsDot(wi, n) / pdf; ray = RayDifferential(p, wi, ray, isect.rayEpsilon); //pathThroughput *= renderer->Transmittance(scene, ray, NULL, rng, arena); } return L; }
Vec3f TraceBase::sampleDirect(const Primitive &light, SurfaceScatterEvent &event, const Medium *medium, int bounce, const Ray &parentRay, Vec3f *transmittance) { Vec3f result(0.0f); if (event.info->bsdf->lobes().isPureSpecular() || event.info->bsdf->lobes().isForward()) return Vec3f(0.0f); result += lightSample(light, event, medium, bounce, parentRay, transmittance); if (!light.isDirac()) result += bsdfSample(light, event, medium, bounce, parentRay); return result; }
void IGIIntegrator::Preprocess(const Scene *scene, const Camera *camera, const Renderer *renderer) { if (scene->lights.size() == 0) return; MemoryArena arena; RNG rng; // Compute samples for emitted rays from lights vector<float> lightNum(nLightPaths * nLightSets); vector<float> lightSampPos(2 * nLightPaths * nLightSets, 0.f); vector<float> lightSampComp(nLightPaths * nLightSets, 0.f); vector<float> lightSampDir(2 * nLightPaths * nLightSets, 0.f); LDShuffleScrambled1D(nLightPaths, nLightSets, &lightNum[0], rng); LDShuffleScrambled2D(nLightPaths, nLightSets, &lightSampPos[0], rng); LDShuffleScrambled1D(nLightPaths, nLightSets, &lightSampComp[0], rng); LDShuffleScrambled2D(nLightPaths, nLightSets, &lightSampDir[0], rng); // Precompute information for light sampling densities Distribution1D *lightDistribution = ComputeLightSamplingCDF(scene); for (uint32_t s = 0; s < nLightSets; ++s) { for (uint32_t i = 0; i < nLightPaths; ++i) { // Follow path _i_ from light to create virtual lights int sampOffset = s*nLightPaths + i; // Choose light source to trace virtual light path from float lightPdf; int ln = lightDistribution->SampleDiscrete(lightNum[sampOffset], &lightPdf); Light *light = scene->lights[ln]; // Sample ray leaving light source for virtual light path RayDifferential ray; float pdf; LightSample ls(lightSampPos[2*sampOffset], lightSampPos[2*sampOffset+1], lightSampComp[sampOffset]); Normal Nl; Spectrum alpha = light->Sample_L(scene, ls, lightSampDir[2*sampOffset], lightSampDir[2*sampOffset+1], camera->shutterOpen, &ray, &Nl, &pdf); if (pdf == 0.f || alpha.IsBlack()) continue; alpha /= pdf * lightPdf; Intersection isect; while (scene->Intersect(ray, &isect) && !alpha.IsBlack()) { // Create virtual light and sample new ray for path alpha *= renderer->Transmittance(scene, RayDifferential(ray), NULL, rng, arena); Vector wo = -ray.d; BSDF *bsdf = isect.GetBSDF(ray, arena); // Create virtual light at ray intersection point Spectrum contrib = alpha * bsdf->rho(wo, rng) / M_PI; virtualLights[s].push_back(VirtualLight(isect.dg.p, isect.dg.nn, contrib, isect.rayEpsilon)); // Sample new ray direction and update weight for virtual light path Vector wi; float pdf; BSDFSample bsdfSample(rng); Spectrum fr = bsdf->Sample_f(wo, &wi, bsdfSample, &pdf); if (fr.IsBlack() || pdf == 0.f) break; Spectrum contribScale = fr * AbsDot(wi, bsdf->dgShading.nn) / pdf; // Possibly terminate virtual light path with Russian roulette float rrProb = min(1.f, contribScale.y()); if (rng.RandomFloat() > rrProb) break; alpha *= contribScale / rrProb; ray = RayDifferential(isect.dg.p, wi, ray, isect.rayEpsilon); } arena.FreeAll(); } } delete lightDistribution; }
Spectrum PhotonIntegrator::Li(const Scene *scene, const Renderer *renderer, const RayDifferential &ray, const Intersection &isect, const Sample *sample, RNG &rng, MemoryArena &arena) const { Spectrum L(0.); Vector wo = -ray.d; // Compute emitted light if ray hit an area light source L += isect.Le(wo); // Evaluate BSDF at hit pbrt::Point BSDF *bsdf = isect.GetBSDF(ray, arena); const pbrt::Point &p = bsdf->dgShading.p; const Normal &n = bsdf->dgShading.nn; L += UniformSampleAllLights(scene, renderer, arena, p, n, wo, isect.rayEpsilon, ray.time, bsdf, sample, rng, lightSampleOffsets, bsdfSampleOffsets); // Compute caustic lighting for photon map integrator ClosePhoton *lookupBuf = arena.Alloc<ClosePhoton>(nLookup); L += LPhoton(causticMap, nCausticPaths, nLookup, lookupBuf, bsdf, rng, isect, wo, maxDistSquared); // Compute indirect lighting for photon map integrator if (finalGather && indirectMap != NULL) { #if 1 // Do one-bounce final gather for photon map BxDFType nonSpecular = BxDFType(BSDF_REFLECTION | BSDF_TRANSMISSION | BSDF_DIFFUSE | BSDF_GLOSSY); if (bsdf->NumComponents(nonSpecular) > 0) { // Find indirect photons around point for importance sampling const uint32_t nIndirSamplePhotons = 50; PhotonProcess proc(nIndirSamplePhotons, arena.Alloc<ClosePhoton>(nIndirSamplePhotons)); float searchDist2 = maxDistSquared; while (proc.nFound < nIndirSamplePhotons) { float md2 = searchDist2; proc.nFound = 0; indirectMap->Lookup(p, proc, md2); searchDist2 *= 2.f; } // Copy photon directions to local array Vector *photonDirs = arena.Alloc<Vector>(nIndirSamplePhotons); for (uint32_t i = 0; i < nIndirSamplePhotons; ++i) photonDirs[i] = proc.photons[i].photon->wi; // Use BSDF to do final gathering Spectrum Li = 0.; for (int i = 0; i < gatherSamples; ++i) { // Sample random direction from BSDF for final gather ray Vector wi; float pdf; BSDFSample bsdfSample(sample, bsdfGatherSampleOffsets, i); Spectrum fr = bsdf->Sample_f(wo, &wi, bsdfSample, &pdf, BxDFType(BSDF_ALL & ~BSDF_SPECULAR)); if (fr.IsBlack() || pdf == 0.f) continue; Assert(pdf >= 0.f); // Trace BSDF final gather ray and accumulate radiance RayDifferential bounceRay(p, wi, ray, isect.rayEpsilon); Intersection gatherIsect; if (scene->Intersect(bounceRay, &gatherIsect)) { // Compute exitant radiance _Lindir_ using radiance photons Spectrum Lindir = 0.f; Normal nGather = gatherIsect.dg.nn; nGather = Faceforward(nGather, -bounceRay.d); RadiancePhotonProcess proc(nGather); float md2 = INFINITY; radianceMap->Lookup(gatherIsect.dg.p, proc, md2); if (proc.photon != NULL) Lindir = proc.photon->Lo; Lindir *= renderer->Transmittance(scene, bounceRay, NULL, rng, arena); // Compute MIS weight for BSDF-sampled gather ray // Compute PDF for photon-sampling of direction _wi_ float photonPdf = 0.f; float conePdf = UniformConePdf(cosGatherAngle); for (uint32_t j = 0; j < nIndirSamplePhotons; ++j) if (Dot(photonDirs[j], wi) > .999f * cosGatherAngle) photonPdf += conePdf; photonPdf /= nIndirSamplePhotons; float wt = PowerHeuristic(gatherSamples, pdf, gatherSamples, photonPdf); Li += fr * Lindir * (AbsDot(wi, n) * wt / pdf); } } L += Li / gatherSamples; // Use nearby photons to do final gathering Li = 0.; for (int i = 0; i < gatherSamples; ++i) { // Sample random direction using photons for final gather ray BSDFSample gatherSample(sample, indirGatherSampleOffsets, i); int photonNum = min((int)nIndirSamplePhotons - 1, Floor2Int(gatherSample.uComponent * nIndirSamplePhotons)); // Sample gather ray direction from _photonNum_ Vector vx, vy; CoordinateSystem(photonDirs[photonNum], &vx, &vy); Vector wi = UniformSampleCone(gatherSample.uDir[0], gatherSample.uDir[1], cosGatherAngle, vx, vy, photonDirs[photonNum]); // Trace photon-sampled final gather ray and accumulate radiance Spectrum fr = bsdf->f(wo, wi); if (fr.IsBlack()) continue; RayDifferential bounceRay(p, wi, ray, isect.rayEpsilon); Intersection gatherIsect; PBRT_PHOTON_MAP_STARTED_GATHER_RAY(&bounceRay); if (scene->Intersect(bounceRay, &gatherIsect)) { // Compute exitant radiance _Lindir_ using radiance photons Spectrum Lindir = 0.f; Normal nGather = gatherIsect.dg.nn; nGather = Faceforward(nGather, -bounceRay.d); RadiancePhotonProcess proc(nGather); float md2 = INFINITY; radianceMap->Lookup(gatherIsect.dg.p, proc, md2); if (proc.photon != NULL) Lindir = proc.photon->Lo; Lindir *= renderer->Transmittance(scene, bounceRay, NULL, rng, arena); // Compute PDF for photon-sampling of direction _wi_ float photonPdf = 0.f; float conePdf = UniformConePdf(cosGatherAngle); for (uint32_t j = 0; j < nIndirSamplePhotons; ++j) if (Dot(photonDirs[j], wi) > .999f * cosGatherAngle) photonPdf += conePdf; photonPdf /= nIndirSamplePhotons; // Compute MIS weight for photon-sampled gather ray float bsdfPdf = bsdf->Pdf(wo, wi); float wt = PowerHeuristic(gatherSamples, photonPdf, gatherSamples, bsdfPdf); Li += fr * Lindir * AbsDot(wi, n) * wt / photonPdf; } PBRT_PHOTON_MAP_FINISHED_GATHER_RAY(&bounceRay); } L += Li / gatherSamples; } #else // for debugging / examples: use the photon map directly Normal nn = Faceforward(n, -ray.d); RadiancePhotonProcess proc(nn); float md2 = INFINITY; radianceMap->Lookup(p, proc, md2); if (proc.photon) L += proc.photon->Lo; #endif } else L += LPhoton(indirectMap, nIndirectPaths, nLookup, lookupBuf, bsdf, rng, isect, wo, maxDistSquared); if (ray.depth+1 < maxSpecularDepth) { Vector wi; // Trace rays for specular reflection and refraction L += SpecularReflect(ray, bsdf, rng, isect, renderer, scene, sample, arena); L += SpecularTransmit(ray, bsdf, rng, isect, renderer, scene, sample, arena); } return L; }
Spectrum MisPathTracer::Li(const Scene* scene, Sampler& sampler, Ray3f& ray) const { Spectrum L(0.f); Intersection its; Spectrum throughput(1.f); bool isSpecular = true; while (true) { if (!scene->rayIntersect(ray, its)) break; if (isSpecular && its.shape->getEmitter()) { EmitterSample emittanceSample(ray.o, its.p, its.shFrame.n); L += throughput * its.shape->getEmitter()->eval(emittanceSample); } if (its.shape->getBSDF()->getType() != BSDFType::Delta) { EmitterSample emSam(its.p); auto lightSpec = scene->sampleEmitter(its, sampler, emSam); float cosFactor = its.shFrame.n.dot(emSam.wi); if (!(cosFactor <= 0.f || lightSpec.isZero() || scene->rayIntersect(emSam.shadowRay))) { BSDFSample bsdfSam(its.p, its.toLocal(-ray.d), its.toLocal(emSam.wi)); bsdfSam.measure = Measure::SolidAngle; auto bsdfSpec = its.shape->getBSDF()->eval(bsdfSam); float pdfEm = emSam.pdf; float pdfBsdf = its.shape->getBSDF()->pdf(bsdfSam); L += throughput * bsdfSpec * lightSpec * cosFactor * miWeight(pdfEm, pdfBsdf); } } BSDFSample bsdfSample(its.p, its.toLocal(-ray.d)); auto bsdf = its.shape->getBSDF()->sample(bsdfSample, sampler); Intersection bsdfIts; Ray3f bsdfRay(its.p, its.toWorld(bsdfSample.wo)); if (scene->rayIntersect(bsdfRay, bsdfIts) && bsdfIts.shape->getEmitter()) { const auto* em = bsdfIts.shape->getEmitter(); EmitterSample emSam(its.p, bsdfIts.p, bsdfIts.shFrame.n); emSam.wi = bsdfRay.d; auto lightSpec = em->eval(emSam); float pdfBsdf = its.shape->getBSDF()->pdf(bsdfSample); float pdfEm = em->pdf(emSam) * scene->getEmitterPdf(); if (pdfBsdf + pdfEm > 0.f) L += throughput * bsdf * lightSpec * miWeight(pdfBsdf, pdfEm); } isSpecular = its.shape->getBSDF()->getType() == BSDFType::Delta; throughput *= bsdf; ray = bsdfRay; float q = 1.f - std::min(throughput.maxCoeff(), 0.99f); if (sampler.next1D() > q) throughput /= (1.f - q); else break; } return L; }