예제 #1
0
Spectrum InfiniteAreaLightIS::Sample_L(const Point &p, float u1,
		float u2, Vector *wi, float *pdf,
		VisibilityTester *visibility) const {
	// Find floating-point $(u,v)$ sample coordinates
	float pdfs[2];
	float fu = uDistrib->Sample(u1, &pdfs[0]);
	int u = Clamp(Float2Int(fu), 0, uDistrib->count-1);
	float fv = vDistribs[u]->Sample(u2, &pdfs[1]);
	if (pdfs[0] == 0.f || pdfs[1] == 0.f) {
		*pdf = 0.f;
		return Spectrum(0.f);
	}
	// Convert sample point to direction on the unit sphere
	float theta = fv * vDistribs[u]->invCount * M_PI;
	float phi = fu * uDistrib->invCount * 2.f * M_PI;
	float costheta = cos(theta), sintheta = sin(theta);
	if (sintheta == 0.f) return 0.f;
	float sinphi = sin(phi), cosphi = cos(phi);
	*wi = LightToWorld(Vector(sintheta * cosphi, sintheta * sinphi,
	                          costheta));
	// Compute PDF for sampled direction
	*pdf = (pdfs[0] * pdfs[1]) / (2. * M_PI * M_PI * sintheta);
	// Return radiance value for direction
	visibility->SetRay(p, *wi);
	return Lbase * radianceMap->Lookup(fu * uDistrib->invCount,
		fv * vDistribs[u]->invCount);
}
예제 #2
0
파일: bdpt.cpp 프로젝트: wjakob/pbrt-v3
int GenerateLightSubpath(
    const Scene &scene, Sampler &sampler, MemoryArena &arena, int maxDepth,
    Float time, const Distribution1D &lightDistr,
    const std::unordered_map<const Light *, size_t> &lightToIndex,
    Vertex *path) {
    if (maxDepth == 0) return 0;
    // Sample initial ray for light subpath
    Float lightPdf;
    int lightNum = lightDistr.SampleDiscrete(sampler.Get1D(), &lightPdf);
    const std::shared_ptr<Light> &light = scene.lights[lightNum];
    RayDifferential ray;
    Normal3f nLight;
    Float pdfPos, pdfDir;
    Spectrum Le = light->Sample_Le(sampler.Get2D(), sampler.Get2D(), time, &ray,
                                   &nLight, &pdfPos, &pdfDir);
    if (pdfPos == 0 || pdfDir == 0 || Le.IsBlack()) return 0;

    // Generate first vertex on light subpath and start random walk
    path[0] =
        Vertex::CreateLight(light.get(), ray, nLight, Le, pdfPos * lightPdf);
    Spectrum beta = Le * AbsDot(nLight, ray.d) / (lightPdf * pdfPos * pdfDir);
    VLOG(2) << "Starting light subpath. Ray: " << ray << ", Le " << Le <<
        ", beta " << beta << ", pdfPos " << pdfPos << ", pdfDir " << pdfDir;
    int nVertices =
        RandomWalk(scene, ray, sampler, arena, beta, pdfDir, maxDepth - 1,
                   TransportMode::Importance, path + 1);

    // Correct subpath sampling densities for infinite area lights
    if (path[0].IsInfiniteLight()) {
        // Set spatial density of _path[1]_ for infinite area light
        if (nVertices > 0) {
            path[1].pdfFwd = pdfPos;
            if (path[1].IsOnSurface())
                path[1].pdfFwd *= AbsDot(ray.d, path[1].ng());
        }

        // Set spatial density of _path[0]_ for infinite area light
        path[0].pdfFwd =
            InfiniteLightDensity(scene, lightDistr, lightToIndex, ray.d);
    }
    return nVertices + 1;
}
예제 #3
0
int GenerateLightSubpath(const Scene &scene, Sampler &sampler,
                         MemoryArena &arena, int maxdepth, Float time,
                         const Distribution1D &lightDistr, Vertex *path) {
    if (maxdepth == 0) return 0;
    // Sample initial ray for light subpath
    Float lightPdf;
    int lightNum = lightDistr.SampleDiscrete(sampler.Get1D(), &lightPdf);
    const std::shared_ptr<Light> &light = scene.lights[lightNum];
    RayDifferential ray;
    Normal3f Nl;
    Float pdfPos, pdfDir;
    Spectrum Le = light->Sample_L(sampler.Get2D(), sampler.Get2D(), time, &ray,
                                  &Nl, &pdfPos, &pdfDir);
    if (pdfPos == 0.f || pdfDir == 0.f || Le.IsBlack()) return 0;

    // Generate first vertex on light subpath and start random walk
    Spectrum weight = Le * AbsDot(Nl, ray.d) / (lightPdf * pdfPos * pdfDir);
    path[0] = Vertex(VertexType::Light,
                     EndpointInteraction(light.get(), ray, Nl), Le);
    path[0].pdfFwd = pdfPos * lightPdf;
    int nvertices =
        RandomWalk(scene, ray, sampler, arena, weight, pdfDir, maxdepth - 1,
                   TransportMode::Importance, path + 1);

    // Correct sampling densities for infinite area lights
    if (path[0].IsInfiniteLight()) {
        // Set positional density of _path[1]_
        if (nvertices > 0) {
            path[1].pdfFwd = pdfPos;
            if (path[1].IsOnSurface())
                path[1].pdfFwd *= AbsDot(ray.d, path[1].GetGeoNormal());
        }

        // Set positional density of _path[0]_
        path[0].pdfFwd = InfiniteLightDensity(scene, lightDistr, ray.d);
    }
    return nvertices + 1;
}
예제 #4
0
파일: igi.cpp 프로젝트: ChiahungTai/pbrt-v2
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;
}
예제 #5
0
void PhotonShootingTask::Run() {
    // Declare local variables for _PhotonShootingTask_
    MemoryArena arena;
    RNG rng(31 * taskNum);
    vector<Photon> localDirectPhotons, localIndirectPhotons, localCausticPhotons;
    vector<RadiancePhoton> localRadiancePhotons;
    uint32_t totalPaths = 0;
    bool causticDone = (integrator->nCausticPhotonsWanted == 0);
    bool indirectDone = (integrator->nIndirectPhotonsWanted == 0);
    PermutedHalton halton(6, rng);
    vector<Spectrum> localRpReflectances, localRpTransmittances;
    while (true) {
        // Follow photon paths for a block of samples
        const uint32_t blockSize = 4096;
        for (uint32_t i = 0; i < blockSize; ++i) {
            float u[6];
            halton.Sample(++totalPaths, u);
            // Choose light to shoot photon from
            float lightPdf;
            int lightNum = lightDistribution->SampleDiscrete(u[0], &lightPdf);
            const Light *light = scene->lights[lightNum];

            // Generate _photonRay_ from light source and initialize _alpha_
            RayDifferential photonRay;
            float pdf;
            LightSample ls(u[1], u[2], u[3]);
            Normal Nl;
            Spectrum Le = light->Sample_L(scene, ls, u[4], u[5],
                                          time, &photonRay, &Nl, &pdf);
            if (pdf == 0.f || Le.IsBlack()) continue;
            Spectrum alpha = (AbsDot(Nl, photonRay.d) * Le) / (pdf * lightPdf);
            if (!alpha.IsBlack()) {
                // Follow photon path through scene and record intersections
                PBRT_PHOTON_MAP_STARTED_RAY_PATH(&photonRay, &alpha);
                bool specularPath = true;
                Intersection photonIsect;
                int nIntersections = 0;
                while (scene->Intersect(photonRay, &photonIsect)) {
                    ++nIntersections;
                    // Handle photon/surface intersection
                    alpha *= renderer->Transmittance(scene, photonRay, NULL, rng, arena);
                    BSDF *photonBSDF = photonIsect.GetBSDF(photonRay, arena);
                    BxDFType specularType = BxDFType(BSDF_REFLECTION |
                                            BSDF_TRANSMISSION | BSDF_SPECULAR);
                    bool hasNonSpecular = (photonBSDF->NumComponents() >
                                           photonBSDF->NumComponents(specularType));
                    Vector wo = -photonRay.d;
                    if (hasNonSpecular) {
                        // Deposit photon at surface
                        Photon photon(photonIsect.dg.p, alpha, wo);
                        bool depositedPhoton = false;
                        if (specularPath && nIntersections > 1) {
                            if (!causticDone) {
                                PBRT_PHOTON_MAP_DEPOSITED_CAUSTIC_PHOTON(&photonIsect.dg, &alpha, &wo);
                                depositedPhoton = true;
                                localCausticPhotons.push_back(photon);
                            }
                        }
                        else {
                            // Deposit either direct or indirect photon
                            // stop depositing direct photons once indirectDone is true; don't
                            // want to waste memory storing too many if we're going a long time
                            // trying to get enough caustic photons desposited.
                            if (nIntersections == 1 && !indirectDone && integrator->finalGather) {
                                PBRT_PHOTON_MAP_DEPOSITED_DIRECT_PHOTON(&photonIsect.dg, &alpha, &wo);
                                depositedPhoton = true;
                                localDirectPhotons.push_back(photon);
                            }
                            else if (nIntersections > 1 && !indirectDone) {
                                PBRT_PHOTON_MAP_DEPOSITED_INDIRECT_PHOTON(&photonIsect.dg, &alpha, &wo);
                                depositedPhoton = true;
                                localIndirectPhotons.push_back(photon);
                            }
                        }

                        // Possibly create radiance photon at photon intersection point
                        if (depositedPhoton && integrator->finalGather &&
                                rng.RandomFloat() < .125f) {
                            Normal n = photonIsect.dg.nn;
                            n = Faceforward(n, -photonRay.d);
                            localRadiancePhotons.push_back(RadiancePhoton(photonIsect.dg.p, n));
                            Spectrum rho_r = photonBSDF->rho(rng, BSDF_ALL_REFLECTION);
                            localRpReflectances.push_back(rho_r);
                            Spectrum rho_t = photonBSDF->rho(rng, BSDF_ALL_TRANSMISSION);
                            localRpTransmittances.push_back(rho_t);
                        }
                    }
                    if (nIntersections >= integrator->maxPhotonDepth) break;

                    // Sample new photon ray direction
                    Vector wi;
                    float pdf;
                    BxDFType flags;
                    Spectrum fr = photonBSDF->Sample_f(wo, &wi, BSDFSample(rng),
                                                       &pdf, BSDF_ALL, &flags);
                    if (fr.IsBlack() || pdf == 0.f) break;
                    Spectrum anew = alpha * fr *
                        AbsDot(wi, photonBSDF->dgShading.nn) / pdf;

                    // Possibly terminate photon path with Russian roulette
                    float continueProb = min(1.f, anew.y() / alpha.y());
                    if (rng.RandomFloat() > continueProb)
                        break;
                    alpha = anew / continueProb;
                    specularPath &= ((flags & BSDF_SPECULAR) != 0);
                    
                    if (indirectDone && !specularPath) break;
                    photonRay = RayDifferential(photonIsect.dg.p, wi, photonRay,
                                                photonIsect.rayEpsilon);
                }
                PBRT_PHOTON_MAP_FINISHED_RAY_PATH(&photonRay, &alpha);
            }
            arena.FreeAll();
        }

        // Merge local photon data with data in _PhotonIntegrator_
        { MutexLock lock(mutex);

        // Give up if we're not storing enough photons
        if (abortTasks)
            return;
        if (nshot > 500000 &&
            (unsuccessful(integrator->nCausticPhotonsWanted,
                                      causticPhotons.size(), blockSize) ||
             unsuccessful(integrator->nIndirectPhotonsWanted,
                                      indirectPhotons.size(), blockSize))) {
            Error("Unable to store enough photons.  Giving up.\n");
            causticPhotons.erase(causticPhotons.begin(), causticPhotons.end());
            indirectPhotons.erase(indirectPhotons.begin(), indirectPhotons.end());
            radiancePhotons.erase(radiancePhotons.begin(), radiancePhotons.end());
            abortTasks = true;
            return;
        }
        progress.Update(localIndirectPhotons.size() + localCausticPhotons.size());
        nshot += blockSize;

        // Merge indirect photons into shared array
        if (!indirectDone) {
            integrator->nIndirectPaths += blockSize;
            for (uint32_t i = 0; i < localIndirectPhotons.size(); ++i)
                indirectPhotons.push_back(localIndirectPhotons[i]);
            localIndirectPhotons.erase(localIndirectPhotons.begin(),
                                       localIndirectPhotons.end());
            if (indirectPhotons.size() >= integrator->nIndirectPhotonsWanted)
                indirectDone = true;
            nDirectPaths += blockSize;
            for (uint32_t i = 0; i < localDirectPhotons.size(); ++i)
                directPhotons.push_back(localDirectPhotons[i]);
            localDirectPhotons.erase(localDirectPhotons.begin(),
                                     localDirectPhotons.end());
        }

        // Merge direct, caustic, and radiance photons into shared array
        if (!causticDone) {
            integrator->nCausticPaths += blockSize;
            for (uint32_t i = 0; i < localCausticPhotons.size(); ++i)
                causticPhotons.push_back(localCausticPhotons[i]);
            localCausticPhotons.erase(localCausticPhotons.begin(), localCausticPhotons.end());
            if (causticPhotons.size() >= integrator->nCausticPhotonsWanted)
                causticDone = true;
        }
        
        for (uint32_t i = 0; i < localRadiancePhotons.size(); ++i)
            radiancePhotons.push_back(localRadiancePhotons[i]);
        localRadiancePhotons.erase(localRadiancePhotons.begin(), localRadiancePhotons.end());
        for (uint32_t i = 0; i < localRpReflectances.size(); ++i)
            rpReflectances.push_back(localRpReflectances[i]);
        localRpReflectances.erase(localRpReflectances.begin(), localRpReflectances.end());
        for (uint32_t i = 0; i < localRpTransmittances.size(); ++i)
            rpTransmittances.push_back(localRpTransmittances[i]);
        localRpTransmittances.erase(localRpTransmittances.begin(), localRpTransmittances.end());
        }

        // Exit task if enough photons have been found
        if (indirectDone && causticDone)
            break;
    }
}
예제 #6
0
파일: bdpt.cpp 프로젝트: wjakob/pbrt-v3
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;
}
예제 #7
0
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;
}
예제 #8
0
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;
}