EmissionVertex sampleLightPath(
    BDPTPathVertex* pLightPath,
    uint32_t maxLightPathDepth,
    const Scene& scene,
    const PowerBasedLightSampler& lightSampler,
    uint32_t misPathCount, // The number of paths used to estimate an integral
    MisFunctor&& mis,
    RandomGenerator&& rng) {
    std::for_each(pLightPath, pLightPath + maxLightPathDepth,
    [&](BDPTPathVertex& vertex) {
        vertex.m_fPathPdf = 0.f;
    });

    if(maxLightPathDepth > 0u) {
        const Light* pLight = nullptr;
        float lightPdf;
        auto positionSample = getFloat2(rng);
        pLightPath[0] = BDPTPathVertex(scene, lightSampler, getFloat(rng), positionSample,
                                       getFloat2(rng), pLight, lightPdf, misPathCount, mis);
        if(pLightPath->m_fPathPdf) {
            while(pLightPath->m_nDepth < maxLightPathDepth &&
                    pLightPath->extend(*(pLightPath + 1), scene, misPathCount, true, rng, mis)) {
                ++pLightPath;
            }
            if(!pLightPath->m_Intersection) {
                pLightPath->m_fPathPdf = 0.f; // If the last vertex is outside the scene, invalidate it
            }
        }
        return { pLight, lightPdf, positionSample };
    }
    return { nullptr, 0.f, Vec2f(0.f) };
}
SensorVertex sampleEyePath(
    BDPTPathVertex* pEyePath,
    uint32_t maxEyePathDepth,
    const Scene& scene,
    const Sensor& sensor,
    uint32_t misPathCount, // The number of paths used to estimate an integral
    MisFunctor&& mis,
    RandomGenerator&& rng) {
    std::for_each(pEyePath, pEyePath + maxEyePathDepth,
    [&](BDPTPathVertex& vertex) {
        vertex.m_fPathPdf = 0.f;
    });

    if(maxEyePathDepth > 0u) {
        auto positionSample = getFloat2(rng);
        pEyePath[0] = BDPTPathVertex(scene, sensor, getFloat(rng), positionSample,
                                     getFloat2(rng), misPathCount, mis);
        if(pEyePath->m_Intersection && pEyePath->m_fPathPdf) {
            while(pEyePath->m_nDepth < maxEyePathDepth &&
                    pEyePath->extend(*(pEyePath + 1), scene, misPathCount, false, rng, mis)) {
                ++pEyePath;
            }
        }
        return { &sensor, positionSample };
    }
    return { nullptr, 0.f, Vec2f(0.f) };
}
void InstantRadiosityRenderer::processPixel(uint32_t threadID, uint32_t tileID, const Vec2u& pixel) const {
    PixelSensor pixelSensor(getSensor(), pixel, getFramebufferSize());
    RaySample raySample;
    float positionPdf, directionPdf;
    auto We = pixelSensor.sampleExitantRay(getScene(), getFloat2(threadID), getFloat2(threadID), raySample, positionPdf, directionPdf);

    auto L = zero<Vec3f>();

    if(We != zero<Vec3f>() && raySample.pdf) {
        auto I = getScene().intersect(raySample.value);
        BSDF bsdf(-raySample.value.dir, I, getScene());
        if(I) {
            // Direct illumination
            for(auto& vpl: m_EmissionVPLBuffer) {
                RaySample shadowRay;
                auto Le = vpl.pLight->sampleDirectIllumination(getScene(), vpl.emissionVertexSample, I, shadowRay);

                if(shadowRay.pdf) {
                    auto contrib = We * Le * bsdf.eval(shadowRay.value.dir) * abs(dot(I.Ns, shadowRay.value.dir)) /
                            (vpl.lightPdf * shadowRay.pdf * m_nLightPathCount * raySample.pdf);

                    if(contrib != zero<Vec3f>()) {
                        if(!getScene().occluded(shadowRay.value)) {
                            L += contrib;
                        }
                    }
                }
            }

            // Indirect illumination
            for(auto& vpl: m_SurfaceVPLBuffer) {
                auto dirToVPL = vpl.intersection.P - I.P;
                auto dist = length(dirToVPL);

                if(dist > 0.f) {
                    dirToVPL /= dist;

                    auto fs1 = bsdf.eval(dirToVPL);
                    auto fs2 = vpl.bsdf.eval(-dirToVPL);
                    auto geometricFactor = abs(dot(I.Ns, dirToVPL)) * abs(dot(vpl.intersection.Ns, -dirToVPL)) / sqr(dist);

                    auto contrib = We * vpl.power * fs1 * fs2 * geometricFactor / raySample.pdf;
                    if(contrib != zero<Vec3f>()) {
                        Ray shadowRay(I, vpl.intersection, dirToVPL, dist);
                        if(!getScene().occluded(shadowRay)) {
                            L += contrib;
                        }
                    }
                }
            }
        }
    }

    accumulate(0, getPixelIndex(pixel.x, pixel.y), Vec4f(L, 1.f));
}
Пример #4
0
	float2 Random::getFloat2(float maxRadius)
	{
		float2 result;
		do {
			result = getFloat2(-float2::ONE, float2::ONE);
		} while (lengthSquared(result) >= 1.0f);

		return result * maxRadius;
	}
