void CaptureParticleWorker::handleMediumInteraction(int depth, int nullInteractions, bool caustic,
		const MediumSamplingRecord &mRec, const Medium *medium, const Vector &wi,
		const Spectrum &weight) {

	if (m_bruteForce || (depth >= m_maxPathDepth && m_maxPathDepth > 0))
		return;

	DirectSamplingRecord dRec(mRec);

	int maxInteractions = m_maxPathDepth - depth - 1;

	Spectrum value = weight * m_scene->sampleAttenuatedSensorDirect(
		dRec, medium, maxInteractions, m_sampler->next2D(), m_sampler);

	if (value.isZero())
		return;

	/* Evaluate the phase function */
	const PhaseFunction *phase = medium->getPhaseFunction();
	PhaseFunctionSamplingRecord pRec(mRec, wi, dRec.d, EImportance);
	value *= phase->eval(pRec);

	if (value.isZero())
		return;

	/* Splat onto the accumulation buffer */
	m_workResult->put(dRec.uv, (Float *) &value[0]);
}
void CaptureParticleWorker::handleSurfaceInteraction(int depth, int nullInteractions,
		bool caustic, const Intersection &its, const Medium *medium,
		const Spectrum &weight) {

	if (its.isSensor()) {
		if (!m_bruteForce && !caustic)
			return;

		const Sensor *sensor = its.shape->getSensor();
		if (sensor != m_sensor)
			return;

		Vector wi = its.toWorld(its.wi);
		Point2 uv;
		Spectrum value = sensor->eval(its, wi, uv) * weight;
		if (value.isZero())
			return;

		m_workResult->put(uv, (Float *) &value[0]);
		return;
	}

	if (m_bruteForce || (depth >= m_maxPathDepth && m_maxPathDepth > 0))
		return;

	int maxInteractions = m_maxPathDepth - depth - 1;

	DirectSamplingRecord dRec(its);
	Spectrum value = weight * m_scene->sampleAttenuatedSensorDirect(
			dRec, its, medium, maxInteractions,
			m_sampler->next2D(), m_sampler);

	if (value.isZero())
		return;

	const BSDF *bsdf = its.getBSDF();

	Vector wo = dRec.d;
	BSDFSamplingRecord bRec(its, its.toLocal(wo), EImportance);

	/* Prevent light leaks due to the use of shading normals -- [Veach, p. 158] */
	Vector wi = its.toWorld(its.wi);
	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)
		return;

	/* Adjoint BSDF for shading normals -- [Veach, p. 155] */
	Float correction = std::abs(
		(Frame::cosTheta(bRec.wi) * woDotGeoN)/
		(Frame::cosTheta(bRec.wo) * wiDotGeoN));
	value *= bsdf->eval(bRec) * correction;

	/* Splat onto the accumulation buffer */
	m_workResult->put(dRec.uv, (Float *) &value[0]);
}
Exemplo n.º 3
0
	Spectrum sample(BSDFSamplingRecord &bRec, Float &pdf, const Point2 &_sample) const {
		Point2 sample(_sample);
		if (bRec.component == -1) {
			/* Choose a component based on the normalized weights */
			size_t entry = m_pdf.sampleReuse(sample.x);

			Spectrum result = m_bsdfs[entry]->sample(bRec, pdf, sample);
			if (result.isZero()) // sampling failed
				return result;

			result *= m_weights[entry] * pdf;
			pdf *= m_pdf[entry];

			EMeasure measure = BSDF::getMeasure(bRec.sampledType);
			for (size_t i=0; i<m_bsdfs.size(); ++i) {
				if (entry == i)
					continue;
				pdf += m_bsdfs[i]->pdf(bRec, measure) * m_pdf[i];
				result += m_bsdfs[i]->eval(bRec, measure) * m_weights[i];
			}

			bRec.sampledComponent += m_offsets[entry];
			return result/pdf;
		} else {
			/* Pick out an individual component */
			int requestedComponent = bRec.component;
			int bsdfIndex = m_indices[requestedComponent].first;
			bRec.component = m_indices[requestedComponent].second;
			Spectrum result = m_bsdfs[bsdfIndex]->sample(bRec, pdf, sample)
				* m_weights[bsdfIndex];
			bRec.component = bRec.sampledComponent = requestedComponent;
			return result;
		}
	}
Exemplo n.º 4
0
	Spectrum E(const Scene *scene, const Intersection &its, const Medium *medium,
			Sampler *sampler, int nSamples, bool handleIndirect) const {
		Spectrum EDir(0.0f), EIndir(0.0f);
		DirectSamplingRecord dRec(its);

		/* Sample the direct illumination component */
		for (int i=0; i<nSamples; i++) {
			int maxIntermediateInteractions = -1;
			Spectrum directRadiance = scene->sampleAttenuatedEmitterDirect(
				dRec, its, medium, maxIntermediateInteractions, sampler->next2D());

			if (!directRadiance.isZero()) {
				Float dp = dot(dRec.d, its.shFrame.n);
				if (dp > 0)
					EDir += directRadiance * dp;
			}
		}

		if (handleIndirect) {
			RadianceQueryRecord rRec(scene, sampler);
			rRec.newQuery(RadianceQueryRecord::ERadianceNoEmission, medium);
			rRec.its = its;
			if (!m_irrCache->get(rRec.its, EIndir))
				handleMiss(RayDifferential(), rRec, EIndir);
		}

		return (EDir / (Float) nSamples) + EIndir;
	}
