Example #1
0
void compute_ibl_environment_sampling(
    SamplingContext&        sampling_context,
    const ShadingContext&   shading_context,
    const EnvironmentEDF&   environment_edf,
    const BSSRDF&           bssrdf,
    const void*             bssrdf_data,
    const ShadingPoint&     incoming_point,
    const ShadingPoint&     outgoing_point,
    const Dual3d&           outgoing,
    const size_t            bssrdf_sample_count,
    const size_t            env_sample_count,
    Spectrum&               radiance)
{
    assert(is_normalized(outgoing.get_value()));

    const Basis3d& shading_basis = incoming_point.get_shading_basis();

    radiance.set(0.0f);

    sampling_context.split_in_place(2, env_sample_count);

    for (size_t i = 0; i < env_sample_count; ++i)
    {
        // Generate a uniform sample in [0,1)^2.
        const Vector2d s = sampling_context.next_vector2<2>();

        // Sample the environment.
        InputEvaluator input_evaluator(shading_context.get_texture_cache());
        Vector3d incoming;
        Spectrum env_value;
        double env_prob;
        environment_edf.sample(
            shading_context,
            input_evaluator,
            s,
            incoming,
            env_value,
            env_prob);

        // Cull samples behind the shading surface.
        assert(is_normalized(incoming));
        const double cos_in = dot(incoming, shading_basis.get_normal());
        if (cos_in <= 0.0)
            continue;

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

        // Evaluate the BSSRDF.
        Spectrum bssrdf_value;
        bssrdf.evaluate(
            bssrdf_data,
            outgoing_point,
            outgoing.get_value(),
            incoming_point,
            incoming,
            bssrdf_value);

        // Compute MIS weight.
        const double bssrdf_prob = cos_in * RcpPi;
        const double mis_weight =
            mis_power2(
                env_sample_count * env_prob,
                bssrdf_sample_count * bssrdf_prob);

        // Add the contribution of this sample to the illumination.
        env_value *= static_cast<float>(transmission * cos_in / env_prob * mis_weight);
        env_value *= bssrdf_value;
        radiance += env_value;
    }

    if (env_sample_count > 1)
        radiance /= static_cast<float>(env_sample_count);
}
Example #2
0
	Spectrum Li(const RayDifferential &r, RadianceQueryRecord &rRec) const {
		/* Some aliases and local variables */
		const Scene *scene = rRec.scene;
		Intersection &its = rRec.its;
		RayDifferential ray(r);
		Spectrum Li(0.0f);
		bool scattered = false;

		/* Perform the first ray intersection (or ignore if the
		   intersection has already been provided). */
		rRec.rayIntersect(ray);
		ray.mint = Epsilon;

		Spectrum throughput(1.0f);
		Float eta = 1.0f;

		while (rRec.depth <= m_maxDepth || m_maxDepth < 0) {
			if (!its.isValid()) {
				/* If no intersection could be found, potentially return
				   radiance from a environment luminaire if it exists */
				if ((rRec.type & RadianceQueryRecord::EEmittedRadiance)
					&& (!m_hideEmitters || scattered))
					Li += throughput * scene->evalEnvironment(ray);
				break;
			}

			const BSDF *bsdf = its.getBSDF(ray);

			/* Possibly include emitted radiance if requested */
			if (its.isEmitter() && (rRec.type & RadianceQueryRecord::EEmittedRadiance)
				&& (!m_hideEmitters || scattered))
				Li += throughput * its.Le(-ray.d);

			/* Include radiance from a subsurface scattering model if requested */
			if (its.hasSubsurface() && (rRec.type & RadianceQueryRecord::ESubsurfaceRadiance))
				Li += throughput * its.LoSub(scene, rRec.sampler, -ray.d, rRec.depth);

			if ((rRec.depth >= m_maxDepth && m_maxDepth > 0)
				|| (m_strictNormals && dot(ray.d, its.geoFrame.n)
					* Frame::cosTheta(its.wi) >= 0)) {

				/* Only continue if:
				   1. The current path length is below the specifed maximum
				   2. If 'strictNormals'=true, when the geometric and shading
				      normals classify the incident direction to the same side */
				break;
			}

			/* ==================================================================== */
			/*                     Direct illumination sampling                     */
			/* ==================================================================== */

			/* Estimate the direct illumination if this is requested */
			DirectSamplingRecord dRec(its);

			if (rRec.type & RadianceQueryRecord::EDirectSurfaceRadiance &&
				(bsdf->getType() & BSDF::ESmooth)) {
				Spectrum value = scene->sampleEmitterDirect(dRec, rRec.nextSample2D());
				if (!value.isZero()) {
					const Emitter *emitter = static_cast<const Emitter *>(dRec.object);

					/* Allocate a record for querying the BSDF */
					BSDFSamplingRecord bRec(its, its.toLocal(dRec.d), ERadiance);

					/* Evaluate BSDF * cos(theta) */
					const Spectrum bsdfVal = bsdf->eval(bRec);

					/* Prevent light leaks due to the use of shading normals */
					if (!bsdfVal.isZero() && (!m_strictNormals
							|| dot(its.geoFrame.n, dRec.d) * Frame::cosTheta(bRec.wo) > 0)) {

						/* Calculate prob. of having generated that direction
						   using BSDF sampling */
						Float bsdfPdf = (emitter->isOnSurface() && dRec.measure == ESolidAngle)
							? bsdf->pdf(bRec) : 0;

						/* Weight using the power heuristic */
						Float weight = miWeight(dRec.pdf, bsdfPdf);
						Li += throughput * value * bsdfVal * weight;
					}
				}
			}

			/* ==================================================================== */
			/*                            BSDF sampling                             */
			/* ==================================================================== */

			/* Sample BSDF * cos(theta) */
			Float bsdfPdf;
			BSDFSamplingRecord bRec(its, rRec.sampler, ERadiance);
			Spectrum bsdfWeight = bsdf->sample(bRec, bsdfPdf, rRec.nextSample2D());
			if (bsdfWeight.isZero())
				break;

			scattered |= bRec.sampledType != BSDF::ENull;

			/* Prevent light leaks due to the use of shading normals */
			const Vector wo = its.toWorld(bRec.wo);
			Float woDotGeoN = dot(its.geoFrame.n, wo);
			if (m_strictNormals && woDotGeoN * Frame::cosTheta(bRec.wo) <= 0)
				break;

			bool hitEmitter = false;
			Spectrum value;

			/* Trace a ray in this direction */
			ray = Ray(its.p, wo, ray.time);
			if (scene->rayIntersect(ray, its)) {
				/* Intersected something - check if it was a luminaire */
				if (its.isEmitter()) {
					value = its.Le(-ray.d);
					dRec.setQuery(ray, its);
					hitEmitter = true;
				}
			} else {
				/* Intersected nothing -- perhaps there is an environment map? */
				const Emitter *env = scene->getEnvironmentEmitter();

				if (env) {
					if (m_hideEmitters && !scattered)
						break;

					value = env->evalEnvironment(ray);
					if (!env->fillDirectSamplingRecord(dRec, ray))
						break;
					hitEmitter = true;
				} else {
					break;
				}
			}

			/* Keep track of the throughput and relative
			   refractive index along the path */
			throughput *= bsdfWeight;
			eta *= bRec.eta;

			/* If a luminaire was hit, estimate the local illumination and
			   weight using the power heuristic */
			if (hitEmitter &&
				(rRec.type & RadianceQueryRecord::EDirectSurfaceRadiance)) {
				/* Compute the prob. of generating that direction using the
				   implemented direct illumination sampling technique */
				const Float lumPdf = (!(bRec.sampledType & BSDF::EDelta)) ?
					scene->pdfEmitterDirect(dRec) : 0;
				Li += throughput * value * miWeight(bsdfPdf, lumPdf);
			}

			/* ==================================================================== */
			/*                         Indirect illumination                        */
			/* ==================================================================== */

			/* Set the recursive query type. Stop if no surface was hit by the
			   BSDF sample or if indirect illumination was not requested */
			if (!its.isValid() || !(rRec.type & RadianceQueryRecord::EIndirectSurfaceRadiance))
				break;
			rRec.type = RadianceQueryRecord::ERadianceNoEmission;

			if (rRec.depth++ >= m_rrDepth) {
				/* Russian roulette: try to keep path weights equal to one,
				   while accounting for the solid angle compression at refractive
				   index boundaries. Stop with at least some probability to avoid
				   getting stuck (e.g. due to total internal reflection) */

				Float q = std::min(throughput.max() * eta * eta, (Float) 0.95f);
				if (rRec.nextSample1D() >= q)
					break;
				throughput /= q;
			}
		}

		/* Store statistics */
		avgPathLength.incrementBase();
		avgPathLength += rRec.depth;

		return Li;
	}
