bool BidirPathTracing::sampleScattering(BSDF& bsdf , 
	const Vector3& hitPos , BidirPathState& pathState)
{
	Real bsdfDirPdf , cosWo;
	int sampledBSDFType;
	Color3 bsdfFactor = bsdf.sample(scene , rng.randVector3() ,
		pathState.dir , bsdfDirPdf , cosWo , &sampledBSDFType);

	if (bsdfFactor.isBlack())
		return 0;

	Real bsdfRevPdf = bsdfDirPdf;
	if ((sampledBSDFType & BSDF_SPECULAR) == 0)
		bsdfRevPdf = bsdf.pdf(scene , pathState.dir , 1);

	Real contProb = bsdf.continueProb;
	if (rng.randFloat() > contProb)
		return 0;

	bsdfDirPdf *= contProb;
	bsdfRevPdf *= contProb;

	// Partial sub-path MIS quantities
	// the evaluation is completed when the actual hit point is known!
	// i.e. after tracing the ray, out of the procedure
	if (sampledBSDFType & BSDF_SPECULAR)
	{
		pathState.specularVertexNum++;

		pathState.dVCM = 0.f;
		pathState.dVC *= mis(cosWo);
	}
	else
	{
		pathState.specularPath &= 0;

		pathState.dVC = mis(1.f / bsdfDirPdf) * (pathState.dVCM +
			pathState.dVC * mis(bsdfRevPdf));
		pathState.dVCM = mis(1.f / bsdfDirPdf);
	}

	pathState.origin = hitPos;
	pathState.throughput = (pathState.throughput | bsdfFactor) *
		(cosWo / bsdfDirPdf);

	return 1;
}
Exemplo n.º 2
0
void compute_ibl_bsdf_sampling(
    SamplingContext&        sampling_context,
    const ShadingContext&   shading_context,
    const EnvironmentEDF&   environment_edf,
    const ShadingPoint&     shading_point,
    const Vector3d&         outgoing,
    const BSDF&             bsdf,
    const void*             bsdf_data,
    const int               bsdf_sampling_modes,
    const size_t            bsdf_sample_count,
    const size_t            env_sample_count,
    Spectrum&               radiance)
{
    assert(is_normalized(outgoing));

    const Vector3d& geometric_normal = shading_point.get_geometric_normal();
    const Basis3d& shading_basis = shading_point.get_shading_basis();

    radiance.set(0.0f);

    for (size_t i = 0; i < bsdf_sample_count; ++i)
    {
        // Sample the BSDF.
        // todo: rendering will be incorrect if the BSDF value returned by the sample() method
        // includes the contribution of a specular component since these are explicitly rejected
        // afterward. We need a mechanism to indicate that we want the contribution of some of
        // the components only.
        Vector3d incoming;
        Spectrum bsdf_value;
        double bsdf_prob;
        const BSDF::Mode bsdf_mode =
            bsdf.sample(
                sampling_context,
                bsdf_data,
                false,              // not adjoint
                true,               // multiply by |cos(incoming, normal)|
                geometric_normal,
                shading_basis,
                outgoing,
                incoming,
                bsdf_value,
                bsdf_prob);

        // Filter scattering modes.
        if (!(bsdf_sampling_modes & bsdf_mode))
            return;

        // Discard occluded samples.
        const double transmission =
            shading_context.get_tracer().trace(
                shading_point,
                incoming,
                ShadingRay::ShadowRay);
        if (transmission == 0.0)
            continue;

        // Evaluate the environment's EDF.
        InputEvaluator input_evaluator(shading_context.get_texture_cache());
        Spectrum env_value;
        double env_prob;
        environment_edf.evaluate(
            input_evaluator,
            incoming,
            env_value,
            env_prob);

        // Apply all weights, including MIS weight.
        if (bsdf_mode == BSDF::Specular)
            env_value *= static_cast<float>(transmission);
        else
        {
            const double mis_weight =
                mis_power2(
                    bsdf_sample_count * bsdf_prob,
                    env_sample_count * env_prob);
            env_value *= static_cast<float>(transmission / bsdf_prob * mis_weight);
        }

        // Add the contribution of this sample to the illumination.
        env_value *= bsdf_value;
        radiance += env_value;
    }

    if (bsdf_sample_count > 1)
        radiance /= static_cast<float>(bsdf_sample_count);
}
    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;
    }