void InstantRadiosityRenderer::beginFrame() {
    auto lightPathDepth = m_nMaxDepth - 2;

    m_EmissionVPLBuffer.clear();
    m_EmissionVPLBuffer.reserve(m_nLightPathCount);

    m_SurfaceVPLBuffer.clear();
    m_SurfaceVPLBuffer.reserve(m_nLightPathCount * lightPathDepth);

    for(auto i: range(m_nLightPathCount)) {
        auto power = Vec3f(1.f / m_nLightPathCount);
        EmissionVPL vpl;
        vpl.pLight = m_Sampler.sample(getScene(), getFloat(0u), vpl.lightPdf);
        if(vpl.pLight && vpl.lightPdf) {
            float emissionVertexPdf;
            RaySample exitantRaySample;
            vpl.emissionVertexSample = getFloat2(0u);
            auto Le = vpl.pLight->sampleExitantRay(getScene(), vpl.emissionVertexSample, getFloat2(0u), exitantRaySample, emissionVertexPdf);

            m_EmissionVPLBuffer.emplace_back(vpl);

            if(Le != zero<Vec3f>() && exitantRaySample.pdf) {
                auto intersection = getScene().intersect(exitantRaySample.value);
                if(intersection) {
                    power *= Le / (vpl.lightPdf * exitantRaySample.pdf);
                    m_SurfaceVPLBuffer.emplace_back(intersection, -exitantRaySample.value.dir, getScene(), power);

                    for(auto depth = 1u; depth < lightPathDepth; ++depth) {
                        Sample3f outgoingDir;
                        float cosThetaOutDir;
                        auto fs = m_SurfaceVPLBuffer.back().bsdf.sample(getFloat3(0u), outgoingDir, cosThetaOutDir,
                                                                 nullptr, true);

                        if(fs != zero<Vec3f>() && outgoingDir.pdf) {
                            Ray ray(m_SurfaceVPLBuffer.back().intersection, outgoingDir.value);
                            auto intersection = getScene().intersect(ray);
                            if(intersection) {
                                power *= fs * abs(cosThetaOutDir) / outgoingDir.pdf;
                                m_SurfaceVPLBuffer.emplace_back(intersection, -ray.dir, getScene(), power);
                            }
                        }
                    }
                }
            }
        }
    }
}
Пример #6
0
    bool extend(const Scene& scene, const RandomGenerator& rng) {
        if(!m_Intersection) {
            // Cannot extend from infinity
            invalidate();
            return false;
        }

        Sample3f woSample;
        float cosThetaOutDir;
        uint32_t sampledEvent;
        auto fs = m_BSDF.sample(Vec3f(getFloat(rng), getFloat2(rng)), woSample, cosThetaOutDir, &sampledEvent, m_bSampleAdjoint);

        if(woSample.pdf == 0.f || fs == zero<Vec3f>()) {
            invalidate();
            return false;
        }

        m_nSampledEvent = sampledEvent;
        m_Power *= abs(cosThetaOutDir) * fs / woSample.pdf;
        ++m_nLength;

        auto nextI = scene.intersect(Ray(m_Intersection, woSample.value));

        if(!nextI) {
            m_Intersection = nextI;
            return true;
        }

        auto incidentDirection = -woSample.value;
        auto sqrLength = sqr(nextI.distance);
        if(sqrLength == 0.f) {
            invalidate();
            return false;
        }
        auto pdfWrtArea = woSample.pdf  * abs(dot(nextI.Ns, incidentDirection)) / sqrLength;

        if(pdfWrtArea == 0.f) {
            invalidate();
            return false;
        }

        m_Intersection = nextI;
        m_BSDF.init(incidentDirection, nextI, scene);

        return true;
    }
    void processSample(uint32_t threadID, uint32_t pixelID, uint32_t sampleID,
                       uint32_t x, uint32_t y) const {
        PixelSensor sensor(getSensor(), Vec2u(x, y), getFramebufferSize());

        auto pixelSample = getPixelSample(threadID, sampleID);
        auto lensSample = getFloat2(threadID);

        RaySample raySample;
        Intersection I;

        sampleExitantRay(
                    sensor,
                    getScene(),
                    lensSample,
                    pixelSample,
                    raySample,
                    I);

        auto ray = raySample.value;

        accumulate(0, pixelID, Vec4f(ray.dir, 1.f));
    }