void CaptureParticleWorker::handleEmission(const PositionSamplingRecord &pRec,
		const Medium *medium, const Spectrum &weight) {
	if (m_bruteForce)
		return;

	DirectSamplingRecord dRec(pRec.p, pRec.time);
	int maxInteractions = m_maxPathDepth - 1;

	/* Create a dummy intersection to ensure that sampleAttenuatedSensorDirect()
	   treats the light source vertex as being located on a surface */
	Intersection its;
	its.p = pRec.p;

	Spectrum value = weight * m_scene->sampleAttenuatedSensorDirect(
			dRec, its, medium, maxInteractions, m_sampler->next2D(), m_sampler);

	if (value.isZero())
		return;

	const Emitter *emitter = static_cast<const Emitter *>(pRec.object);
	value *= emitter->evalDirection(DirectionSamplingRecord(dRec.d), pRec);

	/* Splat onto the accumulation buffer */
	m_workResult->put(dRec.uv, (Float *) &value[0]);
}
Exemplo n.º 6
0
	Spectrum eval(const BSDFSamplingRecord &bRec, EMeasure measure) const {
		bool hasNested = (bRec.typeMask & m_nested->getType() & BSDF::EAll)
			&& (bRec.component == -1 || bRec.component < (int) m_components.size()-1);
		bool hasSpecular = (bRec.typeMask & EGlossyReflection)
			&& (bRec.component == -1 || bRec.component == (int) m_components.size()-1)
			&& measure == ESolidAngle;

		/* Evaluate the roughness texture */
		Float alpha = m_alpha->eval(bRec.its).average();
		Float alphaT = m_distribution.transformRoughness(alpha);

		Spectrum result(0.0f);
		if (hasSpecular && Frame::cosTheta(bRec.wo) * Frame::cosTheta(bRec.wi) > 0) {
			/* Calculate the reflection half-vector */
			const Vector H = normalize(bRec.wo+bRec.wi)
				* signum(Frame::cosTheta(bRec.wo));

			/* Evaluate the microsurface normal distribution */
			const Float D = m_distribution.eval(H, alphaT);

			/* Fresnel term */
			const Float F = fresnelDielectricExt(absDot(bRec.wi, H), m_eta);

			/* Smith's shadow-masking function */
			const Float G = m_distribution.G(bRec.wi, bRec.wo, H, alphaT);

			/* Calculate the specular reflection component */
			Float value = F * D * G /
				(4.0f * std::abs(Frame::cosTheta(bRec.wi)));

			result += m_specularReflectance->eval(bRec.its) * value;
		}

		if (hasNested) {
			BSDFSamplingRecord bRecInt(bRec);
			bRecInt.wi = refractTo(EInterior, bRec.wi);
			bRecInt.wo = refractTo(EInterior, bRec.wo);

			Spectrum nestedResult = m_nested->eval(bRecInt, measure) *
				m_roughTransmittance->eval(std::abs(Frame::cosTheta(bRec.wi)), alpha) *
				m_roughTransmittance->eval(std::abs(Frame::cosTheta(bRec.wo)), alpha);

			Spectrum sigmaA = m_sigmaA->eval(bRec.its) * m_thickness;
			if (!sigmaA.isZero())
				nestedResult *= (-sigmaA *
					(1/std::abs(Frame::cosTheta(bRecInt.wi)) +
					 1/std::abs(Frame::cosTheta(bRecInt.wo)))).exp();

			if (measure == ESolidAngle) {
				/* Solid angle compression & irradiance conversion factors */
				nestedResult *= m_invEta * m_invEta *
					  Frame::cosTheta(bRec.wi) * Frame::cosTheta(bRec.wo)
				   / (Frame::cosTheta(bRecInt.wi) * Frame::cosTheta(bRecInt.wo));
			}

			result += nestedResult;
		}

		return result;
	}
Exemplo n.º 7
0
Spectrum SamplingIntegrator::E(const Scene *scene, const Intersection &its,
		const Medium *medium, Sampler *sampler, int nSamples, bool handleIndirect) const {
	Spectrum E(0.0f);
	RadianceQueryRecord query(scene, sampler);
	DirectSamplingRecord dRec(its);
	Frame frame(its.shFrame.n);

	sampler->generate(Point2i(0));
	for (int i=0; i<nSamples; i++) {
		/* Sample the direct illumination component */
		int maxIntermediateInteractions = -1;
		Spectrum directRadiance = scene->sampleAttenuatedEmitterDirect(
			dRec, its, medium, maxIntermediateInteractions, query.nextSample2D());

		if (!directRadiance.isZero()) {
			Float dp = dot(dRec.d, its.shFrame.n);
			if (dp > 0)
				E += directRadiance * dp;
		}

		/* Sample the indirect illumination component */
		if (handleIndirect) {
			query.newQuery(RadianceQueryRecord::ERadianceNoEmission, medium);
			Vector d = frame.toWorld(Warp::squareToCosineHemisphere(query.nextSample2D()));
			++query.depth;
			query.medium = medium;
			E += Li(RayDifferential(its.p, d, its.time), query) * M_PI;
		}

		sampler->advance();
	}

	return E / (Float) nSamples;
}
Exemplo n.º 8
0
	int createGatherPoints(Scene *scene, const RayDifferential &ray, 
			const Point2 &sample, Sampler *sampler, const Spectrum &weight, 
			std::vector<GatherPoint> &gatherPoints, int depth) {
		int count = 0;
		if (depth >= m_maxDepth && m_maxDepth != -1)
			return 0;
		GatherPoint p;
		if (scene->rayIntersect(ray, p.its)) {
			const BSDF *bsdf = p.its.shape->getBSDF();
			if (!bsdf) {
				p.radius = 0;
				p.sample = sample;
				gatherPoints.push_back(p);
				++count;
			} else {
				if (bsdf->getType() & BSDF::ESmooth) {
					p.weight = weight;
					p.sample = sample;
					p.radius = m_initialRadius;
					p.depth = depth;
					if (p.its.isLuminaire())
						p.emission = p.its.Le(-ray.d);
					gatherPoints.push_back(p);
					++count;
				}

				if (bsdf->getType() & BSDF::EDelta) {
					int compCount = bsdf->getComponentCount();
					for (int i=0; i<compCount; i++) {
						if ((bsdf->getType(i) & BSDF::EDelta) == 0)
							continue;
						/* Sample the BSDF and recurse */
						BSDFQueryRecord bRec(p.its, sampler);
						bRec.component = i;
						Spectrum bsdfVal = bsdf->sample(bRec, Point2(0.0f));
						if (bsdfVal.isZero())
							continue;
						bsdfVal = bsdf->eval(bRec, EDiscrete);

						const Float rrProb = depth < 4 ? 1 : 0.8f;
						if (sampler->independent1D() < rrProb) {
							RayDifferential recursiveRay(p.its.p, p.its.toWorld(bRec.wo), ray.time);
							count += createGatherPoints(scene, recursiveRay, sample, sampler, 
								weight * bsdfVal / rrProb, gatherPoints, depth+1);
						}
					}
				}
			}
		} else if (depth == 1) {
			/* Generate an invalid sample */
			p.emission = scene->LeBackground(ray);
			p.radius = 0;
			p.sample = sample;
			gatherPoints.push_back(p);
			++count;
		}
		return count;
	}