Exemplo n.º 4
0
    SampledSpectrum PathTracingRenderer::Job::contribution(const Scene &scene, const WavelengthSamples &initWLs, const Ray &initRay, IndependentLightPathSampler &pathSampler, ArenaAllocator &mem) const {
        WavelengthSamples wls = initWLs;
        Ray ray = initRay;
        SurfacePoint surfPt;
        SampledSpectrum alpha = SampledSpectrum::One;
        float initY = alpha.importance(wls.selectedLambda);
        SampledSpectrumSum sp(SampledSpectrum::Zero);
        uint32_t pathLength = 0;
        
        Intersection isect;
        if (!scene.intersect(ray, &isect))
            return SampledSpectrum::Zero;
        isect.getSurfacePoint(&surfPt);
        
        Vector3D dirOut_sn = surfPt.shadingFrame.toLocal(-ray.dir);
        if (surfPt.isEmitting()) {
            EDF* edf = surfPt.createEDF(wls, mem);
            SampledSpectrum Le = surfPt.emittance(wls) * edf->evaluate(EDFQuery(), dirOut_sn);
            sp += alpha * Le;
        }
        if (surfPt.atInfinity)
            return sp;

        while (true) {
            ++pathLength;
            if (pathLength >= 100)
                break;
            Normal3D gNorm_sn = surfPt.shadingFrame.toLocal(surfPt.gNormal);
            BSDF* bsdf = surfPt.createBSDF(wls, mem);
            BSDFQuery fsQuery(dirOut_sn, gNorm_sn, wls.selectedLambda);
            
            // Next Event Estimation (explicit light sampling)
            if (bsdf->hasNonDelta()) {
                float lightProb;
                Light light;
                scene.selectLight(pathSampler.getLightSelectionSample(), &light, &lightProb);
                SLRAssert(!std::isnan(lightProb) && !std::isinf(lightProb), "lightProb: unexpected value detected: %f", lightProb);
                
                LightPosQuery lpQuery(ray.time, wls);
                LightPosQueryResult lpResult;
                SampledSpectrum M = light.sample(lpQuery, pathSampler.getLightPosSample(), &lpResult);
                SLRAssert(!std::isnan(lpResult.areaPDF)/* && !std::isinf(xpResult.areaPDF)*/, "areaPDF: unexpected value detected: %f", lpResult.areaPDF);
                
                if (scene.testVisibility(surfPt, lpResult.surfPt, ray.time)) {
                    float dist2;
                    Vector3D shadowDir = lpResult.surfPt.getDirectionFrom(surfPt.p, &dist2);
                    Vector3D shadowDir_l = lpResult.surfPt.shadingFrame.toLocal(-shadowDir);
                    Vector3D shadowDir_sn = surfPt.shadingFrame.toLocal(shadowDir);
                    
                    EDF* edf = lpResult.surfPt.createEDF(wls, mem);
                    SampledSpectrum Le = M * edf->evaluate(EDFQuery(), shadowDir_l);
                    float lightPDF = lightProb * lpResult.areaPDF;
                    SLRAssert(!Le.hasNaN() && !Le.hasInf(), "Le: unexpected value detected: %s", Le.toString().c_str());
                    
                    SampledSpectrum fs = bsdf->evaluate(fsQuery, shadowDir_sn);
                    float cosLight = absDot(-shadowDir, lpResult.surfPt.gNormal);
                    float bsdfPDF = bsdf->evaluatePDF(fsQuery, shadowDir_sn) * cosLight / dist2;
                    
                    float MISWeight = 1.0f;
                    if (!lpResult.posType.isDelta() && !std::isinf(lpResult.areaPDF))
                        MISWeight = (lightPDF * lightPDF) / (lightPDF * lightPDF + bsdfPDF * bsdfPDF);
                    SLRAssert(MISWeight <= 1.0f, "Invalid MIS weight: %g", MISWeight);
                    
                    float G = absDot(shadowDir_sn, gNorm_sn) * cosLight / dist2;
                    sp += alpha * Le * fs * (G * MISWeight / lightPDF);
                    SLRAssert(!std::isnan(G) && !std::isinf(G), "G: unexpected value detected: %f", G);
                }
            }
            
            // get a next direction by sampling BSDF.
            BSDFQueryResult fsResult;
            SampledSpectrum fs = bsdf->sample(fsQuery, pathSampler.getBSDFSample(), &fsResult);
            if (fs == SampledSpectrum::Zero || fsResult.dirPDF == 0.0f)
                break;
            if (fsResult.dirType.isDispersive()) {
                fsResult.dirPDF /= WavelengthSamples::NumComponents;
                wls.flags |= WavelengthSamples::LambdaIsSelected;
            }
            alpha *= fs * absDot(fsResult.dir_sn, gNorm_sn) / fsResult.dirPDF;
            SLRAssert(!alpha.hasInf() && !alpha.hasNaN(),
                      "alpha: %s\nlength: %u, cos: %g, dirPDF: %g",
                      alpha.toString().c_str(), pathLength, absDot(fsResult.dir_sn, gNorm_sn), fsResult.dirPDF);
            
            Vector3D dirIn = surfPt.shadingFrame.fromLocal(fsResult.dir_sn);
            ray = Ray(surfPt.p, dirIn, ray.time, Ray::Epsilon);
            
            // find a next intersection point.
            isect = Intersection();
            if (!scene.intersect(ray, &isect))
                break;
            isect.getSurfacePoint(&surfPt);
            
            dirOut_sn = surfPt.shadingFrame.toLocal(-ray.dir);
            
            // implicit light sampling
            if (surfPt.isEmitting()) {
                float bsdfPDF = fsResult.dirPDF;
                
                EDF* edf = surfPt.createEDF(wls, mem);
                SampledSpectrum Le = surfPt.emittance(wls) * edf->evaluate(EDFQuery(), dirOut_sn);
                float lightProb = scene.evaluateProb(Light(isect.obj));
                float dist2 = surfPt.getSquaredDistance(ray.org);
                float lightPDF = lightProb * surfPt.evaluateAreaPDF() * dist2 / absDot(ray.dir, surfPt.gNormal);
                SLRAssert(!Le.hasNaN() && !Le.hasInf(), "Le: unexpected value detected: %s", Le.toString().c_str());
                SLRAssert(!std::isnan(lightPDF)/* && !std::isinf(lightPDF)*/, "lightPDF: unexpected value detected: %f", lightPDF);
                
                float MISWeight = 1.0f;
                if (!fsResult.dirType.isDelta())
                    MISWeight = (bsdfPDF * bsdfPDF) / (lightPDF * lightPDF + bsdfPDF * bsdfPDF);
                SLRAssert(MISWeight <= 1.0f, "Invalid MIS weight: %g", MISWeight);
                
                sp += alpha * Le * MISWeight;
            }
            if (surfPt.atInfinity)
                break;
            
            // Russian roulette
            float continueProb = std::min(alpha.importance(wls.selectedLambda) / initY, 1.0f);
            if (pathSampler.getPathTerminationSample() < continueProb)
                alpha /= continueProb;
            else
                break;
        }
        
        return sp;
    }