Example #3
0
inline float I(const Spectrum &L) {
    return L.y();
}
Example #4
0
// refl: [0, 1] value.
UniformLambertMaterial::UniformLambertMaterial(const Spectrum& refl) :
		refl(refl) {
    if(refl.minCoeff() < 0 || refl.maxCoeff() > 1) {
        throw physics_error("Don't create non energy conserving lambertian BSDF.");
    }
}
Example #5
0
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 point
    BSDF *bsdf = isect.GetBSDF(ray, arena);
    const 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);
                auto optGatherIsect = scene.Intersect(bounceRay);
                if (optGatherIsect) {
                    // Compute exitant radiance _Lindir_ using radiance photons
                    Spectrum Lindir = 0.f;
                    Normal nGather = optGatherIsect->dg.nn;
                    nGather = Faceforward(nGather, -bounceRay.d);
                    RadiancePhotonProcess proc(nGather);
                    float md2 = INFINITY;
                    radianceMap->Lookup(optGatherIsect->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);
                PBRT_PHOTON_MAP_STARTED_GATHER_RAY(&bounceRay);
                auto optGatherIsect = scene.Intersect(bounceRay);
                if (optGatherIsect) {
                    // Compute exitant radiance _Lindir_ using radiance photons
                    Spectrum Lindir = 0.f;
                    Normal nGather = optGatherIsect->dg.nn;
                    nGather = Faceforward(nGather, -bounceRay.d);
                    RadiancePhotonProcess proc(nGather);
                    float md2 = INFINITY;
                    radianceMap->Lookup(optGatherIsect->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;
}
void DirectLightingIntegrator::compute_outgoing_radiance_light_sampling_low_variance(
    SamplingContext&            sampling_context,
    const MISHeuristic          mis_heuristic,
    const Dual3d&               outgoing,
    Spectrum&                   radiance,
    SpectrumStack&              aovs) const
{
    radiance.set(0.0f);
    aovs.set(0.0f);

    // todo: if we had a way to know that a BSDF is purely specular, we could
    // immediately return black here since there will be no contribution from
    // such a BSDF.

    // Sample emitting triangles.
    if (m_light_sampler.get_emitting_triangle_count() > 0)
    {
        sampling_context.split_in_place(3, m_light_sample_count);

        for (size_t i = 0; i < m_light_sample_count; ++i)
        {
            const Vector3d s = sampling_context.next_vector2<3>();

            LightSample sample;
            m_light_sampler.sample_emitting_triangles(m_time, s, sample);

            add_emitting_triangle_sample_contribution(
                sample,
                mis_heuristic,
                outgoing,
                radiance,
                aovs);
        }

        if (m_light_sample_count > 1)
        {
            const float rcp_light_sample_count = 1.0f / m_light_sample_count;
            radiance *= rcp_light_sample_count;
            aovs *= rcp_light_sample_count;
        }
    }

    // Sample non-physical light sources.
    const size_t light_count = m_light_sampler.get_non_physical_light_count();
    if (light_count > 0)
    {
        sampling_context.split_in_place(2, light_count);

        for (size_t i = 0; i < light_count; ++i)
        {
            const Vector2d s = sampling_context.next_vector2<2>();

            LightSample sample;
            m_light_sampler.sample_non_physical_light(m_time, s, i, sample);

            add_non_physical_light_sample_contribution(
                sample,
                outgoing,
                radiance,
                aovs);
        }
    }
}
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);
}
Example #8
0
void Path::AdvancePath(PathRenderEngine *renderEngine, Sampler *sampler, const RayBuffer *rayBuffer,
		SampleBuffer *sampleBuffer) {
	SLGScene *scene = renderEngine->scene;

	//--------------------------------------------------------------------------
	// Select the path ray hit
	//--------------------------------------------------------------------------

	const RayHit *rayHit = NULL;
	switch (state) {
		case EYE_VERTEX:
			if (scene->volumeIntegrator) {
				rayHit = rayBuffer->GetRayHit(currentPathRayIndex);

				// Use Russian Roulette to check if I have to do participating media computation or not
				if (sample.GetLazyValue() <= scene->volumeIntegrator->GetRRProbability()) {
					Ray volumeRay(pathRay.o, pathRay.d, 0.f, rayHit->Miss() ? std::numeric_limits<float>::infinity() : rayHit->t);
					scene->volumeIntegrator->GenerateLiRays(scene, &sample, volumeRay, volumeComp);
					radiance += volumeComp->GetEmittedLight();

					if (volumeComp->GetRayCount() > 0) {
						// Do the EYE_VERTEX_VOLUME_STEP
						state = EYE_VERTEX_VOLUME_STEP;
						eyeHit = *(rayBuffer->GetRayHit(currentPathRayIndex));
						return;
					}
				}
			} else
				rayHit = rayBuffer->GetRayHit(currentPathRayIndex);
			break;
		case NEXT_VERTEX:
			rayHit = rayBuffer->GetRayHit(currentPathRayIndex);
			break;
		case EYE_VERTEX_VOLUME_STEP:
			// Add scattered light
			radiance += throughput * volumeComp->CollectResults(rayBuffer) / scene->volumeIntegrator->GetRRProbability();

			rayHit = &eyeHit;
			break;
		case TRANSPARENT_SHADOW_RAYS_STEP:
			rayHit = &eyeHit;
			break;
		case TRANSPARENT_ONLY_SHADOW_RAYS_STEP:
		case ONLY_SHADOW_RAYS:
			// Nothing
			break;
		default:
			assert(false);
			break;
	}

	//--------------------------------------------------------------------------
	// Finish direct light sampling
	//--------------------------------------------------------------------------

	if (((state == NEXT_VERTEX) ||
			(state == ONLY_SHADOW_RAYS) ||
			(state == TRANSPARENT_SHADOW_RAYS_STEP) ||
			(state == TRANSPARENT_ONLY_SHADOW_RAYS_STEP)) &&
			(tracedShadowRayCount > 0)) {
		unsigned int leftShadowRaysToTrace = 0;
		for (unsigned int i = 0; i < tracedShadowRayCount; ++i) {
			const RayHit *shadowRayHit = rayBuffer->GetRayHit(currentShadowRayIndex[i]);
			if (shadowRayHit->Miss()) {
				// Nothing was hit, light is visible
				radiance += lightColor[i] / lightPdf[i];
			} else {
				// Something was hit check if it is transparent
				const unsigned int currentShadowTriangleIndex = shadowRayHit->index;
				const unsigned int currentShadowMeshIndex = scene->dataSet->GetMeshID(currentShadowTriangleIndex);
				Material *triMat = scene->objectMaterials[currentShadowMeshIndex];

				if (triMat->IsShadowTransparent()) {
					// It is shadow transparent, I need to continue to trace the ray
					shadowRay[leftShadowRaysToTrace] = Ray(
							shadowRay[i](shadowRayHit->t),
							shadowRay[i].d,
							shadowRay[i].mint,
							shadowRay[i].maxt - shadowRayHit->t);

					lightColor[leftShadowRaysToTrace] = lightColor[i] * triMat->GetSahdowTransparency();
					lightPdf[leftShadowRaysToTrace] = lightPdf[i];
					leftShadowRaysToTrace++;
				} else {
					// Check if there is a texture with alpha
					TexMapInstance *tm = scene->objectTexMaps[currentShadowMeshIndex];

					if (tm) {
						const TextureMap *map = tm->GetTexMap();

						if (map->HasAlpha()) {
							const ExtMesh *mesh = scene->objects[currentShadowMeshIndex];
							const UV triUV = mesh->InterpolateTriUV(scene->dataSet->GetMeshTriangleID(currentShadowTriangleIndex),
									shadowRayHit->b1, shadowRayHit->b2);

							const float alpha = map->GetAlpha(triUV);

							if (alpha < 1.f) {
								// It is shadow transparent, I need to continue to trace the ray
								shadowRay[leftShadowRaysToTrace] = Ray(
										shadowRay[i](shadowRayHit->t),
										shadowRay[i].d,
										shadowRay[i].mint,
										shadowRay[i].maxt - shadowRayHit->t);

								lightColor[leftShadowRaysToTrace] = lightColor[i] * (1.f - alpha);
								lightPdf[leftShadowRaysToTrace] = lightPdf[i];
								leftShadowRaysToTrace++;
							}
						}
					}
				}
			}
		}

		if (leftShadowRaysToTrace > 0) {
			tracedShadowRayCount = leftShadowRaysToTrace;
			if ((state == ONLY_SHADOW_RAYS) || (state == TRANSPARENT_ONLY_SHADOW_RAYS_STEP))
				state = TRANSPARENT_ONLY_SHADOW_RAYS_STEP;
			else {
				eyeHit = *rayHit;
				state = TRANSPARENT_SHADOW_RAYS_STEP;
			}

			return;
		}
	}

	//--------------------------------------------------------------------------
	// Calculate next step
	//--------------------------------------------------------------------------

	depth++;

	const bool missed = rayHit ? rayHit->Miss() : false;
	if (missed ||
			(state == ONLY_SHADOW_RAYS) ||
			(state == TRANSPARENT_ONLY_SHADOW_RAYS_STEP) ||
			(depth >= renderEngine->maxPathDepth)) {
		if (missed && scene->infiniteLight && (scene->useInfiniteLightBruteForce || specularBounce)) {
			// Add the light emitted by the infinite light
			radiance += scene->infiniteLight->Le(pathRay.d) * throughput;
		}

		// Hit nothing/only shadow rays/maxdepth, terminate the path
		sampleBuffer->SplatSample(sample.screenX, sample.screenY, radiance);
		// Restart the path
		Init(renderEngine, sampler);
		return;
	}

	// Something was hit
	const unsigned int currentTriangleIndex = rayHit->index;
	const unsigned int currentMeshIndex = scene->dataSet->GetMeshID(currentTriangleIndex);

	// Get the triangle
	const ExtMesh *mesh = scene->objects[currentMeshIndex];
	const unsigned int triIndex = scene->dataSet->GetMeshTriangleID(currentTriangleIndex);

	// Get the material
	const Material *triMat = scene->objectMaterials[currentMeshIndex];

	// Check if it is a light source
	if (triMat->IsLightSource()) {
		if (specularBounce) {
			// Only TriangleLight can be directly hit
			const LightMaterial *mLight = (LightMaterial *) triMat;
			Spectrum Le = mLight->Le(mesh, triIndex, -pathRay.d);

			radiance += Le * throughput;
		}

		// Terminate the path
		sampleBuffer->SplatSample(sample.screenX, sample.screenY, radiance);
		// Restart the path
		Init(renderEngine, sampler);
		return;
	}

	//--------------------------------------------------------------------------
	// Build the shadow rays (if required)
	//--------------------------------------------------------------------------

	// Interpolate face normal
	Normal N = mesh->InterpolateTriNormal(triIndex, rayHit->b1, rayHit->b2);

	const SurfaceMaterial *triSurfMat = (SurfaceMaterial *) triMat;
	const Point hitPoint = pathRay(rayHit->t);
	const Vector wo = -pathRay.d;

	Spectrum surfaceColor;
	if (mesh->HasColors())
		surfaceColor = mesh->InterpolateTriColor(triIndex, rayHit->b1, rayHit->b2);
	else
		surfaceColor = Spectrum(1.f, 1.f, 1.f);

	// Check if I have to apply texture mapping or normal mapping
	TexMapInstance *tm = scene->objectTexMaps[currentMeshIndex];
	BumpMapInstance *bm = scene->objectBumpMaps[currentMeshIndex];
	NormalMapInstance *nm = scene->objectNormalMaps[currentMeshIndex];
	if (tm || bm || nm) {
		// Interpolate UV coordinates if required
		const UV triUV = mesh->InterpolateTriUV(triIndex, rayHit->b1, rayHit->b2);

		// Check if there is an assigned texture map
		if (tm) {
			const TextureMap *map = tm->GetTexMap();

			// Apply texture mapping
			surfaceColor *= map->GetColor(triUV);

			// Check if the texture map has an alpha channel
			if (map->HasAlpha()) {
				const float alpha = map->GetAlpha(triUV);

				if ((alpha == 0.0f) || ((alpha < 1.f) && (sample.GetLazyValue() > alpha))) {
					pathRay = Ray(pathRay(rayHit->t), pathRay.d, RAY_EPSILON, pathRay.maxt - rayHit->t);
					state = NEXT_VERTEX;
					tracedShadowRayCount = 0;
					return;
				}
			}
		}

		// Check if there is an assigned bump/normal map
		if (bm || nm) {
			if (nm) {
				// Apply normal mapping
				const Spectrum color = nm->GetTexMap()->GetColor(triUV);

				const float x = 2.f * (color.r - 0.5f);
				const float y = 2.f * (color.g - 0.5f);
				const float z = 2.f * (color.b - 0.5f);

				Vector v1, v2;
				CoordinateSystem(Vector(N), &v1, &v2);
				N = Normalize(Normal(
						v1.x * x + v2.x * y + N.x * z,
						v1.y * x + v2.y * y + N.y * z,
						v1.z * x + v2.z * y + N.z * z));
			}

			if (bm) {
				// Apply bump mapping
				const TextureMap *map = bm->GetTexMap();
				const UV &dudv = map->GetDuDv();

				const float b0 = map->GetColor(triUV).Filter();

				const UV uvdu(triUV.u + dudv.u, triUV.v);
				const float bu = map->GetColor(uvdu).Filter();

				const UV uvdv(triUV.u, triUV.v + dudv.v);
				const float bv = map->GetColor(uvdv).Filter();

				const float scale = bm->GetScale();
				const Vector bump(scale * (bu - b0), scale * (bv - b0), 1.f);

				Vector v1, v2;
				CoordinateSystem(Vector(N), &v1, &v2);
				N = Normalize(Normal(
						v1.x * bump.x + v2.x * bump.y + N.x * bump.z,
						v1.y * bump.x + v2.y * bump.y + N.y * bump.z,
						v1.z * bump.x + v2.z * bump.y + N.z * bump.z));
			}
		}
	}

	// Flip the normal if required
	Normal shadeN = (Dot(pathRay.d, N) > 0.f) ? -N : N;

	tracedShadowRayCount = 0;
	const bool skipInfiniteLight = !renderEngine->onlySampleSpecular;
	const unsigned int lightCount = scene->GetLightCount(skipInfiniteLight);
	if (triSurfMat->IsDiffuse() && (scene->GetLightCount(skipInfiniteLight) > 0)) {
		// Direct light sampling: trace shadow rays

		switch (renderEngine->lightStrategy) {
			case ALL_UNIFORM: {
				// ALL UNIFORM direct sampling light strategy
				const Spectrum lightThroughtput = throughput * surfaceColor;

				for (unsigned int j = 0; j < lightCount; ++j) {
					// Select the light to sample
					const LightSource *light = scene->GetLight(j, skipInfiniteLight);

					for (unsigned int i = 0; i < renderEngine->shadowRayCount; ++i) {
						// Select a point on the light surface
						lightColor[tracedShadowRayCount] = light->Sample_L(
								scene, hitPoint, &shadeN,
								sample.GetLazyValue(), sample.GetLazyValue(), sample.GetLazyValue(),
								&lightPdf[tracedShadowRayCount], &shadowRay[tracedShadowRayCount]);

						// Scale light pdf for ALL_UNIFORM strategy
						lightPdf[tracedShadowRayCount] *= renderEngine->shadowRayCount;

						if (lightPdf[tracedShadowRayCount] <= 0.0f)
							continue;

						const Vector lwi = shadowRay[tracedShadowRayCount].d;
						lightColor[tracedShadowRayCount] *= lightThroughtput * Dot(shadeN, lwi) *
								triSurfMat->f(wo, lwi, shadeN);

						if (!lightColor[tracedShadowRayCount].Black())
							tracedShadowRayCount++;
					}
				}
				break;
			}
			case ONE_UNIFORM: {
				// ONE UNIFORM direct sampling light strategy
				const Spectrum lightThroughtput = throughput * surfaceColor;

				for (unsigned int i = 0; i < renderEngine->shadowRayCount; ++i) {
					// Select the light to sample
					float lightStrategyPdf;
					const LightSource *light = scene->SampleAllLights(sample.GetLazyValue(),
							&lightStrategyPdf, skipInfiniteLight);

					// Select a point on the light surface
					lightColor[tracedShadowRayCount] = light->Sample_L(
							scene, hitPoint, &shadeN,
							sample.GetLazyValue(), sample.GetLazyValue(), sample.GetLazyValue(),
							&lightPdf[tracedShadowRayCount], &shadowRay[tracedShadowRayCount]);

					if (lightPdf[tracedShadowRayCount] <= 0.f)
						continue;

					const Vector lwi = shadowRay[tracedShadowRayCount].d;
					lightColor[tracedShadowRayCount] *= lightThroughtput * Dot(shadeN, lwi) *
							triSurfMat->f(wo, lwi, shadeN);

					if (!lightColor[tracedShadowRayCount].Black()) {
						lightPdf[tracedShadowRayCount] *= lightStrategyPdf * renderEngine->shadowRayCount;
						tracedShadowRayCount++;
					}
				}
				break;
			}
			default:
				assert(false);
		}
	}

	//--------------------------------------------------------------------------
	// Build the next vertex path ray
	//--------------------------------------------------------------------------

	float fPdf;
	Vector wi;
	const Spectrum f = triSurfMat->Sample_f(wo, &wi, N, shadeN,
			sample.GetLazyValue(), sample.GetLazyValue(), sample.GetLazyValue(),
			renderEngine->onlySampleSpecular, &fPdf, specularBounce) * surfaceColor;
	if ((fPdf <= 0.f) || f.Black()) {
		if (tracedShadowRayCount > 0)
			state = ONLY_SHADOW_RAYS;
		else {
			// Terminate the path
			sampleBuffer->SplatSample(sample.screenX, sample.screenY, radiance);
			// Restart the path
			Init(renderEngine, sampler);
		}

		return;
	}

	throughput *= f / fPdf;

	if (depth > renderEngine->rrDepth) {
		// Russian Roulette
		switch (renderEngine->rrStrategy) {
			case PROBABILITY: {
				if (renderEngine->rrProb >= sample.GetLazyValue())
					throughput /= renderEngine->rrProb;
				else {
					// Check if terminate the path or I have still to trace shadow rays
					if (tracedShadowRayCount > 0)
						state = ONLY_SHADOW_RAYS;
					else {
						// Terminate the path
						sampleBuffer->SplatSample(sample.screenX, sample.screenY, radiance);
						// Restart the path
						Init(renderEngine, sampler);
					}

					return;
				}
				break;
			}
			case IMPORTANCE: {
				const float prob = Max(throughput.Filter(), renderEngine->rrImportanceCap);
				if (prob >= sample.GetLazyValue())
					throughput /= prob;
				else {
					// Check if terminate the path or I have still to trace shadow rays
					if (tracedShadowRayCount > 0)
						state = ONLY_SHADOW_RAYS;
					else {
						// Terminate the path
						sampleBuffer->SplatSample(sample.screenX, sample.screenY, radiance);
						// Restart the path
						Init(renderEngine, sampler);
					}

					return;
				}
				break;
			}
			default:
				assert(false);
		}
	}

	pathRay.o = hitPoint;
	pathRay.d = wi;
	state = NEXT_VERTEX;
}
Example #9
0
// return the radiance of a specific direction
// note : there are one factor makes the method biased.
//		there is a limitation on the number of vertexes in the path
Spectrum PathTracing::Li( const Ray& ray , const PixelSample& ps ) const
{
	Spectrum	L = 0.0f;
	Spectrum	throughput = 1.0f;

	int			bounces = 0;
	Ray	r = ray;
	while(true)
	{
		Intersection inter;

		// get the intersection between the ray and the scene
		// if it's a light , accumulate the radiance and break
		if( false == scene.GetIntersect( r , &inter ) )
		{
			if( bounces == 0 )
				return scene.Le( r );
			break;
		}

		if( bounces == 0 ) L+=inter.Le(-r.m_Dir);

		// make sure there is intersected primitive
		Sort_Assert( inter.primitive != 0 );

		// evaluate the light
		Bsdf*			bsdf = inter.primitive->GetMaterial()->GetBsdf(&inter);
		float			light_pdf = 0.0f;
		LightSample		light_sample = (bounces==0)?ps.light_sample[0]:LightSample(true);
		BsdfSample		bsdf_sample = (bounces==0)?ps.bsdf_sample[0]:BsdfSample(true);
		const Light*	light = scene.SampleLight( light_sample.t , &light_pdf );
		if( light_pdf > 0.0f )
			L += throughput * EvaluateDirect(	r  , scene , light , inter , light_sample , 
												bsdf_sample , BXDF_TYPE(BXDF_ALL) ) / light_pdf;

		// sample the next direction using bsdf
		float		path_pdf;
		Vector		wi;
		BXDF_TYPE	bxdf_type;
		Spectrum f;
		BsdfSample	_bsdf_sample = (bounces==0)?ps.bsdf_sample[1]:BsdfSample(true);
		f = bsdf->sample_f( -r.m_Dir , wi , _bsdf_sample , &path_pdf , BXDF_ALL , &bxdf_type );
		if( f.IsBlack() || path_pdf == 0.0f )
			break;

		// update path weight
		throughput *= f * AbsDot( wi , inter.normal ) / path_pdf;

		if( throughput.GetIntensity() == 0.0f )
			break;
		if( bounces > 4 )
		{
			float continueProperbility = min( 0.5f , throughput.GetIntensity() );
			if( sort_canonical() > continueProperbility )
				break;
			throughput /= continueProperbility;
		}

		r.m_Ori = inter.intersect;
		r.m_Dir = wi;
		r.m_fMin = 0.001f;

		++bounces;

		// note :	the following code makes the method biased
		//			'path_per_pixel' could be set very large to reduce the side-effect.
		if( bounces >= max_recursive_depth )
			break;
	}

	return L;
}
Example #10
0
u_int64_t CPU_Worker::AdvancePhotonPath(u_int64_t photonTarget) {



  uint todoPhotonCount = 0;

  PhotonPath* livePhotonPaths = new PhotonPath[rayBuffer->GetSize()];

  rayBuffer->Reset();

  size_t initc = min((int) rayBuffer->GetSize(), (int) photonTarget);

  double start = WallClockTime();


  for (size_t i = 0; i < initc; ++i) {

    int p = rayBuffer->ReserveRay();

    Ray * b = &(rayBuffer->GetRayBuffer())[p];

    engine->InitPhotonPath(engine->ss, &livePhotonPaths[i], b, seedBuffer[i]);

  }


  while (todoPhotonCount < photonTarget) {

    Intersect(rayBuffer);

#ifndef __DEBUG
    omp_set_num_threads(config->max_threads);
#pragma omp parallel for schedule(guided)
#endif
    for (unsigned int i = 0; i < rayBuffer->GetRayCount(); ++i) {
      PhotonPath *photonPath = &livePhotonPaths[i];
      Ray *ray = &rayBuffer->GetRayBuffer()[i];
      RayHit *rayHit = &rayBuffer->GetHitBuffer()[i];

      if (photonPath->done == true) {
        continue;
      }

      if (rayHit->Miss()) {
        photonPath->done = true;
      } else { // Something was hit

        Point hitPoint;
        Spectrum surfaceColor;
        Normal N, shadeN;

        if (engine->GetHitPointInformation(engine->ss, ray, rayHit, hitPoint, surfaceColor,
            N, shadeN))
          continue;

        const unsigned int currentTriangleIndex = rayHit->index;
        const unsigned int currentMeshIndex = engine->ss->meshIDs[currentTriangleIndex];

        POINTERFREESCENE::Material *hitPointMat =
            &engine->ss->materials[engine->ss->meshMats[currentMeshIndex]];

        uint matType = hitPointMat->type;

        if (matType == MAT_AREALIGHT) {
          photonPath->done = true;
        } else {

          float fPdf;
          Vector wi;
          Vector wo = -ray->d;
          bool specularBounce = true;

          float u0 = getFloatRNG(seedBuffer[i]);
          float u1 = getFloatRNG(seedBuffer[i]);
          float u2 = getFloatRNG(seedBuffer[i]);

          Spectrum f;

          switch (matType) {

          case MAT_MATTE:
            engine->ss->Matte_Sample_f(&hitPointMat->param.matte, &wo, &wi, &fPdf, &f,
                &shadeN, u0, u1, &specularBounce);

            f *= surfaceColor;
            break;

          case MAT_MIRROR:
            engine->ss->Mirror_Sample_f(&hitPointMat->param.mirror, &wo, &wi, &fPdf,
                &f, &shadeN, &specularBounce);
            f *= surfaceColor;
            break;

          case MAT_GLASS:
            engine->ss->Glass_Sample_f(&hitPointMat->param.glass, &wo, &wi, &fPdf, &f,
                &N, &shadeN, u0, &specularBounce);
            f *= surfaceColor;

            break;

          case MAT_MATTEMIRROR:
            engine->ss->MatteMirror_Sample_f(&hitPointMat->param.matteMirror, &wo, &wi,
                &fPdf, &f, &shadeN, u0, u1, u2, &specularBounce);
            f *= surfaceColor;

            break;

          case MAT_METAL:
            engine->ss->Metal_Sample_f(&hitPointMat->param.metal, &wo, &wi, &fPdf, &f,
                &shadeN, u0, u1, &specularBounce);
            f *= surfaceColor;

            break;

          case MAT_MATTEMETAL:
            engine->ss->MatteMetal_Sample_f(&hitPointMat->param.matteMetal, &wo, &wi,
                &fPdf, &f, &shadeN, u0, u1, u2, &specularBounce);
            f *= surfaceColor;

            break;

          case MAT_ALLOY:
            engine->ss->Alloy_Sample_f(&hitPointMat->param.alloy, &wo, &wi, &fPdf, &f,
                &shadeN, u0, u1, u2, &specularBounce);
            f *= surfaceColor;

            break;

          case MAT_ARCHGLASS:
            engine->ss->ArchGlass_Sample_f(&hitPointMat->param.archGlass, &wo, &wi,
                &fPdf, &f, &N, &shadeN, u0, &specularBounce);
            f *= surfaceColor;

            break;

          case MAT_NULL:
            wi = ray->d;
            specularBounce = 1;
            fPdf = 1.f;
            break;

          default:
            // Huston, we have a problem...
            specularBounce = 1;
            fPdf = 0.f;
            break;
          }

          if (!specularBounce) // if difuse
            lookupA->AddFlux(engine->ss, engine->alpha, hitPoint, shadeN, -ray->d,
                photonPath->flux, currentPhotonRadius2);

          if (photonPath->depth < MAX_PHOTON_PATH_DEPTH) {
            // Build the next vertex path ray
            if ((fPdf <= 0.f) || f.Black()) {
              photonPath->done = true;
            } else {
              photonPath->depth++;
              photonPath->flux *= f / fPdf;

              // Russian Roulette
              const float p = 0.75f;
              if (photonPath->depth < 3) {
                *ray = Ray(hitPoint, wi);
              } else if (getFloatRNG(seedBuffer[i]) < p) {
                photonPath->flux /= p;
                *ray = Ray(hitPoint, wi);
              } else {
                photonPath->done = true;
              }
            }
          } else {
            photonPath->done = true;
          }
        }
      }
    }

    uint oldc = rayBuffer->GetRayCount();

    rayBuffer->Reset();

    for (unsigned int i = 0; i < oldc; ++i) {

      PhotonPath *photonPath = &livePhotonPaths[i];
      Ray *ray = &rayBuffer->GetRayBuffer()[i];

      if (photonPath->done && todoPhotonCount < photonTarget) {
        todoPhotonCount++;

        Ray n;
        engine->InitPhotonPath(engine->ss, photonPath, &n, seedBuffer[i]);

        livePhotonPaths[i].done = false;

        size_t p = rayBuffer->AddRay(n);
        livePhotonPaths[p] = *photonPath;

      } else if (!photonPath->done) {
        rayBuffer->AddRay(*ray);
      }
    }
  }


//  float MPhotonsSec = todoPhotonCount / ((WallClockTime()-start) * 1000000.f);

  //printf("\nRate: %.3f MPhotons/sec\n",MPhotonsSec);


  profiler->addPhotonTracingTime(WallClockTime() - start);
  profiler->addPhotonsTraced(todoPhotonCount);

  rayBuffer->Reset();

  return todoPhotonCount;
}
Example #11
0
	static void convert(const Spectrum &from, float *to) {
		*to = from.y();
	}