Пример #8
0
	float2 Random::getFloat2(float minRadius, float maxRadius)
	{
		float2 v = getFloat2(1.0f);
		float ratio = minRadius / maxRadius;
		return lerp(v, normalize(v), ratio) * maxRadius;
	}
    bool extend(BDPTPathVertex& next,
                const Scene& scene,
                uint32_t pathCount,
                bool sampleAdjoint, // Number of paths sampled with the strategies s/t = m_nDepth + 1, t/s = *
                RandomGenerator&& rng,
                MisFunctor&& mis) {
        if(!m_Intersection) {
            return false; // Can't sample from infinity
        }

        Sample3f woSample;
        float cosThetaOutDir;
        uint32_t sampledEvent;
        auto fs = m_BSDF.sample(Vec3f(getFloat(rng), getFloat2(rng)), woSample, cosThetaOutDir, &sampledEvent, sampleAdjoint);

        if(woSample.pdf == 0.f || fs == zero<Vec3f>()) {
            return false;
        }

        float reversePdf = m_BSDF.pdf(woSample.value, true);

        auto nextI = scene.intersect(Ray(m_Intersection, woSample.value));

        if(!nextI) {
            next.m_Intersection = nextI;
            next.m_BSDF.init(-woSample.value, scene);
            next.m_fPdfWrtArea = woSample.pdf;
            next.m_fPathPdf = m_fPathPdf * woSample.pdf;
            next.m_Power = m_Power * abs(cosThetaOutDir) * fs / woSample.pdf;
            next.m_nDepth = m_nDepth + 1;

            auto previousPointToIncidentDirectionJacobian = abs(cosThetaOutDir); // No division by squared distance since the point is at infinity
            if((sampledEvent & ScatteringEvent::Specular) && m_BSDF.isDelta()) {
                next.m_fdVC = mis(previousPointToIncidentDirectionJacobian) * m_fdVC;
                next.m_fdVCM = 0.f;
            } else {
                // We set dVC first in case next == *this, so that the dVCM is unchanged until next line
                next.m_fdVC = mis(previousPointToIncidentDirectionJacobian / next.m_fPdfWrtArea) * (m_fdVCM + mis(reversePdf) * m_fdVC);
                next.m_fdVCM = mis(pathCount / next.m_fPdfWrtArea);
            }
        } else {
            auto incidentDirection = -woSample.value;
            auto sqrLength = sqr(nextI.distance);
            if(sqrLength == 0.f) {
                return false;
            }

            auto pdfWrtArea = woSample.pdf * abs(dot(nextI.Ns, incidentDirection)) / sqrLength;

            if(pdfWrtArea == 0.f) {
                return false;
            }

            next.m_Intersection = nextI;
            next.m_BSDF.init(incidentDirection, nextI, scene);
            next.m_fPdfWrtArea = pdfWrtArea;
            next.m_fPathPdf = m_fPathPdf * pdfWrtArea;
            next.m_Power = m_Power * abs(cosThetaOutDir) * fs / woSample.pdf;

            next.m_nDepth = m_nDepth + 1;

            auto previousPointToIncidentDirectionJacobian = abs(cosThetaOutDir) / sqrLength;
            if((sampledEvent & ScatteringEvent::Specular) && m_BSDF.isDelta()) {
                next.m_fdVC = mis(previousPointToIncidentDirectionJacobian) * m_fdVC;
                next.m_fdVCM = 0.f;
            } else {
                // We set dVC first in case next == *this, so that the dVCM is unchanged until next line
                next.m_fdVC = mis(previousPointToIncidentDirectionJacobian / next.m_fPdfWrtArea) * (m_fdVCM + mis(reversePdf) * m_fdVC);
                next.m_fdVCM = mis(pathCount / next.m_fPdfWrtArea);
            }
        }

        return true;
    }