Exemplo n.º 9
0
void CaptureParticleWorker::handleSurfaceInteraction(int depth,
		bool caustic, const Intersection &its, const Medium *medium,
		const Spectrum &weight) {
	const ProjectiveCamera *camera = static_cast<const ProjectiveCamera *>(m_camera.get());
	Point2 screenSample;

	if (camera->positionToSample(its.p, screenSample)) {
		Point cameraPosition = camera->getPosition(screenSample);
	
		Float t = dot(camera->getImagePlaneNormal(), its.p-cameraPosition);
		if (t < camera->getNearClip() || t > camera->getFarClip())
			return;

		if (its.isMediumTransition()) 
			medium = its.getTargetMedium(cameraPosition - its.p);

		Spectrum transmittance = m_scene->getTransmittance(its.p,
				cameraPosition, its.time, medium);

		if (transmittance.isZero())
			return;

		const BSDF *bsdf = its.shape->getBSDF();
		Vector wo = cameraPosition - its.p;
		Float dist = wo.length(); wo /= dist;

		BSDFQueryRecord bRec(its, its.toLocal(wo));
		bRec.quantity = EImportance;

		Float importance; 
		if (m_isPerspectiveCamera)
			importance = ((const PerspectiveCamera *) camera)->importance(screenSample) / (dist * dist);
		else
			importance = 1/camera->areaDensity(screenSample);

		Vector wi = its.toWorld(its.wi);

		/* 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)
			return;

		/* Adjoint BSDF for shading normals -- [Veach, p. 155] */
		Float correction = std::abs(
			(Frame::cosTheta(bRec.wi) * woDotGeoN)/
			(Frame::cosTheta(bRec.wo) * wiDotGeoN));

		/* Splat onto the accumulation buffer */
		Ray ray(its.p, wo, 0, dist, its.time);
		Spectrum sampleVal = weight * bsdf->fCos(bRec) 
			* transmittance * (importance * correction);

		m_workResult->splat(screenSample, sampleVal, m_filter);
	}
}
Exemplo n.º 10
0
	Spectrum sample(BSDFSamplingRecord &bRec, const Point2 &_sample) const {
		Point2 sample(_sample);

		Float weights[2];
		weights[1] = std::min((Float) 1.0f, std::max((Float) 0.0f,
			m_weight->eval(bRec.its).average()));
		weights[0] = 1-weights[1];

		if (bRec.component == -1) {
			size_t entry;
			if (sample.x < weights[0]) {
				entry = 0; sample.x /= weights[0];
			} else {
				entry = 1; sample.x = (sample.x - weights[0]) / weights[1];
			}

			Float pdf;
			Spectrum result = m_bsdfs[entry]->sample(bRec, pdf, sample);
			if (result.isZero()) // sampling failed
				return result;

			result *= weights[entry] * pdf;
			pdf *= weights[entry];

			EMeasure measure = BSDF::getMeasure(bRec.sampledType);
			for (size_t i=0; i<m_bsdfs.size(); ++i) {
				if (entry == i)
					continue;
				pdf += m_bsdfs[i]->pdf(bRec, measure) * weights[i];
				result += m_bsdfs[i]->eval(bRec, measure) * weights[i];
			}

			bRec.sampledComponent += m_offsets[entry];
			return result/pdf;
		} else {
			/* Pick out an individual component */
			int requestedComponent = bRec.component;
			int bsdfIndex = m_indices[requestedComponent].first;
			bRec.component = m_indices[requestedComponent].second;
			Spectrum result = m_bsdfs[bsdfIndex]->sample(bRec, sample)
				* weights[bsdfIndex];
			bRec.component = bRec.sampledComponent = requestedComponent;
			return result;
		}
	}
Exemplo n.º 11
0
	Spectrum sample(BSDFSamplingRecord &bRec, const Point2 &sample) const {
		const Intersection& its = bRec.its;
		Intersection perturbed(its);
		perturbed.shFrame = getFrame(its);

		BSDFSamplingRecord perturbedQuery(perturbed, bRec.sampler, bRec.mode);
		perturbedQuery.wi = perturbed.toLocal(its.toWorld(bRec.wi));
		perturbedQuery.sampler = bRec.sampler;
		perturbedQuery.typeMask = bRec.typeMask;
		perturbedQuery.component = bRec.component;
		Spectrum result = m_nested->sample(perturbedQuery, sample);
		if (!result.isZero()) {
			bRec.sampledComponent = perturbedQuery.sampledComponent;
			bRec.sampledType = perturbedQuery.sampledType;
			bRec.wo = its.toLocal(perturbed.toWorld(perturbedQuery.wo));
			bRec.eta = perturbedQuery.eta;
			if (Frame::cosTheta(bRec.wo) * Frame::cosTheta(perturbedQuery.wo) <= 0)
				return Spectrum(0.0f);
		}
		return result;
	}
Exemplo n.º 12
0
	Spectrum sample(BSDFSamplingRecord &bRec, Float &pdf, const Point2 &sample) const {
		bool flipped = false;
		if (Frame::cosTheta(bRec.wi) < 0) {
			bRec.wi.z *= -1;
			flipped = true;
			if (bRec.component != -1)
				bRec.component -= m_nestedBRDF[0]->getComponentCount();
		}

		Spectrum result = m_nestedBRDF[flipped ? 1 : 0]->sample(bRec, pdf, sample);

		if (flipped) {
			bRec.wi.z *= -1;

			if (bRec.component != -1)
				bRec.component += m_nestedBRDF[0]->getComponentCount();
			if (!result.isZero() && pdf != 0) {
				bRec.wo.z *= -1;
				bRec.sampledComponent += m_nestedBRDF[0]->getComponentCount();
			}
		}
		return result;
	}
