197 lines
6.4 KiB
C++
197 lines
6.4 KiB
C++
/*
|
|
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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <mitsuba/render/photonmap.h>
|
|
#include <mitsuba/render/scene.h>
|
|
#include <mitsuba/render/phase.h>
|
|
#include <fstream>
|
|
|
|
MTS_NAMESPACE_BEGIN
|
|
|
|
PhotonMap::PhotonMap(size_t photonCount)
|
|
: m_kdtree(0, PhotonTree::ESlidingMidpoint), m_scale(1.0f) {
|
|
m_kdtree.reserve(photonCount);
|
|
Assert(Photon::m_precompTableReady);
|
|
}
|
|
|
|
PhotonMap::PhotonMap(Stream *stream, InstanceManager *manager)
|
|
: SerializableObject(stream, manager),
|
|
m_kdtree(0, PhotonTree::ESlidingMidpoint) {
|
|
Assert(Photon::m_precompTableReady);
|
|
m_scale = (Float) stream->readFloat();
|
|
m_kdtree.resize(stream->readSize());
|
|
m_kdtree.setDepth(stream->readSize());
|
|
m_kdtree.setAABB(AABB(stream));
|
|
for (size_t i=0; i<m_kdtree.size(); ++i)
|
|
m_kdtree[i] = Photon(stream);
|
|
}
|
|
|
|
void PhotonMap::serialize(Stream *stream, InstanceManager *manager) const {
|
|
Log(EDebug, "Serializing a photon map (%s)",
|
|
memString(m_kdtree.size() * sizeof(Photon)).c_str());
|
|
stream->writeFloat(m_scale);
|
|
stream->writeSize(m_kdtree.size());
|
|
stream->writeSize(m_kdtree.getDepth());
|
|
m_kdtree.getAABB().serialize(stream);
|
|
for (size_t i=0; i<m_kdtree.size(); ++i)
|
|
m_kdtree[i].serialize(stream);
|
|
}
|
|
|
|
PhotonMap::~PhotonMap() {
|
|
}
|
|
|
|
std::string PhotonMap::toString() const {
|
|
std::ostringstream oss;
|
|
oss << "PhotonMap[" << endl
|
|
<< " size = " << m_kdtree.size() << "," << endl
|
|
<< " capacity = " << m_kdtree.capacity() << "," << endl
|
|
<< " aabb = " << m_kdtree.getAABB().toString() << "," << endl
|
|
<< " depth = " << m_kdtree.getDepth() << "," << endl
|
|
<< " scale = " << m_scale << endl
|
|
<< "]";
|
|
return oss.str();
|
|
}
|
|
void PhotonMap::dumpOBJ(const std::string &filename) {
|
|
std::ofstream os(filename.c_str());
|
|
os << "o Photons" << endl;
|
|
for (size_t i=0; i<m_kdtree.size(); ++i) {
|
|
const Point &p = m_kdtree[i].getPosition();
|
|
os << "v " << p.x << " " << p.y << " " << p.z << endl;
|
|
}
|
|
|
|
/// Need to generate some fake geometry so that blender will import the points
|
|
for (size_t i=3; i<=m_kdtree.size(); i++)
|
|
os << "f " << i << " " << i-1 << " " << i-2 << endl;
|
|
os.close();
|
|
}
|
|
|
|
Spectrum PhotonMap::estimateIrradiance(
|
|
const Point &p, const Normal &n,
|
|
Float searchRadius, int maxDepth,
|
|
size_t maxPhotons) const {
|
|
SearchResult *results = static_cast<SearchResult *>(
|
|
alloca((maxPhotons+1) * sizeof(SearchResult)));
|
|
Float squaredRadius = searchRadius*searchRadius;
|
|
size_t resultCount = nnSearch(p, squaredRadius, maxPhotons, results);
|
|
Float invSquaredRadius = 1.0f / squaredRadius;
|
|
|
|
/* Sum over all contributions */
|
|
Spectrum result(0.0f);
|
|
for (size_t i=0; i<resultCount; i++) {
|
|
const SearchResult &searchResult = results[i];
|
|
const Photon &photon = m_kdtree[searchResult.index];
|
|
if (photon.getDepth() > maxDepth)
|
|
continue;
|
|
|
|
Vector wi = -photon.getDirection();
|
|
Vector photonNormal = photon.getNormal();
|
|
Float wiDotGeoN = dot(photonNormal, wi),
|
|
wiDotShN = dot(n, wi);
|
|
|
|
/* Only use photons from the top side of the surface */
|
|
if (dot(wi, n) > 0 && dot(photonNormal, n) > 1e-1f && wiDotGeoN > 1e-2f) {
|
|
/* Account for non-symmetry due to shading normals */
|
|
Spectrum power = photon.getPower() * std::abs(wiDotShN / wiDotGeoN);
|
|
|
|
/* Weight the samples using Simpson's kernel */
|
|
Float sqrTerm = 1.0f - searchResult.distSquared*invSquaredRadius;
|
|
|
|
result += power * (sqrTerm*sqrTerm);
|
|
}
|
|
}
|
|
|
|
/* Based on the assumption that the surface is locally flat,
|
|
the estimate is divided by the area of a disc corresponding to
|
|
the projected spherical search region */
|
|
return result * (m_scale * 3 * INV_PI * invSquaredRadius);
|
|
}
|
|
|
|
Spectrum PhotonMap::estimateRadiance(const Intersection &its,
|
|
Float searchRadius, size_t maxPhotons) const {
|
|
SearchResult *results = static_cast<SearchResult *>(
|
|
alloca((maxPhotons+1) * sizeof(SearchResult)));
|
|
Float squaredRadius = searchRadius*searchRadius;
|
|
size_t resultCount = nnSearch(its.p, squaredRadius, maxPhotons, results);
|
|
Float invSquaredRadius = 1.0f / squaredRadius;
|
|
|
|
/* Sum over all contributions */
|
|
Spectrum result(0.0f);
|
|
const BSDF *bsdf = its.getBSDF();
|
|
for (size_t i=0; i<resultCount; i++) {
|
|
const SearchResult &searchResult = results[i];
|
|
const Photon &photon = m_kdtree[searchResult.index];
|
|
Float sqrTerm = 1.0f - searchResult.distSquared*invSquaredRadius;
|
|
|
|
Vector wi = its.toLocal(-photon.getDirection());
|
|
|
|
BSDFSamplingRecord bRec(its, wi, its.wi, EImportance);
|
|
result += photon.getPower() * bsdf->eval(bRec) * (sqrTerm*sqrTerm);
|
|
}
|
|
|
|
/* Based on the assumption that the surface is locally flat,
|
|
the estimate is divided by the area of a disc corresponding to
|
|
the projected spherical search region */
|
|
return result * (m_scale * 3 * INV_PI * invSquaredRadius);
|
|
}
|
|
|
|
struct RawRadianceQuery {
|
|
RawRadianceQuery(const Intersection &its, int maxDepth)
|
|
: its(its), maxDepth(maxDepth), result(0.0f) {
|
|
bsdf = its.getBSDF();
|
|
}
|
|
|
|
inline void operator()(const Photon &photon) {
|
|
Normal photonNormal(photon.getNormal());
|
|
Vector wi = -photon.getDirection();
|
|
Float wiDotGeoN = absDot(photonNormal, wi);
|
|
|
|
if (photon.getDepth() > maxDepth
|
|
|| dot(photonNormal, its.shFrame.n) < 1e-1f
|
|
|| wiDotGeoN < 1e-2f)
|
|
return;
|
|
|
|
BSDFSamplingRecord bRec(its, its.toLocal(wi), its.wi, EImportance);
|
|
|
|
Spectrum value = photon.getPower() * bsdf->eval(bRec);
|
|
if (value.isZero())
|
|
return;
|
|
|
|
/* Account for non-symmetry due to shading normals */
|
|
value *= std::abs(Frame::cosTheta(bRec.wi) /
|
|
(wiDotGeoN * Frame::cosTheta(bRec.wo)));
|
|
|
|
result += value;
|
|
}
|
|
|
|
const Intersection &its;
|
|
const BSDF *bsdf;
|
|
int maxDepth;
|
|
Spectrum result;
|
|
};
|
|
|
|
size_t PhotonMap::estimateRadianceRaw(const Intersection &its,
|
|
Float searchRadius, Spectrum &result, int maxDepth) const {
|
|
RawRadianceQuery query(its, maxDepth);
|
|
size_t count = m_kdtree.executeQuery(its.p, searchRadius, query);
|
|
result = query.result;
|
|
return count;
|
|
}
|
|
|
|
MTS_IMPLEMENT_CLASS_S(PhotonMap, false, SerializableObject)
|
|
MTS_NAMESPACE_END
|