Example #12
0
void CPU_Worker::AdvanceEyePaths( RayBuffer *rayBuffer, EyePath* todoEyePaths, uint* eyePathIndexes) {

  const uint max = rayBuffer->GetRayCount();

omp_set_num_threads(config->max_threads);
#pragma omp parallel for schedule(guided)
  for (uint i = 0; i < max; i++) {

    EyePath *eyePath = &todoEyePaths[eyePathIndexes[i]];

    const RayHit *rayHit = &rayBuffer->GetHitBuffer()[i];

    if (rayHit->Miss()) {
      // Add an hit point
      //HitPointInfo &hp = *(engine->GetHitPointInfo(eyePath->pixelIndex));
      HitPointStaticInfo &hp = hitPointsStaticInfo_iterationCopy[eyePath->sampleIndex];

      //HitPoint &hp = GetHitPoint(hitPointsIndex++);
      hp.type = CONSTANT_COLOR;
      hp.scrX = eyePath->scrX;
      hp.scrY = eyePath->scrY;

      //            if (scene->infiniteLight)
      //              hp.throughput = scene->infiniteLight->Le(
      //                  eyePath->ray.d) * eyePath->throughput;
      //            else
      //              hp.throughput = Spectrum();

      if (ss->infiniteLight || ss->sunLight || ss->skyLight) {
        //  hp.throughput = scene->infiniteLight->Le(eyePath->ray.d) * eyePath->throughput;

        if (ss->infiniteLight)
          ss->InfiniteLight_Le(&hp.throughput, &eyePath->ray.d, ss->infiniteLight,
              ss->infiniteLightMap);
        if (ss->sunLight)
          ss->SunLight_Le(&hp.throughput, &eyePath->ray.d, ss->sunLight);
        if (ss->skyLight)
          ss->SkyLight_Le(&hp.throughput, &eyePath->ray.d, ss->skyLight);

        hp.throughput *= eyePath->throughput;
      } else
        hp.throughput = Spectrum();

      // Free the eye path
      //ihp.accumPhotonCount = 0;
      //ihp.accumReflectedFlux = Spectrum();
      //ihp.photonCount = 0;
      //hp.reflectedFlux = Spectrum();
      eyePath->done = true;

      //--todoEyePathCount;

    } else {

      // Something was hit
      Point hitPoint;
      Spectrum surfaceColor;
      Normal N, shadeN;

      if (engine->GetHitPointInformation(ss, &eyePath->ray, rayHit, hitPoint, surfaceColor,
          N, shadeN))
        continue;

      // Get the material
      const unsigned int currentTriangleIndex = rayHit->index;
      const unsigned int currentMeshIndex = ss->meshIDs[currentTriangleIndex];

      const uint materialIndex = ss->meshMats[currentMeshIndex];

      POINTERFREESCENE::Material *hitPointMat = &ss->materials[materialIndex];

      uint matType = hitPointMat->type;

      if (matType == MAT_AREALIGHT) {

        // Add an hit point
        //HitPointInfo &hp = *(engine->GetHitPointInfo(
        //    eyePath->pixelIndex));
        HitPointStaticInfo &hp = hitPointsStaticInfo_iterationCopy[eyePath->sampleIndex];

        hp.type = CONSTANT_COLOR;
        hp.scrX = eyePath->scrX;
        hp.scrY = eyePath->scrY;
        //ihp.accumPhotonCount = 0;
        //ihp.accumReflectedFlux = Spectrum();
        //ihp.photonCount = 0;
        //hp.reflectedFlux = Spectrum();

        Vector md = -eyePath->ray.d;
        ss->AreaLight_Le(&hitPointMat->param.areaLight, &md, &N,
            &hp.throughput);
        hp.throughput *= eyePath->throughput;

        // Free the eye path
        eyePath->done = true;

        //--todoEyePathCount;

      } else {

        Vector wo = -eyePath->ray.d;
        float materialPdf;



        Vector wi;
        bool specularMaterial = true;
        float u0 = getFloatRNG(seedBuffer[eyePath->sampleIndex]);
        float u1 = getFloatRNG(seedBuffer[eyePath->sampleIndex]);
        float u2 = getFloatRNG(seedBuffer[eyePath->sampleIndex]);
        Spectrum f;

        switch (matType) {

        case MAT_MATTE:
          ss->Matte_Sample_f(&hitPointMat->param.matte, &wo, &wi, &materialPdf, &f,
              &shadeN, u0, u1, &specularMaterial);
          f *= surfaceColor;
          break;

        case MAT_MIRROR:
          ss->Mirror_Sample_f(&hitPointMat->param.mirror, &wo, &wi, &materialPdf, &f,
              &shadeN, &specularMaterial);
          f *= surfaceColor;
          break;

        case MAT_GLASS:
          ss->Glass_Sample_f(&hitPointMat->param.glass, &wo, &wi, &materialPdf, &f, &N,
              &shadeN, u0, &specularMaterial);
          f *= surfaceColor;

          break;

        case MAT_MATTEMIRROR:
          ss->MatteMirror_Sample_f(&hitPointMat->param.matteMirror, &wo, &wi,
              &materialPdf, &f, &shadeN, u0, u1, u2, &specularMaterial);
          f *= surfaceColor;

          break;

        case MAT_METAL:
          ss->Metal_Sample_f(&hitPointMat->param.metal, &wo, &wi, &materialPdf, &f,
              &shadeN, u0, u1, &specularMaterial);
          f *= surfaceColor;

          break;

        case MAT_MATTEMETAL:
          ss->MatteMetal_Sample_f(&hitPointMat->param.matteMetal, &wo, &wi, &materialPdf,
              &f, &shadeN, u0, u1, u2, &specularMaterial);
          f *= surfaceColor;

          break;

        case MAT_ALLOY:
          ss->Alloy_Sample_f(&hitPointMat->param.alloy, &wo, &wi, &materialPdf, &f,
              &shadeN, u0, u1, u2, &specularMaterial);
          f *= surfaceColor;

          break;

        case MAT_ARCHGLASS:
          ss->ArchGlass_Sample_f(&hitPointMat->param.archGlass, &wo, &wi, &materialPdf,
              &f, &N, &shadeN, u0, &specularMaterial);
          f *= surfaceColor;

          break;

        case MAT_NULL:
          wi = eyePath->ray.d;
          specularMaterial = 1;
          materialPdf = 1.f;

          // I have also to restore the original throughput
          //throughput = prevThroughput;
          break;

        default:
          // Huston, we have a problem...
          specularMaterial = 1;
          materialPdf = 0.f;
          break;

        }

        //            if (f.r != f2.r || f.g != f2.g || f.b != f2.b) {
        //              printf("d");
        //            }

        if ((materialPdf <= 0.f) || f.Black()) {

          // Add an hit point
          //HitPointInfo &hp = *(engine->GetHitPointInfo(
          //    eyePath->pixelIndex));
          HitPointStaticInfo &hp = hitPointsStaticInfo_iterationCopy[eyePath->sampleIndex];
          hp.type = CONSTANT_COLOR;
          hp.scrX = eyePath->scrX;
          hp.scrY = eyePath->scrY;
          hp.throughput = Spectrum();
          //ihp.accumPhotonCount = 0;
          //ihp.accumReflectedFlux = Spectrum();
          //ihp.photonCount = 0;
          //hp.reflectedFlux = Spectrum();
          // Free the eye path
          eyePath->done = true;

          //--todoEyePathCount;
        } else if (specularMaterial || (!hitPointMat->difuse)) {

          eyePath->throughput *= f / materialPdf;
          eyePath->ray = Ray(hitPoint, wi);
        } else {
          // Add an hit point
          //HitPointInfo &hp = *(engine->GetHitPointInfo(
          //    eyePath->pixelIndex));
          HitPointStaticInfo &hp = hitPointsStaticInfo_iterationCopy[eyePath->sampleIndex];
          hp.type = SURFACE;
          hp.scrX = eyePath->scrX;
          hp.scrY = eyePath->scrY;
          //hp.material
          //    = (SurfaceMaterial *) triMat;
          //ihp.accumPhotonCount = 0;
          //ihp.accumReflectedFlux = Spectrum();
          //ihp.photonCount = 0;
          //hp.reflectedFlux = Spectrum();
          hp.materialSS = materialIndex;

          hp.throughput = eyePath->throughput * surfaceColor;
          hp.position = hitPoint;
          hp.wo = -eyePath->ray.d;
          hp.normal = shadeN;

          // Free the eye path
          eyePath->done = true;

          //--todoEyePathCount;
        }

      }

    }
  }

}
Example #13
0
void NativeFilm::UpdateScreenBuffer() {
	switch (toneMapParams->GetType()) {
		case TONEMAP_LINEAR: {
			const LinearToneMapParams &tm = (LinearToneMapParams &)(*toneMapParams);
			const SamplePixel *sp = sampleFrameBuffer->GetPixels();
			Pixel *p = frameBuffer->GetPixels();
			const unsigned int pixelCount = width * height;
			const float perScreenNormalizationFactor = tm.scale / (float)statsTotalSampleCount;

			for (unsigned int i = 0; i < pixelCount; ++i) {
				const float weight = sp[i].weight;

				if (weight > 0.f) {
					if (usePerScreenNormalization) {
						p[i].r = Radiance2PixelFloat(sp[i].radiance.r * perScreenNormalizationFactor);
						p[i].g = Radiance2PixelFloat(sp[i].radiance.g * perScreenNormalizationFactor);
						p[i].b = Radiance2PixelFloat(sp[i].radiance.b * perScreenNormalizationFactor);						
					} else {
						const float invWeight = tm.scale / weight;

						p[i].r = Radiance2PixelFloat(sp[i].radiance.r * invWeight);
						p[i].g = Radiance2PixelFloat(sp[i].radiance.g * invWeight);
						p[i].b = Radiance2PixelFloat(sp[i].radiance.b * invWeight);
					}
				} else {
					p[i].r = 0.f;
					p[i].g = 0.f;
					p[i].b = 0.f;
				}
			}
			break;
		}
		case TONEMAP_REINHARD02: {
			const Reinhard02ToneMapParams &tm = (Reinhard02ToneMapParams &)(*toneMapParams);

			const float alpha = .1f;
			const float preScale = tm.preScale;
			const float postScale = tm.postScale;
			const float burn = tm.burn;

			const SamplePixel *sp = sampleFrameBuffer->GetPixels();
			Pixel *p = frameBuffer->GetPixels();
			const unsigned int pixelCount = width * height;
			const float perScreenNormalizationFactor = 1.f / (float)statsTotalSampleCount;

			// Use the frame buffer as temporary storage and calculate the average luminance
			float Ywa = 0.f;

			for (unsigned int i = 0; i < pixelCount; ++i) {
				const float weight = sp[i].weight;
				Spectrum rgb = sp[i].radiance;

				if ((weight > 0.f) && !rgb.IsNaN()) {
					if (usePerScreenNormalization)
						rgb *= perScreenNormalizationFactor;
					else
						rgb /= weight;

					// Convert to XYZ color space
					p[i].r = 0.412453f * rgb.r + 0.357580f * rgb.g + 0.180423f * rgb.b;
					p[i].g = 0.212671f * rgb.r + 0.715160f * rgb.g + 0.072169f * rgb.b;
					p[i].b = 0.019334f * rgb.r + 0.119193f * rgb.g + 0.950227f * rgb.b;

					Ywa += p[i].g;
				} else {
					p[i].r = 0.f;
					p[i].g = 0.f;
					p[i].b = 0.f;
				}
			}
			Ywa /= pixelCount;

			// Avoid division by zero
			if (Ywa == 0.f)
				Ywa = 1.f;

			const float Yw = preScale * alpha * burn;
			const float invY2 = 1.f / (Yw * Yw);
			const float pScale = postScale * preScale * alpha / Ywa;

			for (unsigned int i = 0; i < pixelCount; ++i) {
				Spectrum xyz = p[i];

				const float ys = xyz.g;
				xyz *= pScale * (1.f + ys * invY2) / (1.f + ys);

				// Convert back to RGB color space
				p[i].r =  3.240479f * xyz.r - 1.537150f * xyz.g - 0.498535f * xyz.b;
				p[i].g = -0.969256f * xyz.r + 1.875991f * xyz.g + 0.041556f * xyz.b;
				p[i].b =  0.055648f * xyz.r - 0.204043f * xyz.g + 1.057311f * xyz.b;

				// Gamma correction
				p[i].r = Radiance2PixelFloat(p[i].r);
				p[i].g = Radiance2PixelFloat(p[i].g);
				p[i].b = Radiance2PixelFloat(p[i].b);
			}
			break;
		}
		default:
			assert (false);
			break;
	}
}
Example #14
0
void VolumePatIntegrator::EyeRandomWalk(const Scene *scene, const Ray &eyeRay,
        VolumeVertexList& vertexList, RNG &rng) const {
    // Do a random walk for the eye ray in the volume
    Spectrum cummulative(1.f);

    // Find the intersection between the eye ray and the volume
    VolumeRegion *vr = scene->volumeRegion;
    float t0, t1;
    if (!vr || !vr->IntersectP(eyeRay, &t0, &t1) || (t1-t0) == 0.f || t0 < 0.f) {
        return;
    }

    // Find the intersection point between the sampled light ray and the volume
    RayDifferential ray(eyeRay);
    Point p = ray(t0), pPrev;
    uint64_t bounces = 0;
    while(vr->WorldBound().Inside(p)) {
        Vector wi = -ray.d;
        const Spectrum sigma_a = vr->Sigma_a(p, wi, eyeRay.time);
        const Spectrum sigma_s = vr->Sigma_s(p, wi, eyeRay.time);
        const Spectrum STER = vr->STER(p, wi, eyeRay.time);
        // Construct and add the _eyeVertex_ to the _vertexList_
        VolumeVertex eyeVertex(p, wi, sigma_a, sigma_s, cummulative, 1.0);
        vertexList.push_back(eyeVertex);

        // Sample the direction of the next event
        float directionPdf = 1.f;
        Vector wo;
        if(STER.y() > rng.RandomFloat()) {
            // Change the ray direction due to a scattering event at _p_
            if(!vr->SampleDirection(p, wi, wo, &directionPdf, rng)) {
                break; // Direction error
            }

            // Account for the losses due to the scattering event at _p_
            cummulative *= sigma_s * vr->p(p, wi, wo, ray.time);
        } else {
            // Account only for the trnsmittance between the previous and the
            // next events becuse there is no direction change.
            wo = ray.d;
        }

        // Sample the distance of the next event
        ray = RayDifferential(p, wo, 0, INFINITY);

        float tDist;
        float distancePdf = 1.f;
        Point Psample;
        if(!vr->SampleDistance(ray, &tDist, Psample, &distancePdf, rng)) {
            break; // The sampled point is outside the volume
        }

        // Account for the sampling Pdfs from sampling a direction and/or distance
        const float pdf = distancePdf * directionPdf;
        cummulative *= 1 / pdf;

        // Update the events and account for the transmittance between the events
        pPrev = p;
        p = Psample;
        const Ray tauRay(pPrev, p - pPrev, 0.f, 1.f, ray.time, ray.depth);
        const Spectrum stepTau = vr->tau(tauRay, .5f * stepSize, rng.RandomFloat());
        const Spectrum TrPP = Exp(-stepTau);
        cummulative *= TrPP;

        // Possibly terminate ray marching if _cummulative_ is small
        if (cummulative.y() < 1e-3) {
            const float continueProb = .5f;
            if (rng.RandomFloat() > continueProb) {
                cummulative = 0.f;
                break;
            }
            cummulative /= continueProb;
        }

        // Terminate if bounces are more than requested
        bounces++;
        if (bounces > maxDepth) {
            break;
        }
    }
}
Example #15
0
Spectrum PathIntegrator::Li(const Scene *scene, const Renderer *renderer,
        const RayDifferential &r, const Intersection &isect,
        const Sample *sample, RNG &rng, MemoryArena &arena) const {
    // Declare common path integration variables
    Spectrum pathThroughput = 1., L = 0.;
    RayDifferential ray(r);
    bool specularBounce = false;
    Intersection localIsect;
    const Intersection *isectp = &isect;
    for (int bounces = 0; ; ++bounces) {
        // Possibly add emitted light at path vertex
        if (bounces == 0 || specularBounce)
            L += pathThroughput * isectp->Le(-ray.d);

        // Sample illumination from lights to find path contribution
        BSDF *bsdf = isectp->GetBSDF(ray, arena);
        const Point &p = bsdf->dgShading.p;
        const Normal &n = bsdf->dgShading.nn;
        Vector wo = -ray.d;
        if (bounces < SAMPLE_DEPTH)
            L += pathThroughput *
                 UniformSampleOneLight(scene, renderer, arena, p, n, wo,
                     isectp->rayEpsilon, ray.time, bsdf, sample, rng,
                     lightNumOffset[bounces], &lightSampleOffsets[bounces],
                     &bsdfSampleOffsets[bounces]);
        else
            L += pathThroughput *
                 UniformSampleOneLight(scene, renderer, arena, p, n, wo,
                     isectp->rayEpsilon, ray.time, bsdf, sample, rng);

        // Sample BSDF to get new path direction

        // Get _outgoingBSDFSample_ for sampling new path direction
        BSDFSample outgoingBSDFSample;
        if (bounces < SAMPLE_DEPTH)
            outgoingBSDFSample = BSDFSample(sample, pathSampleOffsets[bounces], 0);
        else
            outgoingBSDFSample = BSDFSample(rng);
        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;
        pathThroughput *= f * AbsDot(wi, n) / pdf;
        ray = RayDifferential(p, wi, ray, isectp->rayEpsilon);

        // Possibly terminate the path
        if (bounces > 3) {
            float continueProbability = min(.5f, pathThroughput.y());
            if (rng.RandomFloat() > continueProbability)
                break;
            pathThroughput /= continueProbability;
        }
        if (bounces == maxDepth)
            break;

        // Find next vertex of path
        if (!scene->Intersect(ray, &localIsect)) {
            if (specularBounce)
                for (uint32_t i = 0; i < scene->lights.size(); ++i)
                   L += pathThroughput * scene->lights[i]->Le(ray);
            break;
        }
        if (bounces > 1)
            pathThroughput *= renderer->Transmittance(scene, ray, NULL, rng, arena);
        isectp = &localIsect;
    }
    return L;
}
	Spectrum Li(const RayDifferential &r, RadianceQueryRecord &rRec) const {
		/* Some aliases and local variables */
		const Scene *scene = rRec.scene;
		Intersection &its = rRec.its;
		MediumSamplingRecord mRec;
		RayDifferential ray(r);
		Spectrum Li(0.0f);
		bool nullChain = true, scattered = false;
		Float eta = 1.0f;

		/* Perform the first ray intersection (or ignore if the
		   intersection has already been provided). */
		rRec.rayIntersect(ray);
		Spectrum throughput(1.0f);

		if (m_maxDepth == 1)
			rRec.type &= RadianceQueryRecord::EEmittedRadiance;

		/**
		 * Note: the logic regarding maximum path depth may appear a bit
		 * strange. This is necessary to get this integrator's output to
		 * exactly match the output of other integrators under all settings
		 * of this parameter.
		 */
		while (rRec.depth <= m_maxDepth || m_maxDepth < 0) {
			/* ==================================================================== */
			/*                 Radiative Transfer Equation sampling                 */
			/* ==================================================================== */
			if (rRec.medium && rRec.medium->sampleDistance(Ray(ray, 0, its.t), mRec, rRec.sampler)) {
				/* Sample the integral
				   \int_x^y tau(x, x') [ \sigma_s \int_{S^2} \rho(\omega,\omega') L(x,\omega') d\omega' ] dx'
				*/
				const PhaseFunction *phase = rRec.medium->getPhaseFunction();

				throughput *= mRec.sigmaS * mRec.transmittance / mRec.pdfSuccess;

				/* ==================================================================== */
				/*                     Direct illumination sampling                     */
				/* ==================================================================== */

				/* Estimate the single scattering component if this is requested */
				if (rRec.type & RadianceQueryRecord::EDirectMediumRadiance) {
					DirectSamplingRecord dRec(mRec.p, mRec.time);
					int maxInteractions = m_maxDepth - rRec.depth - 1;

					Spectrum value = scene->sampleAttenuatedEmitterDirect(
							dRec, rRec.medium, maxInteractions,
							rRec.nextSample2D(), rRec.sampler);

					if (!value.isZero())
						Li += throughput * value * phase->eval(
								PhaseFunctionSamplingRecord(mRec, -ray.d, dRec.d));
				}

				/* Stop if multiple scattering was not requested, or if the path gets too long */
				if ((rRec.depth + 1 >= m_maxDepth && m_maxDepth > 0) ||
					!(rRec.type & RadianceQueryRecord::EIndirectMediumRadiance))
					break;

				/* ==================================================================== */
				/*             Phase function sampling / Multiple scattering            */
				/* ==================================================================== */

				PhaseFunctionSamplingRecord pRec(mRec, -ray.d);
				Float phaseVal = phase->sample(pRec, rRec.sampler);
				if (phaseVal == 0)
					break;
				throughput *= phaseVal;

				/* Trace a ray in this direction */
				ray = Ray(mRec.p, pRec.wo, ray.time);
				ray.mint = 0;
				scene->rayIntersect(ray, its);
				nullChain = false;
				scattered = true;
			} else {
				/* Sample
					tau(x, y) * (Surface integral). This happens with probability mRec.pdfFailure
					Account for this and multiply by the proper per-color-channel transmittance.
				*/

				if (rRec.medium)
					throughput *= mRec.transmittance / mRec.pdfFailure;

				if (!its.isValid()) {
					/* If no intersection could be found, possibly return
					   attenuated radiance from a background luminaire */
					if ((rRec.type & RadianceQueryRecord::EEmittedRadiance)
						&& (!m_hideEmitters || scattered)) {
						Spectrum value = throughput * scene->evalEnvironment(ray);
						if (rRec.medium)
							value *= rRec.medium->evalTransmittance(ray);
						Li += value;
					}
					break;
				}

				/* Possibly include emitted radiance if requested */
				if (its.isEmitter() && (rRec.type & RadianceQueryRecord::EEmittedRadiance)
					&& (!m_hideEmitters || scattered))
					Li += throughput * its.Le(-ray.d);

				/* Include radiance from a subsurface integrator if requested */
				if (its.hasSubsurface() && (rRec.type & RadianceQueryRecord::ESubsurfaceRadiance))
					Li += throughput * its.LoSub(scene, rRec.sampler, -ray.d, rRec.depth);

				/* Prevent light leaks due to the use of shading normals */
				Float wiDotGeoN = -dot(its.geoFrame.n, ray.d),
					  wiDotShN  = Frame::cosTheta(its.wi);
				if (m_strictNormals && wiDotGeoN * wiDotShN < 0)
					break;

				/* ==================================================================== */
				/*                     Direct illumination sampling                     */
				/* ==================================================================== */

				const BSDF *bsdf = its.getBSDF(ray);

				/* Estimate the direct illumination if this is requested */
				if (rRec.type & RadianceQueryRecord::EDirectSurfaceRadiance &&
						(bsdf->getType() & BSDF::ESmooth)) {
					DirectSamplingRecord dRec(its);
					int maxInteractions = m_maxDepth - rRec.depth - 1;

					Spectrum value = scene->sampleAttenuatedEmitterDirect(
							dRec, its, rRec.medium, maxInteractions,
							rRec.nextSample2D(), rRec.sampler);

					if (!value.isZero()) {
						/* Allocate a record for querying the BSDF */
						BSDFSamplingRecord bRec(its, its.toLocal(dRec.d));
						bRec.sampler = rRec.sampler;

						Float woDotGeoN = dot(its.geoFrame.n, dRec.d);
						/* Prevent light leaks due to the use of shading normals */
						if (!m_strictNormals ||
							woDotGeoN * Frame::cosTheta(bRec.wo) > 0)
							Li += throughput * value * bsdf->eval(bRec);
					}
				}

				/* ==================================================================== */
				/*                   BSDF sampling / Multiple scattering                */
				/* ==================================================================== */

				/* Sample BSDF * cos(theta) */
				BSDFSamplingRecord bRec(its, rRec.sampler, ERadiance);
				Spectrum bsdfVal = bsdf->sample(bRec, rRec.nextSample2D());
				if (bsdfVal.isZero())
					break;

				/* Recursively gather indirect illumination? */
				int recursiveType = 0;
				if ((rRec.depth + 1 < m_maxDepth || m_maxDepth < 0) &&
					(rRec.type & RadianceQueryRecord::EIndirectSurfaceRadiance))
					recursiveType |= RadianceQueryRecord::ERadianceNoEmission;

				/* Recursively gather direct illumination? This is a bit more
				   complicated by the fact that this integrator can create connection
				   through index-matched medium transitions (ENull scattering events) */
				if ((rRec.depth < m_maxDepth || m_maxDepth < 0) &&
					(rRec.type & RadianceQueryRecord::EDirectSurfaceRadiance) &&
					(bRec.sampledType & BSDF::EDelta) &&
					(!(bRec.sampledType & BSDF::ENull) || nullChain)) {
					recursiveType |= RadianceQueryRecord::EEmittedRadiance;
					nullChain = true;
				} else {
					nullChain &= bRec.sampledType == BSDF::ENull;
				}

				/* Potentially stop the recursion if there is nothing more to do */
				if (recursiveType == 0)
					break;
				rRec.type = recursiveType;

				/* Prevent light leaks due to the use of shading normals */
				const Vector wo = its.toWorld(bRec.wo);
				Float woDotGeoN = dot(its.geoFrame.n, wo);
				if (woDotGeoN * Frame::cosTheta(bRec.wo) <= 0 && m_strictNormals)
					break;

				/* Keep track of the throughput, medium, and relative
				   refractive index along the path */
				throughput *= bsdfVal;
				eta *= bRec.eta;
				if (its.isMediumTransition())
					rRec.medium = its.getTargetMedium(wo);

				/* In the next iteration, trace a ray in this direction */
				ray = Ray(its.p, wo, ray.time);
				scene->rayIntersect(ray, its);
				scattered |= bRec.sampledType != BSDF::ENull;
			}

			if (rRec.depth++ >= m_rrDepth) {
				/* Russian roulette: try to keep path weights equal to one,
				   while accounting for the solid angle compression at refractive
				   index boundaries. Stop with at least some probability to avoid
				   getting stuck (e.g. due to total internal reflection) */

				Float q = std::min(throughput.max() * eta * eta, (Float) 0.95f);
				if (rRec.nextSample1D() >= q)
					break;
				throughput /= q;
			}
		}
		avgPathLength.incrementBase();
		avgPathLength += rRec.depth;
		return Li;
	}
