Example #1
0
bool CausticPerturbation::sampleMutation(
		Path &source, Path &proposal, MutationRecord &muRec) {
	int k = source.length(), m = k - 1, l = m - 1;

	if (k < 4 || !source.vertex(l)->isConnectable())
		return false;
	--l;

	while (l >= 0 && !source.vertex(l)->isConnectable())
		--l;

	if (l < 1)
		return false;

	muRec = MutationRecord(ECausticPerturbation, l, m, m-l,
		source.getPrefixSuffixWeight(l, m));
	statsAccepted.incrementBase();
	statsGenerated.incrementBase();

	/* Heuristic perturbation size computation (Veach, p.354) */
	Float lengthE = source.edge(m-1)->length;
	Float lengthL = 0;
	for (int i=l; i<m-1; ++i)
		lengthL += source.edge(i)->length;
	Float factor = lengthE/lengthL,
		theta1 = m_theta1 * factor,
		theta2 = m_theta2 * factor;

	Vector woSource = normalize(source.vertex(l+1)->getPosition()
			- source.vertex(l)->getPosition());
	Float phi = m_sampler->next1D() * 2 * M_PI;
	Float theta = theta2 * math::fastexp(m_logRatio * m_sampler->next1D());
	Vector wo = Frame(woSource).toWorld(sphericalDirection(theta, phi));

	/* Allocate memory for the proposed path */
	proposal.clear();
	proposal.append(source, 0, l+1);
	proposal.append(m_pool.allocEdge());
	for (int i=l+1; i<m; ++i) {
		proposal.append(m_pool.allocVertex());
		proposal.append(m_pool.allocEdge());
	}
	proposal.append(source, m, k+1);
	proposal.vertex(l) = proposal.vertex(l)->clone(m_pool);
	proposal.vertex(m) = proposal.vertex(m)->clone(m_pool);
	BDAssert(proposal.vertexCount() == source.vertexCount());
	BDAssert(proposal.edgeCount() == source.edgeCount());

	Float dist = source.edge(l)->length +
		perturbMediumDistance(m_sampler, source.vertex(l+1));

	/* Sample a perturbation and propagate it through specular interactions */
	if (!proposal.vertex(l)->perturbDirection(m_scene,
			proposal.vertex(l-1), proposal.edge(l-1),
			proposal.edge(l), proposal.vertex(l+1), wo, dist,
			source.vertex(l+1)->getType(), EImportance)) {
		proposal.release(l, m+1, m_pool);
		return false;
	}

	Vector woProposal = normalize(proposal.vertex(l+1)->getPosition()
			- source.vertex(l)->getPosition());
	theta = unitAngle(woSource, woProposal);
	if (theta >= theta2 || theta <= theta1) {
		proposal.release(l, m+1, m_pool);
		return false;
	}

	/* If necessary, propagate the perturbation through a sequence of
	   ideally specular interactions */
	for (int i=l+1; i<m-1; ++i) {
		Float dist = source.edge(i)->length +
			perturbMediumDistance(m_sampler, source.vertex(i+1));

		if (!proposal.vertex(i)->propagatePerturbation(m_scene,
				proposal.vertex(i-1), proposal.edge(i-1),
				proposal.edge(i), proposal.vertex(i+1),
				source.vertex(i)->getComponentType(), dist,
				source.vertex(i+1)->getType(), EImportance)) {
			proposal.release(l, m+1, m_pool);
			return false;
		}
	}

	if (!PathVertex::connect(m_scene,
			proposal.vertex(m-2),
			proposal.edge(m-2),
			proposal.vertex(m-1),
			proposal.edge(m-1),
			proposal.vertex(m),
			proposal.edge(m),
			proposal.vertex(m+1))) {
		proposal.release(l, m+1, m_pool);
		return false;
	}

	proposal.vertex(k-1)->updateSamplePosition(
		proposal.vertex(k-2));

	++statsGenerated;
	return true;
}
Example #2
0
bool BidirectionalMutator::sampleMutation(
		Path &source, Path &proposal, MutationRecord &muRec) {
	TwoTailedGeoDistr desiredLength(2), deletionLength(2);
	int k = source.length();

	/* Sample the desired path length of the proposal. This
	   is done using a two-tailed geometric distribution that is
	   centered around the current path length, and which respects
	   the specified minimum and maximum length constraints. */

	desiredLength.configure(k, m_kmin, m_kmax);
	int kPrime = desiredLength.sample(m_sampler->next1D());

	/* Sample the length of the deletion (in # of edges, 1 means
	   no vertices are removed). When kPrime is smaller than k,
	   we must delete at least k-kPrime+1 edges to be able to
	   achieve the desired path length.

	   When k==kPrime, we must delete *something*, or the mutation
	   is trivial, hence the conditional below expression. */

	int minDeletion = std::max((k == kPrime) ? 2 : 1, k-kPrime+1);
	deletionLength.configure(2, minDeletion, k);
	int kd = deletionLength.sample(m_sampler->next1D());

	/* Based on the desired length, this tells us how many
	   edges need to be added (k' = k - kd + ka) */
	int ka = kPrime-k+kd;

	/* Sample the left endpoint of the deleted range */
	int lMin = 0, lMax = k - kd;
	if (kd == 1 || ka == 1) {
		/* This will help to avoid certain path changes that would otherwise
		   always be rejected. Specifically, we don't want to remove the sensor
		   or emitter sample vertex, and we don't want to insert
		   vertices between a sensor/emitter sample and its supernode */
		lMin++; lMax--;
	}
	m_temp.clear();
	for (int l=lMin; l<=lMax; ++l) {
		int m = l+kd;
		if (!source.vertex(l)->isDegenerate() &&
			!source.vertex(m)->isDegenerate())
			m_temp.push_back(l);
	}
	if (m_temp.size() == 0)
		return false;

	int l = m_temp[std::min((int) (m_temp.size() *
			m_sampler->next1D()), (int) m_temp.size()-1)];
	int m = l+kd;

	/* Don't try to hit the emitter or sensor if they are degenerate */
	int sMin = 0, sMax = ka-1;
	if (l == 0 && m_scene->hasDegenerateEmitters())
		++sMin;
	else if (m == k && m_scene->hasDegenerateSensor())
		--sMax;

	/* Sample the number of SIS-type steps to take from the emitter direction */
	int s = std::min(sMin + (int) ((sMax-sMin+1) * m_sampler->next1D()), sMax);
	int t = ka - s - 1;

	/* Check a few assumptions */
	BDAssert(ka >= 1 && kd >= 1 && kd <= k
			&& l >= lMin && l <= lMax
			&& kPrime == k - kd + ka
			&& kPrime >= m_kmin
			&& kPrime <= m_kmax);

	/* Construct a mutation record */
	muRec = MutationRecord(EBidirectionalMutation, l, m, ka,
		source.getPrefixSuffixWeight(l, m));

	/* Keep some statistics */
	statsGenerated.incrementBase();
	statsAccepted.incrementBase();

	proposal.clear();
	proposal.append(source, 0, l+1);
	proposal.vertex(l) = proposal.vertex(l)->clone(m_pool);

	/* Perform a random walk from the emitter direction */
	if (proposal.randomWalk(m_scene, m_sampler, s, -1, EImportance, m_pool) != s) {
		proposal.release(l, proposal.vertexCount(), m_pool);
		return false;
	}

	/* Perform a random walk from the sensor direction */
	m_tempPath.clear();
	m_tempPath.append(source, m, k+1, true);
	m_tempPath.vertex(k-m) = m_tempPath.vertex(k-m)->clone(m_pool);

	if (m_tempPath.randomWalk(m_scene, m_sampler, t, -1, ERadiance, m_pool) != t) {
		proposal.release(l, proposal.vertexCount(), m_pool);
		m_tempPath.release(k-m, m_tempPath.vertexCount(), m_pool);
		return false;
	}

	PathEdge *connectionEdge = m_pool.allocEdge();
	proposal.append(connectionEdge);
	proposal.append(m_tempPath, 0, m_tempPath.vertexCount(), true);

	BDAssert(proposal.length() == kPrime &&
			 proposal.vertexCount() == proposal.edgeCount() + 1);

	const PathVertex
		*vsPred = l+s > 0 ? proposal.vertex(l+s-1) : NULL,
		*vtPred = l+s+2 <= kPrime ? proposal.vertex(l+s+2) : NULL;
	const PathEdge
		*vsEdge = l+s > 0 ? proposal.edge(l+s-1) : NULL,
		*vtEdge = l+s+1 < kPrime ? proposal.edge(l+s+1) : NULL;

	/* Now try to connect the two subpaths and reject
	   the proposal if there is no throughput */
	PathVertex *vs = proposal.vertex(l+s),
			   *vt = proposal.vertex(l+s+1);

	if (!PathVertex::connect(m_scene, vsPred,
			vsEdge, vs, connectionEdge, vt, vtEdge, vtPred)) {
		proposal.release(l, l+ka+1, m_pool);
		return false;
	}

	if (m >= k-1)
		proposal.vertex(kPrime-1)->updateSamplePosition(
			proposal.vertex(kPrime-2));

	++statsGenerated;
	return true;
}