Color3 BidirPathTracing::getDirectIllumination(BidirPathState& cameraState , 
	const Vector3& hitPos , BSDF& bsdf)
{
	Color3 res(0);

	Real weight = 0.f;

	int lightCount = scene.lights.size();
	Real lightPickProb = 1.f / lightCount;

	int lightId = (int)(rng.randFloat() * lightCount);
	AbstractLight *light = scene.lights[lightId];

	Vector3 dirToLight;
	Real dist , directPdf , emissionPdf , cosAtLight , cosAtSurface;
	int sampledBSDFType;

	Color3 illu = light->illuminance(scene.sceneSphere ,
		hitPos , rng.randVector3() , dirToLight , dist ,
		directPdf , &emissionPdf , &cosAtLight);

	Real bsdfDirPdf , bsdfRevPdf , cosToLight;

	if (!illu.isBlack() && directPdf > 0)
	{
		Color3 bsdfFactor = bsdf.f(scene , dirToLight ,
			cosToLight , &bsdfDirPdf , &bsdfRevPdf);

		Color3 tmp;

		if (!bsdfFactor.isBlack())
		{	
			Real contProb = bsdf.continueProb;

			bsdfDirPdf *= light->isDelta() ? 0.f : contProb;
			bsdfRevPdf *= contProb;
            
			tmp = (illu | bsdfFactor) * cosToLight / (directPdf * lightPickProb);

			if (!tmp.isBlack() && !scene.occluded(hitPos , dirToLight ,
				hitPos + dirToLight * dist))
			{
				Real wLight = mis(bsdfDirPdf) / mis(directPdf * lightPickProb);
				Real wCamera = mis(emissionPdf * cosToLight / (directPdf * cosAtLight)) *
					(cameraState.dVCM + mis(bsdfRevPdf) * cameraState.dVC);
				weight = 1.f / (wLight + 1.f + wCamera);

				//fprintf(fp , "weight = %.6f\n" , weight);

				if (light->isDelta())
				{
					res = res + tmp;
				}
				else
				{
					Real _weight = mis(directPdf) / 
						(mis(directPdf) + mis(bsdfDirPdf));

					res = res + tmp * _weight;
				}
			}
		}
	}

	if (!light->isDelta())
	{
		Color3 bsdfFactor = bsdf.sample(scene , rng.randVector3() ,
			dirToLight , directPdf , cosAtSurface , &sampledBSDFType);

		if (!bsdfFactor.isBlack() && directPdf > 0)
		{
			Real weight = 1.f;

			Real lightPdf;
			if (!(sampledBSDFType & BSDF_SPECULAR))
			{
				illu = light->getRadiance(scene.sceneSphere , dirToLight , hitPos ,
					&lightPdf , &emissionPdf);

				if (cmp(lightPdf) == 0)
					return res;

				weight = mis(directPdf) /
					(mis(directPdf) + mis(lightPdf));
			}

			Intersection lightInter;
			Color3 tmp(0.f);
			Ray ray(hitPos + dirToLight * EPS , dirToLight);

			if (scene.intersect(ray , lightInter) != NULL)
			{
				if (lightInter.matId < 0)
				{
					if (light != scene.lights[-lightInter.matId - 1])
					{
						illu = Color3(0.f);
					}
				}
				else
				{
					illu = Color3(0.f);
				}
			}
			else
			{
				illu = Color3(0.f);
				if (scene.background != NULL)
				{
					illu = getLightRadiance(scene.background ,
							cameraState , Vector3(0) , ray.dir);
				}
			}

			if (!illu.isBlack())
			{
				tmp = (illu | bsdfFactor) * cosAtSurface /
					(directPdf);
				res = res + tmp * weight;
			}
		}
	}
    
	return res * weight;
}
Exemplo n.º 6
0
void compute_ibl_bsdf_sampling(
    SamplingContext&        sampling_context,
    const ShadingContext&   shading_context,
    const EnvironmentEDF&   environment_edf,
    const ShadingPoint&     shading_point,
    const Dual3d&           outgoing,
    const BSDF&             bsdf,
    const void*             bsdf_data,
    const int               bsdf_sampling_modes,
    const size_t            bsdf_sample_count,
    const size_t            env_sample_count,
    Spectrum&               radiance)
{
    assert(is_normalized(outgoing.get_value()));

    radiance.set(0.0f);

    for (size_t i = 0; i < bsdf_sample_count; ++i)
    {
        // Sample the BSDF.
        // todo: rendering will be incorrect if the BSDF value returned by the sample() method
        // includes the contribution of a specular component since these are explicitly rejected
        // afterward. We need a mechanism to indicate that we want the contribution of some of
        // the components only.
        BSDFSample sample(shading_point, outgoing);
        bsdf.sample(
            sampling_context,
            bsdf_data,
            false,              // not adjoint
            true,               // multiply by |cos(incoming, normal)|
            sample);

        // Filter scattering modes.
        if (!(bsdf_sampling_modes & sample.m_mode))
            continue;

        // Discard occluded samples.
        const float transmission =
            shading_context.get_tracer().trace(
                shading_point,
                Vector3d(sample.m_incoming.get_value()),
                VisibilityFlags::ShadowRay);
        if (transmission == 0.0f)
            continue;

        // Evaluate the environment's EDF.
        InputEvaluator input_evaluator(shading_context.get_texture_cache());
        Spectrum env_value;
        float env_prob;
        environment_edf.evaluate(
            shading_context,
            input_evaluator,
            sample.m_incoming.get_value(),
            env_value,
            env_prob);

        // Apply all weights, including MIS weight.
        if (sample.m_mode == ScatteringMode::Specular)
            env_value *= transmission;
        else
        {
            const float mis_weight =
                mis_power2(
                    bsdf_sample_count * sample.m_probability,
                    env_sample_count * env_prob);
            env_value *= transmission / sample.m_probability * mis_weight;
        }

        // Add the contribution of this sample to the illumination.
        env_value *= sample.m_value;
        radiance += env_value;
    }

    if (bsdf_sample_count > 1)
        radiance /= static_cast<float>(bsdf_sample_count);
}