Float BidirectionalMutator::Q(const Path &source, const Path &proposal, const MutationRecord &muRec) const { const int k = source.length(), l = muRec.l, m = muRec.m, ka = muRec.ka, mPrime = l+ka; Spectrum *importanceWeights = (Spectrum *) alloca(ka * sizeof(Spectrum)), *radianceWeights = (Spectrum *) alloca(ka * sizeof(Spectrum)); /* Compute importance transport weights along the subpath */ importanceWeights[0] = Spectrum(1.0f); for (int s=1; s<ka; ++s) importanceWeights[s] = importanceWeights[s-1] * proposal.vertex(l+s-1)->weight[EImportance] * proposal.edge(l+s-1)->weight[EImportance]; /* Compute radiance transport weights along the subpath */ radianceWeights[0] = Spectrum(1.0f); for (int t=1; t<ka; ++t) radianceWeights[t] = radianceWeights[t-1] * proposal.vertex(mPrime-t+1)->weight[ERadiance] * proposal.edge(mPrime-t)->weight[ERadiance]; int sMin = 0, sMax = ka-1; if (l == 0 && m_scene->hasDegenerateEmitters()) ++sMin; else if (m == k && m_scene->hasDegenerateSensor()) --sMax; Float result = 0.0f; for (int s = sMin; s <= sMax; ++s) { const PathEdge *edge = proposal.edge(l+s); const PathVertex *vs = proposal.vertex(l+s), *vt = proposal.vertex(l+s+1); int t = ka - s - 1; /* Cannot connect endpoints with degenerate distributions */ if (!vs->isConnectable() || !vt->isConnectable()) continue; Spectrum weight = importanceWeights[s] * radianceWeights[t] * edge->evalCached(vs, vt, PathEdge::EEverything) * muRec.weight; Float luminance = weight.getLuminance(); if (luminance <= 0 || !std::isfinite(luminance)) { Log(EWarn, "Internal error: luminance = %f!", luminance); continue; } result += 1 / luminance; } return result * pmfMutation(source, muRec); }
Float CausticPerturbation::Q(const Path &source, const Path &proposal, const MutationRecord &muRec) const { int m = muRec.m, l = muRec.l; /* 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 d1 = normalize(source.vertex(l+1)->getPosition() - source.vertex(l)->getPosition()); Vector d2 = normalize(proposal.vertex(l+1)->getPosition() - source.vertex(l)->getPosition()); Float theta = unitAngle(d1, d2); if (theta >= theta2 || theta <= theta1) return 0.0f; Float solidAngleDensity = 1.0f / (2*M_PI * -m_logRatio * std::sin(theta) * theta); Spectrum weight = muRec.weight * proposal.edge(m-1)->evalCached( proposal.vertex(m-1), proposal.vertex(m), PathEdge::EEverything); for (int i=l; i<m-1; ++i) { const PathVertex *v0 = proposal.vertex(i), *v1 = proposal.vertex(i+1); const PathEdge *edge = proposal.edge(i); weight *= edge->evalCached(v0, v1, PathEdge::ETransmittance | PathEdge::EValueCosineImp); if (v1->isMediumInteraction()) weight /= pdfMediumPerturbation(source.vertex(i+1), source.edge(i), edge); } return solidAngleDensity / weight.getLuminance(); }