Exemplo n.º 13
0
void CaptureParticleWorker::handleMediumInteraction(int depth, bool caustic,
		const MediumSamplingRecord &mRec, const Medium *medium,
		Float time, const Vector &wi, const Spectrum &weight) {
	const ProjectiveCamera *camera = static_cast<const ProjectiveCamera *>(m_camera.get());
	Point2 screenSample;

	if (camera->positionToSample(mRec.p, screenSample)) {
		Point cameraPosition = camera->getPosition(screenSample);

		Float t = dot(camera->getImagePlaneNormal(), mRec.p-cameraPosition);
		if (t < camera->getNearClip() || t > camera->getFarClip())
			return;

		Spectrum transmittance = m_scene->getTransmittance(mRec.p,
			cameraPosition, time, medium);

		if (transmittance.isZero())
			return;

		Vector wo = cameraPosition - mRec.p;
		Float dist = wo.length(); wo /= dist;

		Float importance; 
		if (m_isPerspectiveCamera)
			importance = ((const PerspectiveCamera *) camera)->importance(screenSample) / (dist * dist);
		else
			importance = 1/camera->areaDensity(screenSample);

		/* Splat onto the accumulation buffer */
		Ray ray(mRec.p, wo, 0, dist, time);

		Spectrum sampleVal = weight * medium->getPhaseFunction()->f(
			  PhaseFunctionQueryRecord(mRec, wi, wo)) * transmittance * importance;

		m_workResult->splat(screenSample, sampleVal, m_filter);
	}
}
Exemplo n.º 14
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;
}
Exemplo n.º 15
0
	inline Spectrum sample(BSDFSamplingRecord &bRec, Float &_pdf, const Point2 &_sample) const {
		bool hasNested = (bRec.typeMask & m_nested->getType() & BSDF::EAll)
			&& (bRec.component == -1 || bRec.component < (int) m_components.size()-1);
		bool hasSpecular = (bRec.typeMask & EGlossyReflection)
			&& (bRec.component == -1 || bRec.component == (int) m_components.size()-1);

		bool choseSpecular = hasSpecular;
		Point2 sample(_sample);

		/* Evaluate the roughness texture */
		Float alpha = m_alpha->eval(bRec.its).average();
		Float alphaT = m_distribution.transformRoughness(alpha);

		Float probSpecular;
		if (hasSpecular && hasNested) {
			/* Find the probability of sampling the diffuse component */
			probSpecular = 1 - m_roughTransmittance->eval(std::abs(Frame::cosTheta(bRec.wi)), alpha);

			/* Reallocate samples */
			probSpecular = (probSpecular*m_specularSamplingWeight) /
				(probSpecular*m_specularSamplingWeight +
				(1-probSpecular) * (1-m_specularSamplingWeight));

			if (sample.y <= probSpecular) {
				sample.y /= probSpecular;
			} else {
				sample.y = (sample.y - probSpecular) / (1 - probSpecular);
				choseSpecular = false;
			}
		}

		if (choseSpecular) {
			/* Perfect specular reflection based on the microsurface normal */
			Normal m = m_distribution.sample(sample, alphaT);
			bRec.wo = reflect(bRec.wi, m);
			bRec.sampledComponent = (int) m_components.size() - 1;
			bRec.sampledType = EGlossyReflection;
			bRec.eta = 1.0f;

			/* Side check */
			if (Frame::cosTheta(bRec.wo) * Frame::cosTheta(bRec.wi) <= 0)
				return Spectrum(0.0f);
		} else {
			Vector wiBackup = bRec.wi;
			bRec.wi = refractTo(EInterior, bRec.wi);
			Spectrum result = m_nested->sample(bRec, _pdf, sample);
			bRec.wi = wiBackup;
			if (result.isZero())
				return Spectrum(0.0f);
			bRec.wo = refractTo(EExterior, bRec.wo);
			if (bRec.wo.isZero())
				return Spectrum(0.0f);
		}

		/* Guard against numerical imprecisions */
		EMeasure measure = getMeasure(bRec.sampledType);
		_pdf = pdf(bRec, measure);

		if (_pdf == 0)
			return Spectrum(0.0f);
		else
			return eval(bRec, measure) / _pdf;
	}