void UniformResamplingRecursiveMISBPTRenderer::processSample(uint32_t threadID, uint32_t tileID, uint32_t pixelID, uint32_t sampleID,
                       uint32_t x, uint32_t y) const {
    auto mis = [&](float v) {
        return Mis(v);
    };
    ThreadRNG rng(*this, threadID);

    PixelSensor pixelSensor(getSensor(), Vec2u(x, y), getFramebufferSize());

    auto pixelSample = getPixelSample(threadID, sampleID);
    auto sensorSample = getFloat2(threadID);

    auto fImportanceScale = 1.f / getSppCount();
    BDPTPathVertex eyeVertex(getScene(), pixelSensor, sensorSample, pixelSample, m_nLightPathCount, mis);

    if(eyeVertex.m_Power == zero<Vec3f>() || !eyeVertex.m_fPathPdf) {
        return;
    }

    // Sample the light path to connect with the eye path
    auto lightPathIdx = clamp(std::size_t(getFloat(threadID) * m_nLightPathCount),
                              std::size_t(0),
                              m_nLightPathCount - 1);
    auto pLightPath = m_LightPathBuffer.getSlicePtr(lightPathIdx);

    auto maxEyePathDepth = getMaxEyePathDepth();
    auto maxLightPathDepth = getMaxLightPathDepth();


    auto extendEyePath = [&]() -> bool {
        return eyeVertex.m_nDepth < maxEyePathDepth &&
            eyeVertex.extend(eyeVertex, getScene(), getSppCount(), false, rng, mis);
    };

    // Iterate over an eye path
    do {
        // Intersection with light source
        auto totalLength = eyeVertex.m_nDepth;
        if(acceptPathDepth(totalLength) && totalLength <= m_nMaxDepth) {
            auto contrib = fImportanceScale * computeEmittedRadiance(eyeVertex, getScene(), m_LightSampler, getSppCount(), mis);

            if(isInvalidMeasurementEstimate(contrib)) {
                reportInvalidContrib(threadID, tileID, pixelID, [&]() {
                    debugLog() << "s = " << 0 << ", t = " << (eyeVertex.m_nDepth + 1) << std::endl;
                });
            }

            accumulate(FINAL_RENDER, pixelID, Vec4f(contrib, 0));
            accumulate(FINAL_RENDER_DEPTH1 + totalLength - 1u, pixelID, Vec4f(contrib, 0));

            auto strategyOffset = computeBPTStrategyOffset(totalLength + 1, 0u);
            accumulate(BPT_STRATEGY_s0_t2 + strategyOffset, pixelID, Vec4f(contrib, 0));
        }

        // Connections
        if(eyeVertex.m_Intersection) {
            // Direct illumination
            auto totalLength = eyeVertex.m_nDepth + 1;
            if(acceptPathDepth(totalLength) && totalLength <= m_nMaxDepth) {
                float lightPdf;
                auto pLight = m_LightSampler.sample(getScene(), getFloat(threadID), lightPdf);
                auto s2D = getFloat2(threadID);
                auto contrib = fImportanceScale * connectVertices(eyeVertex, EmissionVertex(pLight, lightPdf, s2D), getScene(), getSppCount(), mis);

                if(isInvalidMeasurementEstimate(contrib)) {
                    reportInvalidContrib(threadID, tileID, pixelID, [&]() {
                        debugLog() << "s = " << 1 << ", t = " << (eyeVertex.m_nDepth + 1) << std::endl;
                    });
                }

                accumulate(FINAL_RENDER, pixelID, Vec4f(contrib, 0));
                accumulate(FINAL_RENDER_DEPTH1 + totalLength - 1u, pixelID, Vec4f(contrib, 0));

                auto strategyOffset = computeBPTStrategyOffset(totalLength + 1, 1);
                accumulate(BPT_STRATEGY_s0_t2 + strategyOffset, pixelID, Vec4f(contrib, 0));
            }

            // Connection with each light vertex
            for(auto j = 0u; j < maxLightPathDepth; ++j) {
                auto pLightVertex = pLightPath + j;
                auto totalLength = eyeVertex.m_nDepth + pLightVertex->m_nDepth + 1;

                if(pLightVertex->m_fPathPdf > 0.f && acceptPathDepth(totalLength) && totalLength <= m_nMaxDepth) {
                    auto contrib = fImportanceScale * connectVertices(eyeVertex, *pLightVertex, getScene(), getSppCount(), mis);

                    if(isInvalidMeasurementEstimate(contrib)) {
                        reportInvalidContrib(threadID, tileID, pixelID, [&]() {
                            debugLog() << "s = " << (pLightVertex->m_nDepth + 1) << ", t = " << (eyeVertex.m_nDepth + 1) << std::endl;
                        });
                    }

                    accumulate(FINAL_RENDER, pixelID, Vec4f(contrib, 0));
                    accumulate(FINAL_RENDER_DEPTH1 + totalLength - 1u, pixelID, Vec4f(contrib, 0));

                    auto strategyOffset = computeBPTStrategyOffset(totalLength + 1, pLightVertex->m_nDepth + 1);
                    accumulate(BPT_STRATEGY_s0_t2 + strategyOffset, pixelID, Vec4f(contrib, 0));
                }
            }
        }
    } while(extendEyePath());
}