Example #1
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();
	}

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

	const Frame stdFrame(Vector(1,0,0), Vector(0,1,0), Vector(0,0,1));

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

		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.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.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;

			VPL vpl(ESurfaceVPL, weight);
			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 #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);

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

		Spectrum pathThroughput(1.0f);

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

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

			if (EXPECT_NOT_TAKEN(bsdf == NULL)) {
				/* The MI path tracer doesn't support
				   surfaces without a BSDF (e.g. medium transitions)
				   -- give up. */
				break;
			}

			/* Possibly include emitted radiance if requested */
			if (its.isLuminaire() && (rRec.type & RadianceQueryRecord::EEmittedRadiance))
				Li += pathThroughput * its.Le(-ray.d);

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

			if (m_maxDepth > 0 && rRec.depth >= m_maxDepth)
				break;

			/* ==================================================================== */
			/*                          Luminaire sampling                          */
			/* ==================================================================== */

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

			/* Estimate the direct illumination if this is requested */
			LuminaireSamplingRecord lRec;
			if (rRec.type & RadianceQueryRecord::EDirectSurfaceRadiance && 
				scene->sampleLuminaire(its.p, ray.time, lRec, rRec.nextSample2D())) {
				/* Allocate a record for querying the BSDF */
				const Vector wo = -lRec.d;
				const BSDFQueryRecord bRec(its, its.toLocal(wo));
	
				/* Evaluate BSDF * cos(theta) */
				const Spectrum bsdfVal = bsdf->fCos(bRec);

				Float woDotGeoN = dot(its.geoFrame.n, wo);

				/* Prevent light leaks due to the use of shading normals */
				if (!bsdfVal.isZero() && (!m_strictNormals
						|| woDotGeoN * Frame::cosTheta(bRec.wo) > 0)) {
					/* Calculate prob. of having sampled that direction
					   using BSDF sampling */
					Float bsdfPdf = (lRec.luminaire->isIntersectable() 
							|| lRec.luminaire->isBackgroundLuminaire()) ? 
						bsdf->pdf(bRec) : 0;

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

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

			/* Sample BSDF * cos(theta) */
			BSDFQueryRecord bRec(its);
			Float bsdfPdf;
			Spectrum bsdfVal = bsdf->sampleCos(bRec, bsdfPdf, rRec.nextSample2D());
			if (bsdfVal.isZero()) 
				break;
			bsdfVal /= bsdfPdf;
	
			/* 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;

			/* Trace a ray in this direction */
			ray = Ray(its.p, wo, ray.time);

			bool hitLuminaire = false;
			if (scene->rayIntersect(ray, its)) {
				/* Intersected something - check if it was a luminaire */
				if (its.isLuminaire()) {
					lRec = LuminaireSamplingRecord(its, -ray.d);
					lRec.value = its.Le(-ray.d);
					hitLuminaire = true;
				}
			} else {
				/* No intersection found. Possibly, there is a background
				   luminaire such as an environment map? */
				if (scene->hasBackgroundLuminaire()) {
					lRec.luminaire = scene->getBackgroundLuminaire();
					lRec.value = lRec.luminaire->Le(ray);
					lRec.d = -ray.d;
					hitLuminaire = true;
				} else {
					rRec.depth++;
					break;
				}
			}

			/* If a luminaire was hit, estimate the local illumination and
			   weight using the power heuristic */
			if (hitLuminaire &&  
				(rRec.type & RadianceQueryRecord::EDirectSurfaceRadiance)) {
				/* Prob. of having generated this sample using luminaire sampling */
				const Float lumPdf = (!(bRec.sampledType & BSDF::EDelta)) ?
					scene->pdfLuminaire(ray.o, lRec) : 0;
				const Float weight = miWeight(bsdfPdf, lumPdf);
				Li += pathThroughput * lRec.value * bsdfVal * weight;
			}

			/* ==================================================================== */
			/*                         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;

			/* Russian roulette - Possibly stop the recursion. Don't do this when
			   dealing with a transmission component, since solid angle compression
			   factors cause problems with the heuristic below */
			if (rRec.depth >= m_rrDepth && !(bRec.sampledType & BSDF::ETransmission)) {
				/* Assuming that BSDF importance sampling is perfect,
				   'bsdfVal.max()' should equal the maximum albedo
				   over all spectral samples */
				Float approxAlbedo = std::min((Float) 0.9f, bsdfVal.max());
				if (rRec.nextSample1D() > approxAlbedo) 
					break;
				else
					pathThroughput /= approxAlbedo;
			}

			pathThroughput *= bsdfVal;
			rRec.depth++;
		}

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

		return Li;
	}