Exemplo n.º 16
0
	Spectrum Li(const RayDifferential &ray, RadianceQueryRecord &rRec) const {
		Spectrum LiSurf(0.0f), LiMedium(0.0f), transmittance(1.0f);
		Intersection &its = rRec.its;
		const Scene *scene = rRec.scene;

		bool cacheQuery = (rRec.extra & RadianceQueryRecord::ECacheQuery);
		bool adaptiveQuery = (rRec.extra & RadianceQueryRecord::EAdaptiveQuery);

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

		if (rRec.medium) {
			Ray mediumRaySegment(ray, 0, its.t);
			transmittance = rRec.medium->evalTransmittance(mediumRaySegment);
			mediumRaySegment.mint = ray.mint;
			if (rRec.type & RadianceQueryRecord::EVolumeRadiance &&
					(rRec.depth < m_maxDepth || m_maxDepth < 0) && m_bre.get() != NULL)
				LiMedium = m_bre->query(mediumRaySegment, rRec.medium);
		}

		if (!its.isValid()) {
			/* If no intersection could be found, possibly return
			   attenuated radiance from a background luminaire */
			if ((rRec.type & RadianceQueryRecord::EEmittedRadiance) && !m_hideEmitters)
				LiSurf = scene->evalEnvironment(ray);
			return LiSurf * transmittance + LiMedium;
		}

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

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

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

		if (rRec.depth >= m_maxDepth && m_maxDepth > 0)
			return LiSurf * transmittance + LiMedium;

		unsigned int bsdfType = bsdf->getType() & BSDF::EAll;

		/* Irradiance cache query -> treat as diffuse */
		bool isDiffuse = (bsdfType == BSDF::EDiffuseReflection) || cacheQuery;

		bool hasSpecular = bsdfType & BSDF::EDelta;

		/* Exhaustively recurse into all specular lobes? */
		bool exhaustiveSpecular = rRec.depth < m_maxSpecularDepth && !cacheQuery;

		if (isDiffuse && (dot(its.shFrame.n, ray.d) < 0 || (bsdf->getType() & BSDF::EBackSide))) {
			/* 1. Diffuse indirect */
			int maxDepth = m_maxDepth == -1 ? INT_MAX : (m_maxDepth-rRec.depth);
			if (rRec.type & RadianceQueryRecord::EIndirectSurfaceRadiance && m_globalPhotonMap.get())
				LiSurf += m_globalPhotonMap->estimateIrradiance(its.p,
					its.shFrame.n, m_globalLookupRadius, maxDepth,
					m_globalLookupSize) * bsdf->getDiffuseReflectance(its) * INV_PI;
			if (rRec.type & RadianceQueryRecord::ECausticRadiance && m_causticPhotonMap.get())
				LiSurf += m_causticPhotonMap->estimateIrradiance(its.p,
					its.shFrame.n, m_causticLookupRadius, maxDepth,
					m_causticLookupSize) * bsdf->getDiffuseReflectance(its) * INV_PI;
		}

		if (hasSpecular && exhaustiveSpecular
			&& (rRec.type & RadianceQueryRecord::EIndirectSurfaceRadiance)) {
			/* 1. Specular indirect */
			int compCount = bsdf->getComponentCount();
			RadianceQueryRecord rRec2;
			for (int i=0; i<compCount; i++) {
				unsigned int type = bsdf->getType(i);
				if (!(type & BSDF::EDelta))
					continue;
				/* Sample the BSDF and recurse */
				BSDFSamplingRecord bRec(its, rRec.sampler, ERadiance);
				bRec.component = i;
				Spectrum bsdfVal = bsdf->sample(bRec, Point2(0.5f));
				if (bsdfVal.isZero())
					continue;

				rRec2.recursiveQuery(rRec, RadianceQueryRecord::ERadiance);
				RayDifferential bsdfRay(its.p, its.toWorld(bRec.wo), ray.time);
				if (its.isMediumTransition())
					rRec2.medium = its.getTargetMedium(bsdfRay.d);

				LiSurf += bsdfVal * m_parentIntegrator->Li(bsdfRay, rRec2);
			}
		}

		/* Estimate the direct illumination if this is requested */
		int numEmitterSamples = m_directSamples, numBSDFSamples;
		Float weightLum, weightBSDF;
		Point2 *sampleArray;
		Point2 sample;

		if (rRec.depth > 1 || cacheQuery || adaptiveQuery) {
			/* This integrator is used recursively by another integrator.
			   Be less accurate as this sample will not directly be observed. */
			numBSDFSamples = numEmitterSamples = 1;
			weightLum = weightBSDF = 1.0f;
		} else {
			if (isDiffuse) {
				numBSDFSamples = m_directSamples;
				weightBSDF = weightLum = m_invEmitterSamples;
			} else {
				numBSDFSamples = m_glossySamples;
				weightLum = m_invEmitterSamples;
				weightBSDF = m_invGlossySamples;
			}
		}

		if ((bsdfType & BSDF::ESmooth) && (rRec.type & RadianceQueryRecord::EDirectSurfaceRadiance)) {
			DirectSamplingRecord dRec(its);

			if (numEmitterSamples > 1) {
				sampleArray = rRec.sampler->next2DArray(m_directSamples);
			} else {
				sample = rRec.nextSample2D(); sampleArray = &sample;
			}

			for (int i=0; i<numEmitterSamples; ++i) {
				int interactions = m_maxDepth - rRec.depth - 1;
				Spectrum value = scene->sampleAttenuatedEmitterDirect(
						dRec, its, rRec.medium, interactions,
						sampleArray[i], rRec.sampler);

				/* Estimate the direct illumination if this is requested */
				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));

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

					if (!bsdfVal.isZero()) {
						/* Calculate prob. of having sampled that direction
						   using BSDF sampling */

						if (!hasSpecular || exhaustiveSpecular)
							bRec.typeMask = BSDF::ESmooth;

						Float bsdfPdf = (emitter->isOnSurface()
								&& dRec.measure == ESolidAngle
								&& interactions == 0)
								? bsdf->pdf(bRec) : (Float) 0.0f;

						/* Weight using the power heuristic */
						const Float weight = miWeight(dRec.pdf * numEmitterSamples,
								bsdfPdf * numBSDFSamples) * weightLum;

						LiSurf += value * bsdfVal * weight;
					}
				}
			}
		}

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

		/* Sample direct compontent via BSDF sampling if this is generally requested AND
		     the BSDF is smooth, or there is a delta component that was not handled by the
			 exhaustive sampling loop above */
		bool bsdfSampleDirect = (rRec.type & RadianceQueryRecord::EDirectSurfaceRadiance) &&
			((bsdfType & BSDF::ESmooth) || (hasSpecular && !exhaustiveSpecular));

		/* Sample indirect component via BSDF sampling if this is generally requested AND
		    the BSDF is non-diffuse (diffuse is handled by the global photon map)
			or there is a delta component that was not handled by the exhaustive sampling loop
			above. */
		bool bsdfSampleIndirect = (rRec.type & RadianceQueryRecord::EIndirectSurfaceRadiance) &&
			!isDiffuse && ((bsdfType & BSDF::ESmooth) || (hasSpecular && !exhaustiveSpecular));

		if (bsdfSampleDirect || bsdfSampleIndirect) {
			if (numBSDFSamples > 1) {
				sampleArray = rRec.sampler->next2DArray(
					std::max(m_directSamples, m_glossySamples));
			} else {
				sample = rRec.nextSample2D(); sampleArray = &sample;
			}

			RadianceQueryRecord rRec2;
			Intersection &bsdfIts = rRec2.its;

			DirectSamplingRecord dRec(its);
			for (int i=0; i<numBSDFSamples; ++i) {
				/* Sample BSDF * cos(theta) */
				BSDFSamplingRecord bRec(its, rRec.sampler, ERadiance);
				if (!hasSpecular || exhaustiveSpecular)
					bRec.typeMask = BSDF::ESmooth;

				Float bsdfPdf;
				Spectrum bsdfVal = bsdf->sample(bRec, bsdfPdf, sampleArray[i]);
				if (bsdfVal.isZero())
					continue;

				/* Trace a ray in this direction */
				RayDifferential bsdfRay(its.p, its.toWorld(bRec.wo), ray.time);

				Spectrum value;
				bool hitEmitter = false;
				if (scene->rayIntersect(bsdfRay, bsdfIts)) {
					/* Intersected something - check if it was a luminaire */
					if (bsdfIts.isEmitter() && bsdfSampleDirect) {
						value = bsdfIts.Le(-bsdfRay.d);
						dRec.setQuery(bsdfRay, bsdfIts);
						hitEmitter = true;
					}
				} else if (bsdfSampleDirect) {
					/* Intersected nothing -- perhaps there is an environment map? */
					const Emitter *env = scene->getEnvironmentEmitter();

					if (env) {
						value = env->evalEnvironment(bsdfRay);
						if (env->fillDirectSamplingRecord(dRec, bsdfRay))
							hitEmitter = true;
					}
				}

				if (hitEmitter) {
					const Float emitterPdf = scene->pdfEmitterDirect(dRec);

					Spectrum transmittance = rRec2.medium ?
						rRec2.medium->evalTransmittance(Ray(bsdfRay, 0, bsdfIts.t)) : Spectrum(1.0f);

					const Float weight = miWeight(bsdfPdf * numBSDFSamples,
						emitterPdf * numEmitterSamples) * weightBSDF;

					LiSurf += value * bsdfVal * weight * transmittance;
				}

				/* Recurse */
				if (bsdfSampleIndirect) {
					rRec2.recursiveQuery(rRec,
						RadianceQueryRecord::ERadianceNoEmission);
					rRec2.type ^= RadianceQueryRecord::EIntersection;

					if (its.isMediumTransition())
						rRec2.medium = its.getTargetMedium(bsdfRay.d);

					LiSurf += bsdfVal * m_parentIntegrator->Li(bsdfRay, rRec2) * weightBSDF;
				}
			}
		}

		return LiSurf * transmittance + LiMedium;
	}