Example #17
0
void DipoleSubsurfaceIntegrator::Preprocess(const Scene *scene,
        const Camera *camera, const Renderer *renderer) {
    if (scene->lights.size() == 0) return;
    vector<SurfacePoint> pts;
    // Get _SurfacePoint_s for translucent objects in scene
    if (filename != "") {
        // Initialize _SurfacePoint_s from file
        vector<float> fpts;
        if (ReadFloatFile(filename.c_str(), &fpts)) {
            if ((fpts.size() % 8) != 0)
                Error("Excess values (%d) in points file \"%s\"", int(fpts.size() % 8),
                      filename.c_str());
            for (u_int i = 0; i < fpts.size(); i += 8)
                pts.push_back(SurfacePoint(Point(fpts[i], fpts[i+1], fpts[i+2]),
                                           Normal(fpts[i+3], fpts[i+4], fpts[i+5]),
                                           fpts[i+6], fpts[i+7]));
        }
    }
    if (pts.size() == 0) {
        Point pCamera = camera->CameraToWorld(camera->shutterOpen,
                                              Point(0, 0, 0));
        FindPoissonPointDistribution(pCamera, camera->shutterOpen,
                                     minSampleDist, scene, &pts);
    }

    // Compute irradiance values at sample points
    RNG rng;
    MemoryArena arena;
    PBRT_SUBSURFACE_STARTED_COMPUTING_IRRADIANCE_VALUES();
    ProgressReporter progress(pts.size(), "Computing Irradiances");
    for (uint32_t i = 0; i < pts.size(); ++i) {
        SurfacePoint &sp = pts[i];
        Spectrum E(0.f);
        for (uint32_t j = 0; j < scene->lights.size(); ++j) {
            // Add irradiance from light at point
            const Light *light = scene->lights[j];
            Spectrum Elight = 0.f;
            int nSamples = RoundUpPow2(light->nSamples);
            uint32_t scramble[2] = { rng.RandomUInt(), rng.RandomUInt() };
            uint32_t compScramble = rng.RandomUInt();
            for (int s = 0; s < nSamples; ++s) {
                float lpos[2];
                Sample02(s, scramble, lpos);
                float lcomp = VanDerCorput(s, compScramble);
                LightSample ls(lpos[0], lpos[1], lcomp);
                Vector wi;
                float lightPdf;
                VisibilityTester visibility;
                Spectrum Li = light->Sample_L(sp.p, sp.rayEpsilon,
                                              ls, camera->shutterOpen, &wi, &lightPdf, &visibility);
                if (Dot(wi, sp.n) <= 0.) continue;
                if (Li.IsBlack() || lightPdf == 0.f) continue;
                Li *= visibility.Transmittance(scene, renderer, NULL, rng, arena);
                if (visibility.Unoccluded(scene))
                    Elight += Li * AbsDot(wi, sp.n) / lightPdf;
            }
            E += Elight / nSamples;
        }
        irradiancePoints.push_back(IrradiancePoint(sp, E));
        PBRT_SUBSURFACE_COMPUTED_IRRADIANCE_AT_POINT(&sp, &E);
        arena.FreeAll();
        progress.Update();
    }
    progress.Done();
    PBRT_SUBSURFACE_FINISHED_COMPUTING_IRRADIANCE_VALUES();

    // Create octree of clustered irradiance samples
    octree = octreeArena.Alloc<SubsurfaceOctreeNode>();
    for (uint32_t i = 0; i < irradiancePoints.size(); ++i)
        octreeBounds = Union(octreeBounds, irradiancePoints[i].p);
    for (uint32_t i = 0; i < irradiancePoints.size(); ++i)
        octree->Insert(octreeBounds, &irradiancePoints[i], octreeArena);
    octree->InitHierarchy();
}
Example #18
0
// PathIntegrator Method Definitions
Spectrum PathIntegrator::Li(const RayDifferential &r, const Scene &scene,
                            Sampler &sampler, MemoryArena &arena,
                            int depth) const {
    ProfilePhase p(Prof::SamplerIntegratorLi);
    Spectrum L(0.f), beta(1.f);
    RayDifferential ray(r);
    bool specularBounce = false;
    for (int bounces = 0;; ++bounces) {
        // Find next path vertex and accumulate contribution

        // Intersect _ray_ with scene and store intersection in _isect_
        SurfaceInteraction isect;
        bool foundIntersection = scene.Intersect(ray, &isect);

        // Possibly add emitted light at intersection
        if (bounces == 0 || specularBounce) {
            // Add emitted light at path vertex or from the environment
            if (foundIntersection)
                L += beta * isect.Le(-ray.d);
            else
                for (const auto &light : scene.lights)
                    L += beta * light->Le(ray);
        }

        // Terminate path if ray escaped or _maxDepth_ was reached
        if (!foundIntersection || bounces >= maxDepth) break;

        // Compute scattering functions and skip over medium boundaries
        isect.ComputeScatteringFunctions(ray, arena, true);
        if (!isect.bsdf) {
            ray = isect.SpawnRay(ray.d);
            bounces--;
            continue;
        }

        // Sample illumination from lights to find path contribution
        L += beta * UniformSampleOneLight(isect, scene, arena, sampler);

        // Sample BSDF to get new path direction
        Vector3f wo = -ray.d, wi;
        Float pdf;
        BxDFType flags;
        Spectrum f = isect.bsdf->Sample_f(wo, &wi, sampler.Get2D(), &pdf,
                                          BSDF_ALL, &flags);
        if (f.IsBlack() || pdf == 0.f) break;
        beta *= f * AbsDot(wi, isect.shading.n) / pdf;
        Assert(std::isinf(beta.y()) == false);
        specularBounce = (flags & BSDF_SPECULAR) != 0;
        ray = isect.SpawnRay(wi);

        // Account for subsurface scattering, if applicable
        if (isect.bssrdf && (flags & BSDF_TRANSMISSION)) {
            // Importance sample the BSSRDF
            SurfaceInteraction pi;
            Spectrum S = isect.bssrdf->Sample_S(
                scene, sampler.Get1D(), sampler.Get2D(), arena, &pi, &pdf);
#ifndef NDEBUG
            Assert(std::isinf(beta.y()) == false);
#endif
            if (S.IsBlack() || pdf == 0) break;
            beta *= S / pdf;

            // Account for the direct subsurface scattering component
            L += beta * UniformSampleOneLight(pi, scene, arena, sampler);

            // Account for the indirect subsurface scattering component
            Spectrum f = pi.bsdf->Sample_f(pi.wo, &wi, sampler.Get2D(), &pdf,
                                           BSDF_ALL, &flags);
            if (f.IsBlack() || pdf == 0) break;
            beta *= f * AbsDot(wi, pi.shading.n) / pdf;
#ifndef NDEBUG
            Assert(std::isinf(beta.y()) == false);
#endif
            specularBounce = (flags & BSDF_SPECULAR) != 0;
            ray = pi.SpawnRay(wi);
        }

        // Possibly terminate the path with Russian roulette
        if (bounces > 3) {
            Float continueProbability = std::min((Float).95, beta.y());
            if (sampler.Get1D() > continueProbability) break;
            beta /= continueProbability;
            Assert(std::isinf(beta.y()) == false);
        }
    }
    return L;
}
Example #19
0
void compute_ibl_environment_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               env_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);

    // todo: if we had a way to know that a BSDF is purely specular, we could
    // immediately return black here since there will be no contribution from
    // such a BSDF.

    sampling_context.split_in_place(2, env_sample_count);

    for (size_t i = 0; i < env_sample_count; ++i)
    {
        // Generate a uniform sample in [0,1)^2.
        const Vector2d s = sampling_context.next_vector2<2>();

        // Sample the environment.
        InputEvaluator input_evaluator(shading_context.get_texture_cache());
        Vector3d incoming;
        Spectrum env_value;
        double env_prob;
        environment_edf.sample(
            input_evaluator,
            s,
            incoming,
            env_value,
            env_prob);

        // Cull samples behind the shading surface.
        assert(is_normalized(incoming));
        const double cos_in = dot(incoming, shading_basis.get_normal());
        if (cos_in < 0.0)
            continue;

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

        // Evaluate the BSDF.
        Spectrum bsdf_value;
        const double bsdf_prob =
            bsdf.evaluate(
                bsdf_data,
                false,                          // not adjoint
                true,                           // multiply by |cos(incoming, normal)|
                geometric_normal,
                shading_basis,
                outgoing,
                incoming,
                env_sampling_modes,
                bsdf_value);
        if (bsdf_prob == 0.0)
            continue;

        // Compute MIS weight.
        const double mis_weight =
            mis_power2(
                env_sample_count * env_prob,
                bsdf_sample_count * bsdf_prob);

        // Add the contribution of this sample to the illumination.
        env_value *= static_cast<float>(transmission / env_prob * mis_weight);
        env_value *= bsdf_value;
        radiance += env_value;
    }

    if (env_sample_count > 1)
        radiance /= static_cast<float>(env_sample_count);
}
Example #20
0
 float fr(const vec3& wo, vec3& wi, float wavelen,
          float surrounding_refractive_index, float& pdf, float u1,
          float u2) const
 {
   return reflectance.sample(wavelen) * brdf_lambertian(wo, wi, pdf, u1, u2);
 }
