Spectrum ConnectBDPT(const Scene &scene, Vertex *lightVertices, Vertex *cameraVertices, int s, int t, const Distribution1D &lightDistr, const Camera &camera, Sampler &sampler, Point2f *pRaster, Float *misWeightPtr) { Spectrum L(0.f); // Ignore invalid connections related to infinite area lights if (t > 1 && s != 0 && cameraVertices[t - 1].type == VertexType::Light) return Spectrum(0.f); // Perform connection and write contribution to _L_ Vertex sampled; if (s == 0) { // Interpret the camera subpath as a complete path const Vertex &pt = cameraVertices[t - 1]; if (pt.IsLight()) L = pt.Le(scene, cameraVertices[t - 2]) * pt.beta; } else if (t == 1) { // Sample a point on the camera and connect it to the light subpath const Vertex &qs = lightVertices[s - 1]; if (qs.IsConnectible()) { VisibilityTester vis; Vector3f wi; Float pdf; Spectrum Wi = camera.Sample_Wi(qs.GetInteraction(), sampler.Get2D(), &wi, &pdf, pRaster, &vis); if (pdf > 0 && !Wi.IsBlack()) { // Initialize dynamically sampled vertex and _L_ for $t=1$ case sampled = Vertex::CreateCamera(&camera, vis.P1(), Wi / pdf); L = qs.beta * qs.f(sampled) * vis.Tr(scene, sampler) * sampled.beta; if (qs.IsOnSurface()) L *= AbsDot(wi, qs.ns()); } } } else if (s == 1) { // Sample a point on a light and connect it to the camera subpath const Vertex &pt = cameraVertices[t - 1]; if (pt.IsConnectible()) { Float lightPdf; VisibilityTester vis; Vector3f wi; Float pdf; int lightNum = lightDistr.SampleDiscrete(sampler.Get1D(), &lightPdf); const std::shared_ptr<Light> &light = scene.lights[lightNum]; Spectrum lightWeight = light->Sample_Li( pt.GetInteraction(), sampler.Get2D(), &wi, &pdf, &vis); if (pdf > 0 && !lightWeight.IsBlack()) { EndpointInteraction ei(vis.P1(), light.get()); sampled = Vertex::CreateLight(ei, lightWeight / (pdf * lightPdf), 0); sampled.pdfFwd = sampled.PdfLightOrigin(scene, pt, lightDistr); L = pt.beta * pt.f(sampled) * vis.Tr(scene, sampler) * sampled.beta; if (pt.IsOnSurface()) L *= AbsDot(wi, pt.ns()); } } } else { // Handle all other bidirectional connection cases const Vertex &qs = lightVertices[s - 1], &pt = cameraVertices[t - 1]; if (qs.IsConnectible() && pt.IsConnectible()) { L = qs.beta * qs.f(pt) * pt.f(qs) * pt.beta; if (!L.IsBlack()) L *= G(scene, sampler, qs, pt); } } // Compute MIS weight for connection strategy Float misWeight = L.IsBlack() ? 0.f : MISWeight(scene, lightVertices, cameraVertices, sampled, s, t, lightDistr); L *= misWeight; if (misWeightPtr) *misWeightPtr = misWeight; return L; }
Spectrum ConnectBDPT( const Scene &scene, Vertex *lightVertices, Vertex *cameraVertices, int s, int t, const Distribution1D &lightDistr, const std::unordered_map<const Light *, size_t> &lightToIndex, const Camera &camera, Sampler &sampler, Point2f *pRaster, Float *misWeightPtr) { Spectrum L(0.f); // Ignore invalid connections related to infinite area lights if (t > 1 && s != 0 && cameraVertices[t - 1].type == VertexType::Light) return Spectrum(0.f); // Perform connection and write contribution to _L_ Vertex sampled; if (s == 0) { // Interpret the camera subpath as a complete path const Vertex &pt = cameraVertices[t - 1]; if (pt.IsLight()) L = pt.Le(scene, cameraVertices[t - 2]) * pt.beta; DCHECK(!L.HasNaNs()); } else if (t == 1) { // Sample a point on the camera and connect it to the light subpath const Vertex &qs = lightVertices[s - 1]; if (qs.IsConnectible()) { VisibilityTester vis; Vector3f wi; Float pdf; Spectrum Wi = camera.Sample_Wi(qs.GetInteraction(), sampler.Get2D(), &wi, &pdf, pRaster, &vis); if (pdf > 0 && !Wi.IsBlack()) { // Initialize dynamically sampled vertex and _L_ for $t=1$ case sampled = Vertex::CreateCamera(&camera, vis.P1(), Wi / pdf); L = qs.beta * qs.f(sampled, TransportMode::Importance) * sampled.beta; if (qs.IsOnSurface()) L *= AbsDot(wi, qs.ns()); DCHECK(!L.HasNaNs()); // Only check visibility after we know that the path would // make a non-zero contribution. if (!L.IsBlack()) L *= vis.Tr(scene, sampler); } } } else if (s == 1) { // Sample a point on a light and connect it to the camera subpath const Vertex &pt = cameraVertices[t - 1]; if (pt.IsConnectible()) { Float lightPdf; VisibilityTester vis; Vector3f wi; Float pdf; int lightNum = lightDistr.SampleDiscrete(sampler.Get1D(), &lightPdf); const std::shared_ptr<Light> &light = scene.lights[lightNum]; Spectrum lightWeight = light->Sample_Li( pt.GetInteraction(), sampler.Get2D(), &wi, &pdf, &vis); if (pdf > 0 && !lightWeight.IsBlack()) { EndpointInteraction ei(vis.P1(), light.get()); sampled = Vertex::CreateLight(ei, lightWeight / (pdf * lightPdf), 0); sampled.pdfFwd = sampled.PdfLightOrigin(scene, pt, lightDistr, lightToIndex); L = pt.beta * pt.f(sampled, TransportMode::Radiance) * sampled.beta; if (pt.IsOnSurface()) L *= AbsDot(wi, pt.ns()); // Only check visibility if the path would carry radiance. if (!L.IsBlack()) L *= vis.Tr(scene, sampler); } } } else { // Handle all other bidirectional connection cases const Vertex &qs = lightVertices[s - 1], &pt = cameraVertices[t - 1]; if (qs.IsConnectible() && pt.IsConnectible()) { L = qs.beta * qs.f(pt, TransportMode::Importance) * pt.f(qs, TransportMode::Radiance) * pt.beta; VLOG(2) << "General connect s: " << s << ", t: " << t << " qs: " << qs << ", pt: " << pt << ", qs.f(pt): " << qs.f(pt, TransportMode::Importance) << ", pt.f(qs): " << pt.f(qs, TransportMode::Radiance) << ", G: " << G(scene, sampler, qs, pt) << ", dist^2: " << DistanceSquared(qs.p(), pt.p()); if (!L.IsBlack()) L *= G(scene, sampler, qs, pt); } } ++totalPaths; if (L.IsBlack()) ++zeroRadiancePaths; ReportValue(pathLength, s + t - 2); // Compute MIS weight for connection strategy Float misWeight = L.IsBlack() ? 0.f : MISWeight(scene, lightVertices, cameraVertices, sampled, s, t, lightDistr, lightToIndex); VLOG(2) << "MIS weight for (s,t) = (" << s << ", " << t << ") connection: " << misWeight; DCHECK(!std::isnan(misWeight)); L *= misWeight; if (misWeightPtr) *misWeightPtr = misWeight; return L; }
Spectrum ConnectBDPT(const Scene &scene, Vertex *lightSubpath, Vertex *cameraSubpath, int s, int t, const Distribution1D &lightDistr, const Camera &camera, Sampler &sampler, Point2f *rasterPos, Float *misWeight) { Spectrum weight(0.f); // Ignore invalid connections related to infinite area lights if (t > 1 && s != 0 && cameraSubpath[t - 1].type == VertexType::Light) return Spectrum(0.f); // Perform connection and write contribution to _weight_ Vertex sampled; if (s == 0) { // Interpret the camera subpath as a complete path const Vertex &pt = cameraSubpath[t - 1]; if (pt.IsLight()) { const Vertex &ptMinus = cameraSubpath[t - 2]; weight = pt.Le(scene, ptMinus) * pt.weight; } } else if (t == 1) { // Sample a point on the camera and connect it to the light subpath const Vertex &qs = lightSubpath[s - 1]; if (qs.IsConnectible()) { VisibilityTester vis; Vector3f wi; Float pdf; Spectrum cameraWeight = camera.Sample_We(qs.GetInteraction(), sampler.Get2D(), &wi, &pdf, rasterPos, &vis); if (pdf > 0 && !cameraWeight.IsBlack()) { // Initialize dynamically sampled vertex and _weight_ sampled = Vertex(VertexType::Camera, EndpointInteraction(vis.P1(), &camera), cameraWeight); weight = qs.weight * qs.f(sampled) * vis.T(scene, sampler) * sampled.weight; if (qs.IsOnSurface()) weight *= AbsDot(wi, qs.GetShadingNormal()); } } } else if (s == 1) { // Sample a point on a light and connect it to the camera subpath const Vertex &pt = cameraSubpath[t - 1]; if (pt.IsConnectible()) { Float lightPdf; VisibilityTester vis; Vector3f wi; Float pdf; int lightNum = lightDistr.SampleDiscrete(sampler.Get1D(), &lightPdf); const std::shared_ptr<Light> &light = scene.lights[lightNum]; Spectrum lightWeight = light->Sample_L( pt.GetInteraction(), sampler.Get2D(), &wi, &pdf, &vis); if (pdf > 0 && !lightWeight.IsBlack()) { sampled = Vertex(VertexType::Light, EndpointInteraction(vis.P1(), light.get()), lightWeight / (pdf * lightPdf)); sampled.pdfFwd = sampled.PdfLightOrigin(scene, pt, lightDistr); weight = pt.weight * pt.f(sampled) * vis.T(scene, sampler) * sampled.weight; if (pt.IsOnSurface()) weight *= AbsDot(wi, pt.GetShadingNormal()); } } } else { // Handle all other cases const Vertex &qs = lightSubpath[s - 1], &pt = cameraSubpath[t - 1]; if (qs.IsConnectible() && pt.IsConnectible()) { weight = qs.weight * qs.f(pt) * GeometryTerm(scene, sampler, qs, pt) * pt.f(qs) * pt.weight; } } // Compute MIS weight for connection strategy *misWeight = weight.IsBlack() ? 0.f : MISWeight(scene, lightSubpath, cameraSubpath, sampled, s, t, lightDistr); return weight; }