Exemplo n.º 17
0
	Spectrum Li(const RayDifferential &r, RadianceQueryRecord &rRec) const {
		/* Some aliases and local variables */
		const Scene *scene = rRec.scene;
		Intersection &its = rRec.its, bsdfIts;
		RayDifferential ray(r);
		LuminaireSamplingRecord lRec;
		Spectrum Li(0.0f);
		Point2 sample;

		/* Perform the first ray intersection (or ignore if the 
		   intersection has already been provided). */
		if (!rRec.rayIntersect(ray)) {
			/* If no intersection could be found, possibly return 
			   radiance from a background luminaire */
			if (rRec.type & RadianceQueryRecord::EEmittedRadiance)
				return scene->LeBackground(ray);
			else
				return Spectrum(0.0f);
		}

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

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

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

		if (EXPECT_NOT_TAKEN(!bsdf)) {
			/* The direct illumination integrator doesn't support
			   surfaces without a BSDF (e.g. medium transitions)
			   -- give up. */
			return Li;
		}

		/* Leave here if direct illumination was not requested */
		if (!(rRec.type & RadianceQueryRecord::EDirectSurfaceRadiance))
			return Li;

		/* ==================================================================== */
		/*                          Luminaire sampling                          */
		/* ==================================================================== */
		bool adaptiveQuery = (rRec.extra & RadianceQueryRecord::EAdaptiveQuery);

		Point2 *sampleArray;
		size_t numLuminaireSamples = m_luminaireSamples,
			   numBSDFSamples = m_bsdfSamples;
		Float fracLum = m_fracLum, fracBSDF = m_fracBSDF,
		      weightLum = m_weightLum, weightBSDF = m_weightBSDF;

		if (rRec.depth > 1 || adaptiveQuery) {
			/* This integrator is used recursively by another integrator.
			   Be less accurate as this sample will not directly be observed. */
			numBSDFSamples = numLuminaireSamples = 1;
			fracLum = fracBSDF = .5f;
			weightLum = weightBSDF = 1.0f;
		}

		if (numLuminaireSamples > 1) {
			sampleArray = rRec.sampler->next2DArray(numLuminaireSamples);
		} else {
			sample = rRec.nextSample2D(); sampleArray = &sample;
		}

		for (size_t i=0; i<numLuminaireSamples; ++i) {
			/* Estimate the direct illumination if this is requested */
			if (scene->sampleLuminaire(its.p, ray.time, lRec, sampleArray[i])) {
				/* Allocate a record for querying the BSDF */
				BSDFQueryRecord bRec(its, its.toLocal(-lRec.d));

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

				if (!bsdfVal.isZero()) {
					/* 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 * fracLum, 
							bsdfPdf * fracBSDF) * weightLum;
					Li += lRec.value * bsdfVal * weight;
				}
			}
		}

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

		if (numBSDFSamples > 1) {
			sampleArray = rRec.sampler->next2DArray(numBSDFSamples);
		} else {
			sample = rRec.nextSample2D(); sampleArray = &sample;
		}

		for (size_t i=0; i<numBSDFSamples; ++i) {
			/* Sample BSDF * cos(theta) */
			BSDFQueryRecord bRec(its, rRec.sampler, ERadiance);
			Float bsdfPdf;
			Spectrum bsdfVal = bsdf->sample(bRec, bsdfPdf, sampleArray[i]);
			if (bsdfVal.isZero())
				continue;

			/* Trace a ray in this direction */
			Ray bsdfRay(its.p, its.toWorld(bRec.wo), ray.time);
			if (scene->rayIntersect(bsdfRay, bsdfIts)) {
				/* Intersected something - check if it was a luminaire */
				if (bsdfIts.isLuminaire()) {
					lRec = LuminaireSamplingRecord(bsdfIts, -bsdfRay.d);
					lRec.value = bsdfIts.Le(-bsdfRay.d);
				} else {
					continue;
				}
			} else {
				/* No intersection found. Possibly, there is a background
				   luminaire such as an environment map? */
				if (scene->hasBackgroundLuminaire()) {
					lRec.luminaire = scene->getBackgroundLuminaire();
					lRec.d = -bsdfRay.d;
					lRec.value = lRec.luminaire->Le(bsdfRay);
				} else {
					continue;
				}
			}

			const Float lumPdf = (!(bRec.sampledType & BSDF::EDelta)) ? 
				scene->pdfLuminaire(its.p, lRec) : 0;
	
			const Float weight = miWeight(bsdfPdf * fracBSDF, 
				lumPdf * fracLum) * weightBSDF;
			Li += lRec.value * bsdfVal * weight;
		}

		return Li;
	}
Exemplo n.º 18
0
void ParticleTracer::process(const WorkUnit *workUnit, WorkResult *workResult,
		const bool &stop) {
	const RangeWorkUnit *range = static_cast<const RangeWorkUnit *>(workUnit);
	MediumSamplingRecord mRec;
	Intersection its;
	ref<Sensor> sensor    = m_scene->getSensor();
	bool needsTimeSample  = sensor->needsTimeSample();
	PositionSamplingRecord pRec(sensor->getShutterOpen()
		+ 0.5f * sensor->getShutterOpenTime());
	Ray ray;

	m_sampler->generate(Point2i(0));

	for (size_t index = range->getRangeStart(); index <= range->getRangeEnd() && !stop; ++index) {
		m_sampler->setSampleIndex(index);

		/* Sample an emission */
		if (needsTimeSample)
			pRec.time = sensor->sampleTime(m_sampler->next1D());

		const Emitter *emitter = NULL;
		const Medium *medium;

		Spectrum power;
		Ray ray;

		if (m_emissionEvents) {
			/* Sample the position and direction component separately to
			   generate emission events */
			power = m_scene->sampleEmitterPosition(pRec, m_sampler->next2D());
			emitter = static_cast<const Emitter *>(pRec.object);
			medium = emitter->getMedium();

			/* Forward the sampling event to the attached handler */
			handleEmission(pRec, medium, power);

			DirectionSamplingRecord dRec;
			power *= emitter->sampleDirection(dRec, pRec,
					emitter->needsDirectionSample() ? m_sampler->next2D() : Point2(0.5f));
			ray.setTime(pRec.time);
			ray.setOrigin(pRec.p);
			ray.setDirection(dRec.d);
		} else {
			/* Sample both components together, which is potentially
			   faster / uses a better sampling strategy */

			power = m_scene->sampleEmitterRay(ray, emitter,
				m_sampler->next2D(), m_sampler->next2D(), pRec.time);
			medium = emitter->getMedium();
			handleNewParticle();
		}

		int depth = 1, nullInteractions = 0;
		bool delta = false;

		Spectrum throughput(1.0f); // unitless path throughput (used for russian roulette)
		while (!throughput.isZero() && (depth <= m_maxDepth || m_maxDepth < 0)) {
			m_scene->rayIntersectAll(ray, its);

            /* ==================================================================== */
            /*                 Radiative Transfer Equation sampling                 */
            /* ==================================================================== */
			if (medium && medium->sampleDistance(Ray(ray, 0, its.t), mRec, m_sampler)) {
				/* Sample the integral
				  \int_x^y tau(x, x') [ \sigma_s \int_{S^2} \rho(\omega,\omega') L(x,\omega') d\omega' ] dx'
				*/

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

				/* Forward the medium scattering event to the attached handler */
				handleMediumInteraction(depth, nullInteractions,
						delta, mRec, medium, -ray.d, throughput*power);

				PhaseFunctionSamplingRecord pRec(mRec, -ray.d, EImportance);

				throughput *= medium->getPhaseFunction()->sample(pRec, m_sampler);
				delta = false;

				ray = Ray(mRec.p, pRec.wo, ray.time);
				ray.mint = 0;
			} else if (its.t == std::numeric_limits<Float>::infinity()) {
				/* There is no surface in this direction */
				break;
			} 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 (medium)
					throughput *= mRec.transmittance / mRec.pdfFailure;

				const BSDF *bsdf = its.getBSDF();

				/* Forward the surface scattering event to the attached handler */
				handleSurfaceInteraction(depth, nullInteractions, delta, its, medium, throughput*power);

				BSDFSamplingRecord bRec(its, m_sampler, EImportance);
				Spectrum bsdfWeight = bsdf->sample(bRec, m_sampler->next2D());
				if (bsdfWeight.isZero())
					break;

				/* Prevent light leaks due to the use of shading normals -- [Veach, p. 158] */
				Vector wi = -ray.d, wo = its.toWorld(bRec.wo);
				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;

				/* Keep track of the weight, medium and relative
				   refractive index along the path */
				throughput *= bsdfWeight;
				if (its.isMediumTransition())
					medium = its.getTargetMedium(woDotGeoN);

				if (bRec.sampledType & BSDF::ENull)
					++nullInteractions;
				else
					delta = bRec.sampledType & BSDF::EDelta;

#if 0
				/* This is somewhat unfortunate: for accuracy, we'd really want the
				   correction factor below to match the path tracing interpretation
				   of a scene with shading normals. However, this factor can become
				   extremely large, which adds unacceptable variance to output
				   renderings.

				   So for now, it is disabled. The adjoint particle tracer and the
				   photon mapping variants still use this factor for the last
				   bounce -- just not for the intermediate ones, which introduces
				   a small (though in practice not noticeable) amount of error. This
				   is also what the implementation of SPPM by Toshiya Hachisuka does.

				   Ultimately, we'll need better adjoint BSDF sampling strategies
				   that incorporate these extra terms */

				/* Adjoint BSDF for shading normals -- [Veach, p. 155] */
				throughput *= std::abs(
					(Frame::cosTheta(bRec.wi) * woDotGeoN)/
					(Frame::cosTheta(bRec.wo) * wiDotGeoN));
#endif

				ray.setOrigin(its.p);
				ray.setDirection(wo);
				ray.mint = Epsilon;
			}

			if (depth++ >= m_rrDepth) {
				/* Russian roulette: try to keep path weights equal to one,
				   Stop with at least some probability to avoid
				   getting stuck (e.g. due to total internal reflection) */

				Float q = std::min(throughput.max(), (Float) 0.95f);
				if (m_sampler->next1D() >= q)
					break;
				throughput /= q;
			}
		}
	}
}
Exemplo n.º 19
0
	void process(const WorkUnit *workUnit, WorkResult *workResult, const bool &stop) {
		ImageBlock *result = static_cast<ImageBlock *>(workResult);
		const SeedWorkUnit *wu = static_cast<const SeedWorkUnit *>(workUnit);
		Path *current = new Path(), *proposed = new Path();
		Spectrum relWeight(0.0f);

		result->clear();

		/// Reconstruct the seed path
		m_pathSampler->reconstructPath(wu->getSeed(), m_config.importanceMap, *current);
		relWeight = current->getRelativeWeight();
		BDAssert(!relWeight.isZero());

		DiscreteDistribution suitabilities(m_mutators.size());
		MutationRecord muRec, currentMuRec(Mutator::EMutationTypeCount,0,0,0,Spectrum(0.f));
		ref<Timer> timer = new Timer();

		size_t consecRejections = 0;
		Float accumulatedWeight = 0;

		#if defined(MTS_DEBUG_FP)
			enableFPExceptions();
		#endif

		#if defined(MTS_BD_DEBUG_HEAVY)
			std::ostringstream oss;
			Path backup;
		#endif
		for (size_t mutationCtr=0; mutationCtr < m_config.nMutations
				&& !stop; ++mutationCtr) {
			if (wu->getTimeout() > 0 && (mutationCtr % 8192) == 0 &&
					(int) timer->getMilliseconds() > wu->getTimeout())
				break;

			/* Query all mutators for their suitability */
			suitabilities.clear();
			for (size_t j=0; j<m_mutators.size(); ++j)
				suitabilities.append(m_mutators[j]->suitability(*current));
			#if defined(MTS_BD_DEBUG_HEAVY)
				current->clone(backup, *m_pool);
			#endif

			size_t mutatorIdx = 0;
			bool success = false;
			Mutator *mutator = NULL;

			if (suitabilities.normalize() == 0) {
				/* No mutator can handle this path -- give up */
				size_t skip = m_config.nMutations - mutationCtr;
				accumulatedWeight += skip;
				consecRejections += skip;
				break;
			}

			mutatorIdx = suitabilities.sample(m_sampler->next1D());
			mutator = m_mutators[mutatorIdx].get();

			/* Sample a mutated path */
			success = mutator->sampleMutation(*current, *proposed, muRec, currentMuRec);

			#if defined(MTS_BD_DEBUG_HEAVY)
				if (backup != *current)
					Log(EError, "Detected an unexpected path modification after a "
						"mutation of type %s (k=%i)!", muRec.toString().c_str(),
						current->length());
				if (success) {
					bool fail = false;
					for (int i=0; i<muRec.l; ++i)
						if (*backup.vertex(i) != *proposed->vertex(i))
							fail = true;

					for (int i=1; i <= backup.length() - muRec.m; ++i)
						if (*backup.vertex(muRec.m+i) != *proposed->vertex(muRec.l+muRec.ka+i))
							fail = true;
					if (fail)
						Log(EError, "Detected an unexpected path modification outside of the "
							"specified range after a mutation of type %s (k=%i)!",
							muRec.toString().c_str(), current->length());
				}
				backup.release(*m_pool);
			#endif

			statsAccepted.incrementBase(1);
			if (success) {
				Float Qxy = mutator->Q(*current, *proposed, muRec) * suitabilities[mutatorIdx];
				suitabilities.clear();
				for (size_t j=0; j<m_mutators.size(); ++j)
					suitabilities.append(m_mutators[j]->suitability(*proposed));
				suitabilities.normalize();
				Float Qyx = mutator->Q(*proposed, *current, muRec.reverse()) * suitabilities[mutatorIdx];

				Float a;
				if (!m_config.importanceMap) {
					if(Qxy > RCPOVERFLOW)
					a = std::min((Float) 1, Qyx / Qxy);
					else
						a = 0.f;
				} else {
					const Float *luminanceValues = m_config.importanceMap->getFloatData();
					const Point2 &curPos = current->getSamplePosition();
					const Point2 &propPos = proposed->getSamplePosition();
					Vector2i size = m_config.importanceMap->getSize();
					Point2i curPosI(
						std::min(std::max(0, (int) curPos.x), size.x-1),
						std::min(std::max(0, (int) curPos.y), size.y-1));
					Point2i propPosI(
						std::min(std::max(0, (int) propPos.x), size.x-1),
						std::min(std::max(0, (int) propPos.y), size.y-1));

					Float curValue = luminanceValues[curPosI.x + curPosI.y * size.x];
					Float propValue = luminanceValues[propPosI.x + propPosI.y * size.x];

					a = std::min((Float) 1, (Qyx * curValue) / (Qxy * propValue));
				}

				#if defined(MTS_BD_DEBUG_HEAVY)
					if (!proposed->verify(m_scene, EImportance, oss)) {
						Log(EWarn, "%s proposed as %s, Qxy=%f, Qyx=%f", oss.str().c_str(),
								muRec.toString().c_str(), Qxy, Qyx);
						proposed->release(muRec.l, muRec.l + muRec.ka + 1, *m_pool);
						oss.str("");
						continue;
					}
				#endif

				if (Qxy == 0) { // be tolerant of this (can occasionally happen due to floating point inaccuracies)
					a = 0;
				} else if (Qxy < 0 || Qyx < 0 || std::isnan(Qxy) || std::isnan(Qyx)) {
					#if defined(MTS_BD_DEBUG)
						Log(EDebug, "Source path: %s", current->toString().c_str());
						Log(EDebug, "Proposal path: %s", proposed->toString().c_str());
						Log(EWarn, "Internal error while computing acceptance probabilities: "
							"Qxy=%f, Qyx=%f, muRec=%s", Qxy, Qyx, muRec.toString().c_str());
					#endif
					a = 0;
				}

				accumulatedWeight += 1-a;

				/* Accept with probability 'a' */
				if (a == 1 || m_sampler->next1D() < a) {
					current->release(muRec.l, muRec.m+1, *m_pool);
					Spectrum value = relWeight * accumulatedWeight;
					if (!value.isZero())
						result->put(current->getSamplePosition(), &value[0]);

					/* The mutation was accepted */
					std::swap(current, proposed);
					relWeight = current->getRelativeWeight();
					mutator->accept(muRec);
					currentMuRec = muRec;
					accumulatedWeight = a;
					consecRejections = 0;
					++statsAccepted;
				} else {
					/* The mutation was rejected */
					proposed->release(muRec.l, muRec.l + muRec.ka + 1, *m_pool);
					consecRejections++;
					if (a > 0) {
						Spectrum value = proposed->getRelativeWeight() * a;
						result->put(proposed->getSamplePosition(), &value[0]);
					}
				}
			} else {
				accumulatedWeight += 1;
				consecRejections++;
			}
		}
		#if defined(MTS_BD_DEBUG)
			if (consecRejections == m_config.nMutations)
				Log(EWarn, "Encountered a path that could *never* be mutated!: %s",
					current->toString().c_str());
		#endif

		if (accumulatedWeight > 0) {
			Spectrum value = relWeight * accumulatedWeight;
			result->put(current->getSamplePosition(), &value[0]);
		}

		#if defined(MTS_DEBUG_FP)
			disableFPExceptions();
		#endif

		current->release(*m_pool);
		delete current;
		delete proposed;
		if (!m_pool->unused())
			Log(EError, "Internal error: detected a memory pool leak!");
	}
Exemplo n.º 20
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;
	}
Exemplo n.º 21
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;
	}
	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;
	}