/* This file is part of Mitsuba, a physically based rendering system. Copyright (c) 2007-2014 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 #include MTS_NAMESPACE_BEGIN static StatsCounter statsAccepted("Caustic perturbation", "Acceptance rate", EPercentage); static StatsCounter statsGenerated("Caustic perturbation", "Successful generation rate", EPercentage); CausticPerturbation::CausticPerturbation(const Scene *scene, Sampler *sampler, MemoryPool &pool, Float minJump, Float coveredArea) : m_scene(scene), m_sampler(sampler), m_pool(pool) { if (!scene->getSensor()->getClass()->derivesFrom(MTS_CLASS(PerspectiveCamera))) Log(EError, "The caustic perturbation requires a perspective camera."); const PerspectiveCamera *camera = static_cast(scene->getSensor()); Vector2i filmSize = camera->getFilm()->getSize(), cropSize = camera->getFilm()->getCropSize(); /* Simple heuristic for choosing a jump size: assumes that each pixel on the camera subtends the same area on the sphere */ Float degPerPixel = std::min( camera->getXFov() / filmSize.x, camera->getYFov() / filmSize.y), radPerPixel = degPerPixel * M_PI / 180.0f; Float r1 = minJump, r2 = std::sqrt(coveredArea * cropSize.x*cropSize.y / M_PI); /* [Veach, p. 354] */ /* These represent the *desired* angle change range as seen from the camera */ m_theta1 = radPerPixel * r1; m_theta2 = radPerPixel * r2; m_logRatio = -math::fastlog(m_theta2 / m_theta1); } CausticPerturbation::~CausticPerturbation() { } Mutator::EMutationType CausticPerturbation::getType() const { return ECausticPerturbation; } Float CausticPerturbation::suitability(const Path &path) const { int k = path.length(), m = k - 1, l = m - 1; if (k < 4 || !path.vertex(l)->isConnectable()) return false; --l; while (l >= 0 && !path.vertex(l)->isConnectable()) --l; return l >= 1 ? 1.0f : 0.0f; } bool CausticPerturbation::sampleMutation( Path &source, Path &proposal, MutationRecord &muRec, const MutationRecord& sourceMuRec) { 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; ilength; 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; iclone(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; ilength + 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; } 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; ilength; 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; ievalCached(v0, v1, PathEdge::ETransmittance | PathEdge::EValueCosineImp); if (v1->isMediumInteraction()) weight /= pdfMediumPerturbation(source.vertex(i+1), source.edge(i), edge); } const Float lumWeight = weight.getLuminance(); if(lumWeight <= RCPOVERFLOW) return 0.f; return solidAngleDensity / lumWeight; } void CausticPerturbation::accept(const MutationRecord &) { ++statsAccepted; } MTS_IMPLEMENT_CLASS(CausticPerturbation, false, Mutator) MTS_NAMESPACE_END