/* This file is part of Mitsuba, a physically based rendering system. Copyright (c) 2007-2012 by Wenzel Jakob and others. Mitsuba is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License Version 3 as published by the Free Software Foundation. Mitsuba is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include MTS_NAMESPACE_BEGIN void Path::initialize(const Scene *scene, Float time, ETransportMode mode, MemoryPool &pool) { release(pool); m_vertices.push_back(pool.allocVertex()); m_vertices[0]->makeEndpoint(scene, time, mode); } int Path::randomWalk(const Scene *scene, Sampler *sampler, int nSteps, int rrStart, ETransportMode mode, MemoryPool &pool) { /* Determine the relevant edge and vertex to start the random walk */ PathVertex *curVertex = m_vertices[m_vertices.size()-1], *predVertex = m_vertices.size() < 2 ? NULL : m_vertices[m_vertices.size()-2]; PathEdge *predEdge = m_edges.empty() ? NULL : m_edges[m_edges.size()-1]; Spectrum throughput(1.0f); for (int i=0; isampleNext(scene, sampler, predVertex, predEdge, succEdge, succVertex, mode, rrStart != -1 && i >= rrStart, &throughput)) { pool.release(succVertex); pool.release(succEdge); return i; } append(succEdge, succVertex); predVertex = curVertex; curVertex = succVertex; predEdge = succEdge; } return nSteps; } int Path::randomWalkFromPixel(const Scene *scene, Sampler *sampler, int nSteps, const Point2i &pixelPosition, int rrStart, MemoryPool &pool) { PathVertex *v1 = pool.allocVertex(), *v2 = pool.allocVertex(); PathEdge *e0 = pool.allocEdge(), *e1 = pool.allocEdge(); /* Use a special sampling routine for the first two sensor vertices so that the resulting subpath passes through the specified pixel position */ int t = vertex(0)->sampleSensor(scene, sampler, pixelPosition, e0, v1, e1, v2); if (t < 1) { pool.release(e0); pool.release(v1); return 0; } append(e0, v1); if (t < 2) { pool.release(e1); pool.release(v2); return 1; } append(e1, v2); PathVertex *predVertex = v1, *curVertex = v2; PathEdge *predEdge = e1; Spectrum throughput(1.0f); for (; tsampleNext(scene, sampler, predVertex, predEdge, succEdge, succVertex, ERadiance, rrStart != -1 && t >= rrStart, &throughput)) { pool.release(succVertex); pool.release(succEdge); return t; } append(succEdge, succVertex); predVertex = curVertex; curVertex = succVertex; predEdge = succEdge; } return nSteps; } std::pair Path::alternatingRandomWalkFromPixel(const Scene *scene, Sampler *sampler, Path &emitterPath, int nEmitterSteps, Path &sensorPath, int nSensorSteps, const Point2i &pixelPosition, int rrStart, MemoryPool &pool) { /* Determine the relevant edges and vertices to start the random walk */ PathVertex *curVertexS = emitterPath.vertex(0), *curVertexT = sensorPath.vertex(0), *predVertexS = NULL, *predVertexT = NULL; PathEdge *predEdgeS = NULL, *predEdgeT = NULL; PathVertex *v1 = pool.allocVertex(), *v2 = pool.allocVertex(); PathEdge *e0 = pool.allocEdge(), *e1 = pool.allocEdge(); /* Use a special sampling routine for the first two sensor vertices so that the resulting subpath passes through the specified pixel position */ int t = curVertexT->sampleSensor(scene, sampler, pixelPosition, e0, v1, e1, v2); if (t >= 1) { sensorPath.append(e0, v1); } else { pool.release(e0); pool.release(v1); } if (t == 2) { sensorPath.append(e1, v2); predVertexT = v1; curVertexT = v2; predEdgeT = e1; } else { pool.release(e1); pool.release(v2); curVertexT = NULL; } Spectrum throughputS(1.0f), throughputT(1.0f); int s = 0; do { if (curVertexT && (t < nSensorSteps || nSensorSteps == -1)) { PathVertex *succVertexT = pool.allocVertex(); PathEdge *succEdgeT = pool.allocEdge(); if (curVertexT->sampleNext(scene, sampler, predVertexT, predEdgeT, succEdgeT, succVertexT, ERadiance, rrStart != -1 && t >= rrStart, &throughputT)) { sensorPath.append(succEdgeT, succVertexT); predVertexT = curVertexT; curVertexT = succVertexT; predEdgeT = succEdgeT; t++; } else { pool.release(succVertexT); pool.release(succEdgeT); curVertexT = NULL; } } else { curVertexT = NULL; } if (curVertexS && (s < nEmitterSteps || nEmitterSteps == -1)) { PathVertex *succVertexS = pool.allocVertex(); PathEdge *succEdgeS = pool.allocEdge(); if (curVertexS->sampleNext(scene, sampler, predVertexS, predEdgeS, succEdgeS, succVertexS, EImportance, rrStart != -1 && s >= rrStart, &throughputS)) { emitterPath.append(succEdgeS, succVertexS); predVertexS = curVertexS; curVertexS = succVertexS; predEdgeS = succEdgeS; s++; } else { pool.release(succVertexS); pool.release(succEdgeS); curVertexS = NULL; } } else { curVertexS = NULL; } } while (curVertexS || curVertexT); return std::make_pair(s, t); } void Path::reverse() { std::reverse(m_vertices.begin(), m_vertices.end()); std::reverse(m_edges.begin(), m_edges.end()); } void Path::release(MemoryPool &pool) { for (size_t i=0; iclone(pool)); for (size_t i=0; iclone(pool)); } void Path::append(const Path &path) { for (size_t i=0; iisConnectable(); isNull[pos] = v->isNullInteraction() && !connectable[pos]; pos++; } for (int i=t; i>=0; --i) { const PathVertex *v = sensorSubpath.vertex(i); connectable[pos] = v->isConnectable(); isNull[pos] = v->isNullInteraction() && !connectable[pos]; pos++; } if (k <= 3) sampleDirect = false; EMeasure vsMeasure = EArea, vtMeasure = EArea; if (sampleDirect) { /* When direct sampling is enabled, we may be able to create certain connections that otherwise would have failed (e.g. to an orthographic camera or a directional light source) */ const AbstractEmitter *emitter = (s > 0 ? emitterSubpath.vertex(1) : vt)->getAbstractEmitter(); const AbstractEmitter *sensor = (t > 0 ? sensorSubpath.vertex(1) : vs)->getAbstractEmitter(); EMeasure emitterDirectMeasure = emitter->getDirectMeasure(); EMeasure sensorDirectMeasure = sensor->getDirectMeasure(); connectable[0] = emitterDirectMeasure != EDiscrete && emitterDirectMeasure != EInvalidMeasure; connectable[1] = emitterDirectMeasure != EInvalidMeasure; connectable[k-1] = sensorDirectMeasure != EInvalidMeasure; connectable[k] = sensorDirectMeasure != EDiscrete && sensorDirectMeasure != EInvalidMeasure; /* The following is needed to handle orthographic cameras & directional light sources together with direct sampling */ if (t == 1) vtMeasure = sensor->needsDirectionSample() ? EArea : EDiscrete; else if (s == 1) vsMeasure = emitter->needsDirectionSample() ? EArea : EDiscrete; } /* Collect importance transfer area/volume densities from vertices */ pos = 0; pdfImp[pos++] = 1.0; for (int i=0; ipdf[EImportance] * emitterSubpath.edge(i)->pdf[EImportance]; pdfImp[pos++] = vs->evalPdf(scene, vsPred, vt, EImportance, vsMeasure) * connectionEdge->pdf[EImportance]; if (t > 0) { pdfImp[pos++] = vt->evalPdf(scene, vs, vtPred, EImportance, vtMeasure) * sensorSubpath.edge(t-1)->pdf[EImportance]; for (int i=t-1; i>0; --i) pdfImp[pos++] = sensorSubpath.vertex(i)->pdf[EImportance] * sensorSubpath.edge(i-1)->pdf[EImportance]; } /* Collect radiance transfer area/volume densities from vertices */ pos = 0; if (s > 0) { for (int i=0; ipdf[ERadiance] * emitterSubpath.edge(i)->pdf[ERadiance]; pdfRad[pos++] = vs->evalPdf(scene, vt, vsPred, ERadiance, vsMeasure) * emitterSubpath.edge(s-1)->pdf[ERadiance]; } pdfRad[pos++] = vt->evalPdf(scene, vtPred, vs, ERadiance, vtMeasure) * connectionEdge->pdf[ERadiance]; for (int i=t; i>0; --i) pdfRad[pos++] = sensorSubpath.vertex(i-1)->pdf[ERadiance] * sensorSubpath.edge(i-1)->pdf[ERadiance]; pdfRad[pos++] = 1.0; /* When the path contains specular surface interactions, it is possible to compute the correct MI weights even without going through all the trouble of computing the proper generalized geometric terms (described in the SIGGRAPH 2012 specular manifolds paper). The reason is that these all cancel out. But to make sure that that's actually true, we need to convert some of the area densities in the 'pdfRad' and 'pdfImp' arrays into the projected solid angle measure */ for (int i=1; i <= k-3; ++i) { if (i == s || !(connectable[i] && !connectable[i+1])) continue; const PathVertex *cur = i <= s ? emitterSubpath.vertex(i) : sensorSubpath.vertex(k-i); const PathVertex *succ = i+1 <= s ? emitterSubpath.vertex(i+1) : sensorSubpath.vertex(k-i-1); const PathEdge *edge = i < s ? emitterSubpath.edge(i) : sensorSubpath.edge(k-i-1); pdfImp[i+1] *= edge->length * edge->length / std::abs( (succ->isOnSurface() ? dot(edge->d, succ->getGeometricNormal()) : 1) * (cur->isOnSurface() ? dot(edge->d, cur->getGeometricNormal()) : 1)); } for (int i=k-1; i >= 3; --i) { if (i-1 == s || !(connectable[i] && !connectable[i-1])) continue; const PathVertex *cur = i <= s ? emitterSubpath.vertex(i) : sensorSubpath.vertex(k-i); const PathVertex *succ = i-1 <= s ? emitterSubpath.vertex(i-1) : sensorSubpath.vertex(k-i+1); const PathEdge *edge = i <= s ? emitterSubpath.edge(i-1) : sensorSubpath.edge(k-i); pdfRad[i-1] *= edge->length * edge->length / std::abs( (succ->isOnSurface() ? dot(edge->d, succ->getGeometricNormal()) : 1) * (cur->isOnSurface() ? dot(edge->d, cur->getGeometricNormal()) : 1)); } int emitterRefIndirection = 2, sensorRefIndirection = k-2; /* One more array sweep before the actual useful work starts -- phew! :) "Collapse" edges/vertices that were caused by BSDF::ENull interactions. The BDPT implementation is smart enough to connect straight through those, so they shouldn't be treated as Dirac delta events in what follows */ for (int i=1; i <= k-3; ++i) { if (!connectable[i] || !isNull[i+1]) continue; int start = i+1, end = start; while (isNull[end+1]) ++end; if (!connectable[end+1]) { /// The chain contains a non-ENull interaction isNull[start] = false; continue; } const PathVertex *before = i <= s ? emitterSubpath.vertex(i) : sensorSubpath.vertex(k-i); const PathVertex *after = end+1 <= s ? emitterSubpath.vertex(end+1) : sensorSubpath.vertex(k-end-1); Vector d = before->getPosition() - after->getPosition(); Float lengthSquared = d.lengthSquared(); d /= std::sqrt(lengthSquared); Float geoTerm = std::abs( (before->isOnSurface() ? dot(before->getGeometricNormal(), d) : 1) * (after->isOnSurface() ? dot(after->getGeometricNormal(), d) : 1)) / lengthSquared; pdfRad[start-1] *= pdfRad[end] * geoTerm; pdfRad[end] = 1; pdfImp[start] *= pdfImp[end+1] * geoTerm; pdfImp[end+1] = 1; /* When an ENull chain starts right after the emitter / before the sensor, we must keep track of the reference vertex for direct sampling strategies. */ if (start == 2) emitterRefIndirection = end + 1; else if (end == k-2) sensorRefIndirection = start - 1; i = end; } double initial = 1.0f; /* When direct sampling strategies are enabled, we must account for them here as well */ if (sampleDirect) { /* Direct connection probability of the emitter */ const PathVertex *sample = s>0 ? emitterSubpath.vertex(1) : vt; const PathVertex *ref = emitterRefIndirection <= s ? emitterSubpath.vertex(emitterRefIndirection) : sensorSubpath.vertex(k-emitterRefIndirection); EMeasure measure = sample->getAbstractEmitter()->getDirectMeasure(); if (connectable[1] && connectable[emitterRefIndirection]) ratioEmitterDirect = ref->evalPdfDirect(scene, sample, EImportance, measure == ESolidAngle ? EArea : measure) / pdfImp[1]; /* Direct connection probability of the sensor */ sample = t>0 ? sensorSubpath.vertex(1) : vs; ref = sensorRefIndirection <= s ? emitterSubpath.vertex(sensorRefIndirection) : sensorSubpath.vertex(k-sensorRefIndirection); measure = sample->getAbstractEmitter()->getDirectMeasure(); if (connectable[k-1] && connectable[sensorRefIndirection]) ratioSensorDirect = ref->evalPdfDirect(scene, sample, ERadiance, measure == ESolidAngle ? EArea : measure) / pdfRad[k-1]; if (s == 1) initial /= ratioEmitterDirect; else if (t == 1) initial /= ratioSensorDirect; } double weight = 1, pdf = initial; /* With all of the above information, the MI weight can now be computed. Since the goal is to evaluate the power heuristic, the absolute area product density of each strategy is interestingly not required. Instead, an incremental scheme can be used that only finds the densities relative to the (s,t) strategy, which can be done using a linear sweep. For details, refer to the Veach thesis, p.306. */ for (int i=s+1; i 1)) weight += value*value; pdf = next; } /* As above, but now compute pdf[i] with i=0; --i) { double next = pdf * (double) pdfRad[i+1] / (double) pdfImp[i+1], value = next; if (sampleDirect) { if (i == 1) value *= ratioEmitterDirect; else if (i == sensorRefIndirection) value *= ratioSensorDirect; } int tPrime = k-i-1; if (connectable[i] && (connectable[i+1] || isNull[i+1]) && (lightImage || tPrime > 1)) weight += value*value; pdf = next; } return (Float) (1.0 / weight); } void Path::collapseTo(PathEdge &target) const { BDAssert(m_edges.size() > 0); target.pdf[ERadiance] = 1.0f; target.pdf[EImportance] = 1.0f; target.weight[ERadiance] = Spectrum(1.0f); target.weight[EImportance] = Spectrum(1.0f); target.d = m_edges[0]->d; target.medium = m_edges[0]->medium; target.length = 0; for (size_t i=0; iweight[ERadiance]; target.weight[EImportance] *= edge->weight[EImportance]; target.pdf[ERadiance] *= edge->pdf[ERadiance]; target.pdf[EImportance] *= edge->pdf[EImportance]; target.length += edge->length; if (target.medium != edge->medium) target.medium = NULL; } for (size_t i=0; iisSurfaceInteraction() && vertex->componentType == BSDF::ENull); target.weight[ERadiance] *= vertex->weight[ERadiance]; target.weight[EImportance] *= vertex->weight[EImportance]; target.pdf[ERadiance] *= vertex->pdf[ERadiance]; target.pdf[EImportance] *= vertex->pdf[EImportance]; } } std::string Path::toString() const { std::ostringstream oss; oss << "Path[" << endl; if (m_vertices.size() == m_edges.size() + 1) { for (size_t i=0; i " << indent(m_vertices[i]->toString()); if (i " << indent(m_edges[i]->toString()); } if (i+1 < m_vertices.size()) oss << ","; oss << endl; } } else if (m_edges.size() == m_vertices.size() + 1) { for (size_t i=0; i " << indent(m_edges[i]->toString()); if (i " << indent(m_vertices[i]->toString()); } if (i+1 < m_edges.size()) oss << ","; oss << endl; } } else { SLog(EError, "Unknown path configuration!"); } oss << "]"; return oss.str(); } std::string Path::summarize() const { std::ostringstream oss; BDAssert(m_vertices.size() == m_edges.size() + 1); for (size_t i=0; itype) { case PathVertex::EEmitterSupernode: oss << "E"; break; case PathVertex::ESensorSupernode: oss << "C"; break; case PathVertex::EEmitterSample: oss << "e"; break; case PathVertex::ESensorSample: oss << "c"; break; case PathVertex::ESurfaceInteraction: oss << "S"; break; case PathVertex::EMediumInteraction: oss << "M"; break; default: SLog(EError, "Unknown vertex typ!"); } if (!vertex->isConnectable()) oss << "d"; if (imedium == NULL) oss << " - "; else oss << " = "; } } return oss.str(); } MTS_NAMESPACE_END