Example #21
0
bool spectrumHasBinaryData(const Spectrum& s)
{
    return s.hasBinaryData();
}
Example #22
0
Spectrum GlossyPRTIntegrator::Li(const Scene *scene, const Renderer *,
        const RayDifferential &ray, const Intersection &isect,
        const Sample *sample, RNG &rng, MemoryArena &arena) const {
    Spectrum L = 0.f;
    Vector wo = -ray.d;
    // Compute emitted light if ray hit an area light source
    L += isect.Le(wo);

    // Evaluate BSDF at hit point
    BSDF *bsdf = isect.GetBSDF(ray, arena);
    const Point &p = bsdf->dgShading.p;
    // Compute reflected radiance with glossy PRT at point

    // Compute SH radiance transfer matrix at point and SH coefficients
    Spectrum *c_t = arena.Alloc<Spectrum>(SHTerms(lmax));
    Spectrum *T = arena.Alloc<Spectrum>(SHTerms(lmax)*SHTerms(lmax));
    SHComputeTransferMatrix(p, isect.rayEpsilon, scene, rng, nSamples,
                            lmax, T);
    SHMatrixVectorMultiply(T, c_in, c_t, lmax);

    // Rotate incident SH lighting to local coordinate frame
    Vector r1 = bsdf->LocalToWorld(Vector(1,0,0));
    Vector r2 = bsdf->LocalToWorld(Vector(0,1,0));
    Normal nl = Normal(bsdf->LocalToWorld(Vector(0,0,1)));
    Matrix4x4 rot(r1.x, r2.x, nl.x, 0,
                  r1.y, r2.y, nl.y, 0,
                  r1.z, r2.z, nl.z, 0,
                     0,    0,    0, 1);
    Spectrum *c_l = arena.Alloc<Spectrum>(SHTerms(lmax));
    SHRotate(c_t, c_l, rot, lmax, arena);
    #if 0

    // Sample BSDF and integrate against direct SH coefficients
    float *Ylm = ALLOCA(float, SHTerms(lmax));
    int ns = 1024;
    for (int i = 0; i < ns; ++i) {
        Vector wi;
        float pdf;
        Spectrum f = bsdf->Sample_f(wo, &wi, BSDFSample(rng), &pdf);
        if (pdf > 0.f && !f.IsBlack() && !scene->IntersectP(Ray(p, wi))) {
            f *= fabsf(Dot(wi, n)) / (pdf * ns);
            SHEvaluate(bsdf->WorldToLocal(wi), lmax, Ylm);
    
            Spectrum Li = 0.f;
            for (int j = 0; j < SHTerms(lmax); ++j)
                Li += Ylm[j] * c_l[j] * f;
            L += Li.Clamp();
        }
    }
    #else

    // Compute final coefficients _c\_o_ using BSDF matrix
    Spectrum *c_o = arena.Alloc<Spectrum>(SHTerms(lmax));
    SHMatrixVectorMultiply(B, c_l, c_o, lmax);

    // Evaluate outgoing radiance function for $\wo$ and add to _L_
    Vector woLocal = bsdf->WorldToLocal(wo);
    float *Ylm = ALLOCA(float, SHTerms(lmax));
    SHEvaluate(woLocal, lmax, Ylm);
    Spectrum Li = 0.f;
    for (int i = 0; i < SHTerms(lmax); ++i)
        Li += Ylm[i] * c_o[i];
    L += Li.Clamp();
    #endif
    return L;
}
Example #23
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_
            LightSample ls(u[1], u[2], u[3]);
            LightInfo2 li = light->Sample_L(scene, ls, u[4], u[5], time);
            Spectrum Le(li.L);
            RayDifferential photonRay(li.ray);
            Normal Nl(li.N);
            if (li.pdf == 0.f || Le.IsBlack()) continue;
            Spectrum alpha = (AbsDot(Nl, photonRay.d) * Le) / (li.pdf * lightPdf);
            if (!alpha.IsBlack()) {
                // Follow photon path through scene and record intersections
                PBRT_PHOTON_MAP_STARTED_RAY_PATH(&photonRay, &alpha);
                bool specularPath = true;
                int nIntersections = 0;
                auto optPhotonIsect = scene.Intersect(photonRay);
                while (optPhotonIsect) {
                    ++nIntersections;
                    // Handle photon/surface intersection
                    alpha *= renderer->Transmittance(scene, photonRay, NULL, rng, arena);
                    BSDF *photonBSDF = optPhotonIsect->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(optPhotonIsect->dg.p, alpha, wo);
                        bool depositedPhoton = false;
                        if (specularPath && nIntersections > 1) {
                            if (!causticDone) {
                                PBRT_PHOTON_MAP_DEPOSITED_CAUSTIC_PHOTON(&optPhotonIsect->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(&optPhotonIsect->dg, &alpha, &wo);
                                depositedPhoton = true;
                                localDirectPhotons.push_back(photon);
                            }
                            else if (nIntersections > 1 && !indirectDone) {
                                PBRT_PHOTON_MAP_DEPOSITED_INDIRECT_PHOTON(&optPhotonIsect->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 = optPhotonIsect->dg.nn;
                            n = Faceforward(n, -photonRay.d);
                            localRadiancePhotons.push_back(RadiancePhoton(optPhotonIsect->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(optPhotonIsect->dg.p, wi, photonRay,
                                                optPhotonIsect->rayEpsilon);
                    optPhotonIsect = scene.Intersect(photonRay);
                }
                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;
    }
}
Example #24
0
MedianCutEnvironmentLight::MedianCutEnvironmentLight(const Transform &light2world,
    const Spectrum &L, int ns_, const string &texmap)
  : Light(light2world, ns_), impl(new MedCutEnvImpl {nullptr, nullptr, ns_}) {
  int width = 0, height = 0;
  RGBSpectrum *texels = NULL;
  // Read texel data from _texmap_ into _texels_
  if (texmap != "") {
    texels = ReadImage(texmap, &width, &height);
    if (texels)
      for (int i = 0; i < width * height; ++i)
        texels[i] *= L.ToRGBSpectrum();
  }
  if (!texels) {
    width = height = 1;
    texels = new RGBSpectrum[1];
    texels[0] = L.ToRGBSpectrum();
  }
  impl->radianceMap = new MIPMap<RGBSpectrum>(width, height, texels);
  impl->width = width;
  impl->inv_w = 1.0f/(width-1);
  impl->height = height;
  impl->inv_h = 1.0f/(height-1);
  impl->createDistantLight = [this](const RGBSpectrum& s, float cy, float cx) {
    ParamSet p;

    float rgb[3];
    s.ToRGB(rgb);
    p.AddRGBSpectrum("L", rgb, 3);

    const float theta = impl->inv_h*cy * M_PI
              , phi   = impl->inv_w*cx * 2.f * M_PI;
    const float costheta = cosf(theta), sintheta = sinf(theta);
    const float sinphi = sinf(phi), cosphi = cosf(phi);
    const Point wi(-sintheta*cosphi, -sintheta*sinphi, -costheta);
    p.AddPoint(string("to"), &wi, 1);

#if DEBUG >= 1
fprintf(stderr, "rgb (%f,%f,%f)\n", rgb[0], rgb[1], rgb[2]);
#endif

    return CreateDistantLight(this->LightToWorld, p);
  };

  impl->solid_angle = ((2.f * M_PI) / (width - 1)) * (M_PI / (1.f * (height - 1)));

#if DEBUG >= 1
fprintf(stderr, "solid_angle = %f\n", impl->solid_angle);
#endif

  for (int y = 0; y < height; ++y) {
    float sinTheta = sinf(M_PI * float(y + 0.5f)/height);
    for (int x = 0; x < width; ++x)
      texels[y*width + x] *= impl->solid_angle * sinTheta;
  }

  // Initialize energy sum array; the array is shifted for (1,1)
  // i.e. (0,*) and (*,0) are inserted 0-boundaries
  fprintf(stderr, "[+] [%10.2f] Initializing sum array\n", clock()*1.0/CLOCKS_PER_SEC);

#if DEBUG >= 3
for (int y = 0; y < 5; ++y) {
  for (int x = 0; x < 5; ++x) {
    fprintf(stderr, "%.2f ", texels[y*width+x].y());
  }
  fprintf(stderr, "\n");
}
fprintf(stderr, "\n");
#endif

  vector<vector<float>> acc(height+1, vector<float>(width+1));
  for (int y = 0; y < height; ++y)
    for (int x = 0; x < width; ++x)
      acc[y+1][x+1] = acc[y+1][x] + texels[y*width + x].y();

#if DEBUG >= 3
for (int y = 0; y < 5; ++y) {
  for (int x = 0; x < 5; ++x) {
    fprintf(stderr, "%.2f ", acc[y][x]);
  }
  fprintf(stderr, "\n");
}
fprintf(stderr, "\n");
#endif

   for (int x = 1; x <= width; ++x)
    for (int y = 1; y <= height; ++y)
      acc[y][x] += acc[y-1][x];

#if DEBUG >= 3
for (int y = 0; y < 5; ++y) {
  for (int x = 0; x < 5; ++x) {
    fprintf(stderr, "%.2f ", acc[y][x]);
  }
  fprintf(stderr, "\n");
}
fprintf(stderr, "\n");
#endif

 
  // initialize median cut
  printf("[+] [%10.2f] Calculating median cut\n", clock()*1.0/CLOCKS_PER_SEC);
  subdivide(this->impl, 1, texels, acc, 0, 0, width-1, height-1);
  printf("[+] [%10.2f] Done with %d lights\n", clock()*1.0/CLOCKS_PER_SEC, static_cast<int>(impl->ls.size()));

  delete[] texels;

  // Initialize sampling PDFs for environment area light
  // Compute scalar-valued image _img_ from environment map
  float filter = 1.f / max(width, height);
  float *img = new float[width*height];
  for (int v = 0; v < height; ++v) {
    float vp = (float)v / (float)height;
    float sinTheta = sinf(M_PI * float(v+.5f)/float(height));
    for (int u = 0; u < width; ++u) {
      float up = (float)u / (float)width;
      img[u+v*width] = impl->radianceMap->Lookup(up, vp, filter).y();
      img[u+v*width] *= sinTheta;
    }
  }

  // Compute sampling impl->distributions for rows and columns of image
  impl->distribution = new Distribution2D(img, width, height);
  delete[] img;
}
void LightEffectSoundSolid::renderColor(Spectrum spectrum) {
	double bassFreq = getParameter("bass freq").getValue().getDouble();
	double trebbleFreq = getParameter("trebble freq").getValue().getDouble();
	double bassBoost = getParameter("bass boost").getValue().getDouble();
	double trebbleBoost = getParameter("trebble boost").getValue().getDouble();
	double fStart = getParameter("start frequency").getValue().getDouble();
	double fEnd = getParameter("end frequency").getValue().getDouble();
	double dbScaler = getParameter("db scaler").getValue().getDouble();
	double dbFactor = getParameter("db factor").getValue().getDouble();
	double avgFactor = getParameter("average factor").getValue().getDouble();
	double changeFactor = getParameter("change factor").getValue().getDouble();
	double noiseFloor = getParameter("noise floor").getValue().getDouble();
	double avgFilterStrength = getParameter("average filter strength").getValue().getDouble();
	uint8_t minSaturation = getParameter("min saturation").getValue().getDouble()*255;
	double filterStrength = getParameter("color filter strength").getValue().getDouble();
	uint8_t threshold = getParameter("threshold").getValue().getDouble()*255;

	double r = 0., g = 0., b = 0.;
	size_t binCount = spectrum.getBinCount();

	if(bassIndex == -1) {
		for(bassIndex = 0; (bassIndex < binCount) &&
			(spectrum.getByIndex(bassIndex).getFreqCenter() <= bassFreq); ++bassIndex);
		std::cout << "Bass Index: " << bassIndex << std::endl;
		
		for(endIndex = 0; (endIndex < binCount) &&
			(spectrum.getByIndex(endIndex).getFreqCenter() <= fEnd); ++endIndex);
		std::cout << "End Index: " << endIndex << std::endl;

		prevSpectrum = spectrum;
	}

	//Compute average
	double curAvg = 0;
	for(unsigned int i = 0; i < endIndex; ++i) {
		curAvg += spectrum.getByIndex(i).getEnergy();
	}

	curAvg = 20.*std::log10(curAvg/endIndex) + noiseFloor;

	//double curAvg = spectrum.getAverageEnergyDB() + noiseFloor;

	if(curAvg < 0)
		curAvg = 0;

	avg = avg*avgFilterStrength
		+ curAvg*(1. - avgFilterStrength);

	//Scale to be applied to each bin
	double scale = 1. / dbScaler;

	for(unsigned int i = 0; i < binCount; ++i) {
		FrequencyBin& bin = spectrum.getByIndex(i);
		double f = bin.getFreqCenter();



		if(f > fEnd)
			break;

		if(f >= fStart) {
			//float hue = (f <= bassFreq) ? 40.f*std::pow((double)i/(bassIndex-1), 4.)
				//: (45.f + 240.f * (i-bassIndex) / (binCount - bassIndex - 1));
			//float hue = 240. * i / (binCount - 1);
			int yellowPoint = 11;
			
			float hue;
			if(i < yellowPoint) {
				hue = 60. * i / (yellowPoint-1);
			}
			else {
				hue = 60. + 180.*(i-yellowPoint) / (binCount - yellowPoint - 1);
			}

			Color c = Color::HSV(255.f*hue/360.f, 255, 255);

			double db = bin.getEnergyDB();
			
			FrequencyBin& prevBin = prevSpectrum.getByIndex(i);
			double change = db - prevBin.getEnergyDB();
			if(change < 0)
				change = 0;

			//Bass boost
			if(f <= bassFreq)
			db += bassBoost;

			//Trebble boost
			if(f >= trebbleFreq)
				db += trebbleBoost;
			
			//Raise by noise floor, subtract loosly-tracking average
			db += noiseFloor - avg;

			//Reject anything below the average
			if(db < 0)
				continue;

			//Scale partially based on average level
			db *= dbFactor;
			db += avgFactor*avg + changeFactor*change;

			r += db * c.getRed();
			g += db * c.getGreen();
			b += db * c.getBlue();
		}
	}

	//Scale color
	r *= scale;
	g *= scale;
	b *= scale;

	//Compute largest component
	double largest = std::max(r, std::max(g, b));

	//Use the largest value to limit the maximum brightness to 255
	if(largest > 255) {
		double scale = 255. / largest;

		r *= scale;
		g *= scale;
		b *= scale;
	}

	Color cTmp(r, g, b);
	
	uint8_t h = cTmp.getHue(), s = cTmp.getSat(), v = cTmp.getVal();

	if(v < threshold) {
		v = 0;
	}

	//Enforce saturation minimum
	cTmp = Color::HSV(h, std::max(minSaturation, s), v);

	//Filter the color
	c.filter(cTmp, filterStrength);

	//Update previous spectrum
	prevSpectrum = spectrum;
}
Example #26
0
size_t generateVPLs(const Scene *scene, Random *random,
                    size_t offset, size_t count, int maxDepth, bool prune, std::deque<VPL> &vpls) {
    if (maxDepth <= 1)
        return 0;

    static Sampler *sampler = NULL;
    if (!sampler) {
        Properties props("halton");
        props.setInteger("scramble", 0);
        sampler = static_cast<Sampler *> (PluginManager::getInstance()->
                                          createObject(MTS_CLASS(Sampler), props));
        sampler->configure();
        sampler->generate(Point2i(0));
    }

    const Sensor *sensor = scene->getSensor();
    Float time = sensor->getShutterOpen()
                 + sensor->getShutterOpenTime() * sampler->next1D();

    const Frame stdFrame(Vector(1,0,0), Vector(0,1,0), Vector(0,0,1));
    int retries = 0;

    while (vpls.size() < count) {
        sampler->setSampleIndex(++offset);

        if (vpls.empty() && ++retries > 10000) {
            /* Unable to generate VPLs in this scene -- give up. */
            return 0;
        }

        PositionSamplingRecord pRec(time);
        DirectionSamplingRecord dRec;
        Spectrum weight = scene->sampleEmitterPosition(pRec,
                          sampler->next2D());

        size_t start = vpls.size();

        /* Sample an emitted particle */
        const Emitter *emitter = static_cast<const Emitter *>(pRec.object);

        if (!emitter->isEnvironmentEmitter() && emitter->needsDirectionSample()) {
            VPL lumVPL(EPointEmitterVPL, weight);
            lumVPL.its.p = pRec.p;
            lumVPL.its.time = time;
            lumVPL.its.shFrame = pRec.n.isZero() ? stdFrame : Frame(pRec.n);
            lumVPL.emitter = emitter;
            appendVPL(scene, random, lumVPL, prune, vpls);

            weight *= emitter->sampleDirection(dRec, pRec, sampler->next2D());
        } else {
            /* Hack to get the proper information for directional VPLs */
            DirectSamplingRecord diRec(
                scene->getKDTree()->getAABB().getCenter(), pRec.time);

            Spectrum weight2 = emitter->sampleDirect(diRec, sampler->next2D())
                               / scene->pdfEmitterDiscrete(emitter);

            if (weight2.isZero())
                continue;

            VPL lumVPL(EDirectionalEmitterVPL, weight2);
            lumVPL.its.p = Point(0.0);
            lumVPL.its.time = time;
            lumVPL.its.shFrame = Frame(-diRec.d);
            lumVPL.emitter = emitter;
            appendVPL(scene, random, lumVPL, false, vpls);
            dRec.d = -diRec.d;

            Point2 offset = warp::squareToUniformDiskConcentric(sampler->next2D());
            Vector perpOffset = Frame(diRec.d).toWorld(Vector(offset.x, offset.y, 0));
            BSphere geoBSphere = scene->getKDTree()->getAABB().getBSphere();
            pRec.p = geoBSphere.center + (perpOffset - dRec.d) * geoBSphere.radius;
            weight = weight2 * M_PI * geoBSphere.radius * geoBSphere.radius;
        }

        int depth = 2;
        Ray ray(pRec.p, dRec.d, time);
        Intersection its;

        while (!weight.isZero() && (depth < maxDepth || maxDepth == -1)) {
            if (!scene->rayIntersect(ray, its))
                break;

            const BSDF *bsdf = its.getBSDF();
            BSDFSamplingRecord bRec(its, sampler, EImportance);
            Spectrum bsdfVal = bsdf->sample(bRec, sampler->next2D());
            if (bsdfVal.isZero())
                break;

            /* Assuming that BSDF importance sampling is perfect,
            	the following should equal the maximum albedo
            	over all spectral samples */
            Float approxAlbedo = std::min((Float) 0.95f, bsdfVal.max());
            if (sampler->next1D() > approxAlbedo)
                break;
            else
                weight /= approxAlbedo;

            /* Modified by Lifan Wu
               Assume Lambertian BRDF
            */
            VPL vpl(ESurfaceVPL, weight);
            //VPL vpl(ESurfaceVPL, weight * bsdfVal);
            vpl.its = its;

            if (BSDF::getMeasure(bRec.sampledType) == ESolidAngle)
                appendVPL(scene, random, vpl, prune, vpls);

            weight *= bsdfVal;

            Vector wi = -ray.d, wo = its.toWorld(bRec.wo);
            ray = Ray(its.p, wo, 0.0f);

            /* Prevent light leaks due to the use of shading normals -- [Veach, p. 158] */
            Float wiDotGeoN = dot(its.geoFrame.n, wi),
                  woDotGeoN = dot(its.geoFrame.n, wo);
            if (wiDotGeoN * Frame::cosTheta(bRec.wi) <= 0 ||
                    woDotGeoN * Frame::cosTheta(bRec.wo) <= 0)
                break;

            /* Disabled for now -- this increases VPL weights
               and accuracy is not really a big requirement */
#if 0
            /* Adjoint BSDF for shading normals -- [Veach, p. 155] */
            weight *= std::abs(
                          (Frame::cosTheta(bRec.wi) * woDotGeoN)/
                          (Frame::cosTheta(bRec.wo) * wiDotGeoN));
#endif

            ++depth;
        }

        size_t end = vpls.size();
        for (size_t i=start; i<end; ++i)
            vpls[i].emitterScale = 1.0f / (end - start);
    }

    return offset;
}
Example #27
0
Spectrum MetropolisRenderer::Lbidir(const Scene *scene,
        const PathVertex *cameraPath, int cameraPathLength,
        const PathVertex *lightPath, int lightPathLength,
        MemoryArena &arena, const vector<LightingSample> &samples,
        RNG &rng, float time, const Distribution1D *lightDistribution,
        const RayDifferential &eRay, const Spectrum &eAlpha) const {
    PBRT_MLT_STARTED_LBIDIR();
    Spectrum L = 0.;
    bool previousSpecular = true, allSpecular = true;
    // Compute number of specular vertices for each path length
    int nVerts = cameraPathLength + lightPathLength + 2;
    int *nSpecularVertices = ALLOCA(int, nVerts);
    memset(nSpecularVertices, 0, nVerts * sizeof(int));
    for (int i = 0; i < cameraPathLength; ++i)
        for (int j = 0; j < lightPathLength; ++j)
            if (cameraPath[i].specularBounce || lightPath[j].specularBounce)
                ++nSpecularVertices[i+j+2];
    for (int i = 0; i < cameraPathLength; ++i) {
        // Initialize basic variables for camera path vertex
        const PathVertex &vc = cameraPath[i];
        const Point &pc = vc.bsdf->dgShading.p;
        const Normal &nc = vc.bsdf->dgShading.nn;

        // Compute reflected light at camera path vertex

        // Add emitted light from vertex if appropriate
        if (previousSpecular && (directLighting == NULL || !allSpecular))
            L += vc.alpha * vc.isect.Le(vc.wPrev);

        // Compute direct illumination for Metropolis path vertex
        Spectrum Ld(0.f);
        if (directLighting == NULL || !allSpecular) {
            // Choose light and call _EstimateDirect()_ for Metropolis vertex
            const LightingSample &ls = samples[i];
            float lightPdf;
            uint32_t lightNum = lightDistribution->SampleDiscrete(ls.lightNum,
                                                                  &lightPdf);
            const Light *light = scene->lights[lightNum];
            PBRT_MLT_STARTED_ESTIMATE_DIRECT();
            
            Ld = vc.alpha *
                 EstimateDirect(scene, this, arena, light, pc, nc, vc.wPrev,
                                vc.isect.rayEpsilon, time, vc.bsdf, rng,
                                ls.lightSample, ls.bsdfSample,
                                BxDFType(BSDF_ALL & ~BSDF_SPECULAR)) / lightPdf;
            PBRT_MLT_FINISHED_ESTIMATE_DIRECT();
        }
        previousSpecular = vc.specularBounce;
        allSpecular &= previousSpecular;
        L += Ld / (i + 1 - nSpecularVertices[i+1]);
        if (!vc.specularBounce) {
            // Loop over light path vertices and connect to camera vertex
            for (int j = 0; j < lightPathLength; ++j) {
                const PathVertex &vl = lightPath[j];
                const Point &pl = vl.bsdf->dgShading.p;
                const Normal &nl = vl.bsdf->dgShading.nn;
                if (!vl.specularBounce) {
                    // Compute contribution between camera and light vertices
                    Vector w = Normalize(pl - pc);
                    Spectrum fc = vc.bsdf->f(vc.wPrev, w) * (1 + vc.nSpecularComponents);
                    Spectrum fl = vl.bsdf->f(-w, vl.wPrev) * (1 + vl.nSpecularComponents);
                    if (fc.IsBlack() || fl.IsBlack()) continue;
                    Ray r(pc, pl - pc, 1e-3f, .999f, time);
                    if (!scene->IntersectP(r)) {
                        // Compute weight for bidirectional path, _pathWt_
                        float pathWt = 1.f / (i + j + 2 - nSpecularVertices[i+j+2]);
                        float G = AbsDot(nc, w) * AbsDot(nl, w) / DistanceSquared(pl, pc);
                        L += (vc.alpha * fc * G * fl * vl.alpha) * pathWt;
                    }
                }
            }
        }
    }
    // Add contribution of escaped ray, if any
    if (!eAlpha.IsBlack() && previousSpecular &&
        (directLighting == NULL || !allSpecular))
        for (uint32_t i = 0; i < scene->lights.size(); ++i)
           L += eAlpha * scene->lights[i]->Le(eRay);
    PBRT_MLT_FINISHED_LBIDIR();
    return L;
}
Example #28
0
Spectrum SingleScatteringIntegrator::Li(const Scene *scene, const Renderer *renderer,
        const RayDifferential &ray, const Sample *sample, RNG &rng,
        Spectrum *T, MemoryArena &arena) const {
    VolumeRegion *vr = scene->volumeRegion;
    float t0, t1;
    if (!vr || !vr->IntersectP(ray, &t0, &t1) || (t1-t0) == 0.f) {
        *T = 1.f;
        return 0.f;
    }
    // Do single scattering volume integration in _vr_
    Spectrum Lv(0.);

    // Prepare for volume integration stepping
    int nSamples = Ceil2Int((t1-t0) / stepSize);
    float step = (t1 - t0) / nSamples;
    Spectrum Tr(1.f);
    Point p = ray(t0), pPrev;
    Vector w = -ray.d;
    t0 += sample->oneD[scatterSampleOffset][0] * step;

    // Compute sample patterns for single scattering samples
    float *lightNum = arena.Alloc<float>(nSamples);
    LDShuffleScrambled1D(1, nSamples, lightNum, rng);
    float *lightComp = arena.Alloc<float>(nSamples);
    LDShuffleScrambled1D(1, nSamples, lightComp, rng);
    float *lightPos = arena.Alloc<float>(2*nSamples);
    LDShuffleScrambled2D(1, nSamples, lightPos, rng);
    uint32_t sampOffset = 0;
    for (int i = 0; i < nSamples; ++i, t0 += step) {
        // Advance to sample at _t0_ and update _T_
        pPrev = p;
        p = ray(t0);
        Ray tauRay(pPrev, p - pPrev, 0.f, 1.f, ray.time, ray.depth);
        Spectrum stepTau = vr->tau(tauRay,
                                   .5f * stepSize, rng.RandomFloat());
        Tr *= Exp(-stepTau);

        // Possibly terminate ray marching if transmittance is small
        if (Tr.y() < 1e-3) {
            const float continueProb = .5f;
            if (rng.RandomFloat() > continueProb) break;
            Tr /= continueProb;
        }

        // Compute single-scattering source term at _p_
        Lv += Tr * vr->Lve(p, w, ray.time);
        Spectrum ss = vr->sigma_s(p, w, ray.time);
        if (!ss.IsBlack() && scene->lights.size() > 0) {
            int nLights = scene->lights.size();
            int ln = min(Floor2Int(lightNum[sampOffset] * nLights),
                         nLights-1);
            Light *light = scene->lights[ln];
            // Add contribution of _light_ due to scattering at _p_
            float pdf;
            VisibilityTester vis;
            Vector wo;
            LightSample ls(lightComp[sampOffset], lightPos[2*sampOffset],
                           lightPos[2*sampOffset+1]);
            Spectrum L = light->Sample_L(p, 0.f, ls, ray.time, &wo, &pdf, &vis);
            
            if (!L.IsBlack() && pdf > 0.f && vis.Unoccluded(scene)) {
                Spectrum Ld = L * vis.Transmittance(scene, renderer, NULL, rng, arena);
                Lv += Tr * ss * vr->p(p, w, -wo, ray.time) * Ld * float(nLights) /
                        pdf;
            }
        }
        ++sampOffset;
    }
    *T = Tr;
    return Lv * step;
}
Example #29
0
void LightCPURenderThread::RenderFunc() {
	//SLG_LOG("[LightCPURenderThread::" << threadIndex << "] Rendering thread started");

	//--------------------------------------------------------------------------
	// Initialization
	//--------------------------------------------------------------------------

	LightCPURenderEngine *engine = (LightCPURenderEngine *)renderEngine;
	RandomGenerator *rndGen = new RandomGenerator(engine->seedBase + threadIndex);
	Scene *scene = engine->renderConfig->scene;
	PerspectiveCamera *camera = scene->camera;
	Film *film = threadFilm;

	// Setup the sampler
	double metropolisSharedTotalLuminance, metropolisSharedSampleCount;
	Sampler *sampler = engine->renderConfig->AllocSampler(rndGen, film,
			&metropolisSharedTotalLuminance, &metropolisSharedSampleCount);
	const u_int sampleBootSize = 12;
	const u_int sampleEyeStepSize = 4;
	const u_int sampleLightStepSize = 5;
	const u_int sampleSize = 
		sampleBootSize + // To generate the initial setup
		engine->maxPathDepth * sampleEyeStepSize + // For each eye vertex
		engine->maxPathDepth * sampleLightStepSize; // For each light vertex
	sampler->RequestSamples(sampleSize);

	//--------------------------------------------------------------------------
	// Trace light paths
	//--------------------------------------------------------------------------

	vector<SampleResult> sampleResults;
	while (!boost::this_thread::interruption_requested()) {
		sampleResults.clear();

		// Select one light source
		float lightPickPdf;
		const LightSource *light = scene->SampleAllLights(sampler->GetSample(2), &lightPickPdf);

		// Initialize the light path
		float lightEmitPdfW;
		Ray nextEventRay;
		Spectrum lightPathFlux = light->Emit(*scene,
			sampler->GetSample(3), sampler->GetSample(4), sampler->GetSample(5), sampler->GetSample(6), sampler->GetSample(7),
			&nextEventRay.o, &nextEventRay.d, &lightEmitPdfW);
		if (lightPathFlux.Black()) {
			sampler->NextSample(sampleResults);
			continue;
		}
		lightPathFlux /= lightEmitPdfW * lightPickPdf;
		assert (!lightPathFlux.IsNaN() && !lightPathFlux.IsInf());

		// Sample a point on the camera lens
		Point lensPoint;
		if (!camera->SampleLens(sampler->GetSample(8), sampler->GetSample(9),
				&lensPoint)) {
			sampler->NextSample(sampleResults);
			continue;
		}

		//----------------------------------------------------------------------
		// I don't try to connect the light vertex directly with the eye
		// because InfiniteLight::Emit() returns a point on the scene bounding
		// sphere. Instead, I trace a ray from the camera like in BiDir.
		// This is also a good why to test the Film Per-Pixel-Normalization and
		// the Per-Screen-Normalization Buffers used by BiDir.
		//----------------------------------------------------------------------

		TraceEyePath(sampler, &sampleResults);

		//----------------------------------------------------------------------
		// Trace the light path
		//----------------------------------------------------------------------

		int depth = 1;
		while (depth <= engine->maxPathDepth) {
			const u_int sampleOffset = sampleBootSize + sampleEyeStepSize * engine->maxPathDepth +
				(depth - 1) * sampleLightStepSize;

			RayHit nextEventRayHit;
			BSDF bsdf;
			Spectrum connectionThroughput;
			if (scene->Intersect(device, true, sampler->GetSample(sampleOffset),
					&nextEventRay, &nextEventRayHit, &bsdf, &connectionThroughput)) {
				// Something was hit

				lightPathFlux *= connectionThroughput;

				//--------------------------------------------------------------
				// Try to connect the light path vertex with the eye
				//--------------------------------------------------------------

				ConnectToEye(sampler->GetSample(sampleOffset + 1),
						bsdf, lensPoint, lightPathFlux, sampleResults);

				if (depth >= engine->maxPathDepth)
					break;

				//--------------------------------------------------------------
				// Build the next vertex path ray
				//--------------------------------------------------------------

				float bsdfPdf;
				Vector sampledDir;
				BSDFEvent event;
				float cosSampleDir;
				const Spectrum bsdfSample = bsdf.Sample(&sampledDir,
						sampler->GetSample(sampleOffset + 2),
						sampler->GetSample(sampleOffset + 3),
						&bsdfPdf, &cosSampleDir, &event);
				if (bsdfSample.Black())
					break;

				if (depth >= engine->rrDepth) {
					// Russian Roulette
					const float prob = RenderEngine::RussianRouletteProb(bsdfSample, engine->rrImportanceCap);
					if (sampler->GetSample(sampleOffset + 4) < prob)
						bsdfPdf *= prob;
					else
						break;
				}

				lightPathFlux *= bsdfSample * (cosSampleDir / bsdfPdf);
				assert (!lightPathFlux.IsNaN() && !lightPathFlux.IsInf());

				nextEventRay = Ray(bsdf.hitPoint.p, sampledDir);
				++depth;
			} else {
				// Ray lost in space...
				break;
			}
		}

		sampler->NextSample(sampleResults);

#ifdef WIN32
		// Work around Windows bad scheduling
		renderThread->yield();
#endif
	}

	delete sampler;
	delete rndGen;

	//SLG_LOG("[LightCPURenderThread::" << threadIndex << "] Rendering thread halted");
}
Example #30
0
void compute_ibl_bssrdf_sampling(
    SamplingContext&        sampling_context,
    const ShadingContext&   shading_context,
    const EnvironmentEDF&   environment_edf,
    const BSSRDF&           bssrdf,
    const void*             bssrdf_data,
    const ShadingPoint&     incoming_point,
    const ShadingPoint&     outgoing_point,
    const Dual3d&           outgoing,
    const size_t            bssrdf_sample_count,
    const size_t            env_sample_count,
    Spectrum&               radiance)
{
    assert(is_normalized(outgoing.get_value()));

    radiance.set(0.0f);

    sampling_context.split_in_place(2, bssrdf_sample_count);

    for (size_t i = 0; i < bssrdf_sample_count; ++i)
    {
        // Generate a uniform sample in [0,1)^2.
        const Vector2d s = sampling_context.next_vector2<2>();

        // Sample the BSSRDF (hemisphere cosine).
        Vector3d incoming = sample_hemisphere_cosine(s);
        const double cos_in = incoming.y;
        const double bssrdf_prob = cos_in * RcpPi;
        incoming = incoming_point.get_shading_basis().transform_to_parent(incoming);
        if (incoming_point.get_side() == ObjectInstance::BackSide)
            incoming = -incoming;
        assert(is_normalized(incoming));

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

        // Evaluate the BSSRDF.
        Spectrum bssrdf_value;
        bssrdf.evaluate(
            bssrdf_data,
            outgoing_point,
            outgoing.get_value(),
            incoming_point,
            incoming,
            bssrdf_value);

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

        // Compute MIS weight.
        const double mis_weight =
            mis_power2(
                bssrdf_sample_count * bssrdf_prob,
                env_sample_count * env_prob);

        // Add the contribution of this sample to the illumination.
        env_value *= static_cast<float>(transmission * cos_in / bssrdf_prob * mis_weight);
        env_value *= bssrdf_value;
        radiance += env_value;
    }

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