mitsuba/include/mitsuba/render/irrcache.h

344 lines
9.5 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/>.
*/
#pragma once
#if !defined(__MITSUBA_RENDER_IRRCACHE_H_)
#define __MITSUBA_RENDER_IRRCACHE_H_
#include <mitsuba/render/scene.h>
#include <mitsuba/core/octree.h>
MTS_NAMESPACE_BEGIN
/* 3 (X, Y, and Z) components for each spectral sample */
typedef Spectrum RotationalGradient[3];
typedef Spectrum TranslationalGradient[3];
/**
* \brief Utility data structure for hemispherical sampling and
* translational/rotational gradient computation.
*
* Uses the improved translational gradients proposed in
* the paper "Improved radiance gradient computation" by
* Krivanek J., Gautron P., Bouatouch K., Pattanaik S.
* (Proceedings of SCCG 2005)
*
* \author Wenzel Jakob
* \ingroup librender
*/
class MTS_EXPORT_RENDER HemisphereSampler : public Object {
public:
struct SampleEntry {
Vector d;
Spectrum L;
Float dist;
Float cosTheta;
Float sinTheta;
};
/**
* Allocate storage for a hemispherical sample with M
* elevation and N azimuthal samples.
*/
HemisphereSampler(uint32_t M, uint32_t N);
/// Return the elevational resolution
inline uint32_t getM() const { return m_M; }
/// Return the azimuthal resolution
inline uint32_t getN() const { return m_N; }
/// Access a cell by index
inline SampleEntry &operator() (uint32_t j, uint32_t k) {
return m_entries[j*m_N + k];
}
/// Access a cell by index (const version)
inline const SampleEntry &operator() (uint32_t j, uint32_t k) const {
return m_entries[j*m_N + k];
}
/// Generate a set of projected solid angle-distributed directions
void generateDirections(const Intersection &its);
/// Compute mean distance, gradients etc.
void process(const Intersection &its);
/// Return the irradiance gradient with respect to rotation
inline const RotationalGradient &getRotationalGradient() const {
return m_rGrad;
}
/// Return the irradiance gradient with respect to translation
inline const RotationalGradient &getTranslationalGradient() const {
return m_tGrad;
}
/// Return the average distance over all cells (harmonic mean)
inline Float getHarmonicMeanDistance() const {
return m_hMean;
}
/// Return the minimum distance over all cells
inline Float getMinimumDistance() const {
return m_hMin;
}
/// Return the minimum distance over all cells (>10 deg in elevation)
inline Float getMinimumDistanceRestricted() const {
return m_hMinRestricted;
}
/// Return the computed irradiance
inline const Spectrum &getIrradiance() const {
return m_E;
}
MTS_DECLARE_CLASS()
protected:
/// Free all memory
virtual ~HemisphereSampler();
private:
uint32_t m_M, m_N;
SampleEntry *m_entries;
Vector *m_uk, *m_vk, *m_vkMinus;
Spectrum m_E;
RotationalGradient m_rGrad;
TranslationalGradient m_tGrad;
Float m_hMean, m_hMin, m_hMinRestricted;
ref<Random> m_random;
};
/** \brief Irradiance cache data structure based on "A Ray Tracing Solution
* for Diffuse Interreflection" by Greg J. Ward, Francis M. Rubinstein and
* Robert D. Clear (Computer Graphics, Volume 22, Number 4, August 1988)
*
* with extensions from
* "Irradiance Gradients" by Greg J. Ward and Paul S. Heckbert
* (1992 Eurographics Workshop on Rendering).
*
* Includes optimizations proposed by Jaroslav Krivanek et al. in
* "Making Radiance and Irradiance Caching Practical: Adaptive Caching and
* Neighbor Clamping" (in EGSR 2006),
*
* and
*
* "An Approximate Global Illumination System for Computer Generated Films"
* by E. Tabellion and A. Lamorlette (SIGGRAPH 2004)
*
* \author Wenzel Jakob
* \ingroup librender
*/
class MTS_EXPORT_RENDER IrradianceCache : public SerializableObject {
public:
struct Record;
/* ===================================================================== */
/* Public access methods */
/* ===================================================================== */
/**
* Create an empty irradiance of the given size
*/
IrradianceCache(const AABB &aabb);
/**
* Unserialize an irradiance cache from a binary data stream
*/
IrradianceCache(Stream *stream, InstanceManager *manager);
/**
* Set the quality parameter \kappa from the
* Tabellion and Lamorlette paper. Once samples
* have been stored, this should only be decreased.
*/
inline void setQuality(Float quality) { m_kappa = quality; }
/**
* Set the influence region cutoff values of samples in the
* cache. Will be multiplied by the scene size
*/
void clampInfluence(Float min, Float max);
/**
* Minimal influence region falloff with increasing distance
*/
inline void clampScreen(bool active) { m_clampScreen = active; }
/**
* Enable neighbor clamping?
*/
inline void clampNeighbor(bool active) { m_clampNeighbor = active; }
/**
* Enable/disable irradiance gradients
*/
inline void useGradients(bool active) { m_useGradients = active; }
/**
* Add a sample to the irradiance cache
*
* \param ray
* Ray differentials (if they exist)
* \param its
* The position/normal of the surface in question
* \param sample
* Record containing all hemispherical samples and
* derived gradient information
*/
Record *put(const RayDifferential &ray, const Intersection &its,
const HemisphereSampler &hs);
/**
* Use the irradiance cache to interpolate/extrapolate an
* irradiance value for the given position and surface normal
* Returns false on a cache miss
*/
bool get(const Intersection &its, Spectrum &E) const;
/// Manually insert an irradiance record
void insert(Record *rec);
/**
* Serialize an irradiance cache to a binary data stream
*/
void serialize(Stream *stream, InstanceManager *manager) const;
/// Return a string representation
std::string toString() const;
/* ===================================================================== */
/* Internal data structures */
/* ===================================================================== */
struct Record {
/* Sample position */
Point p;
/* Normal vector of the associated surface */
Normal n;
/* Minimum intersection distance */
Float R0;
/* Unclamped distance - must be stored for
neighbor clamping */
Float originalR0;
/* Minimum/Maximum distance - must be
stored for neighbor clamping. */
Float R0_min, R0_max;
/* Irradiance value */
Spectrum E;
/* Rotational gradient for improved interpolation */
RotationalGradient rGrad;
/* Translational gradient for improved interpolation */
TranslationalGradient tGrad;
/// Dummy constructor
inline Record() { }
/// Copy constructor
inline Record(const Record *rec)
: p(rec->p), n(rec->n), R0(rec->R0), originalR0(rec->originalR0),
R0_min(rec->R0_min), R0_max(rec->R0_max), E(rec->E) {
for (int i=0; i<3; ++i) {
tGrad[i] = rec->tGrad[i];
rGrad[i] = rec->rGrad[i];
}
}
/// Unserialize from a binary data stream
inline Record(Stream *stream) {
p = Point(stream);
n = Normal(stream);
R0 = stream->readFloat();
originalR0 = stream->readFloat();
R0_min = stream->readFloat();
R0_max = stream->readFloat();
E = Spectrum(stream);
for (int i=0; i<3; ++i)
rGrad[i] = Spectrum(stream);
for (int i=0; i<3; ++i)
tGrad[i] = Spectrum(stream);
}
/// Serialize to a binary data stream
inline void serialize(Stream *stream) const {
p.serialize(stream);
n.serialize(stream);
stream->writeFloat(R0);
stream->writeFloat(originalR0);
stream->writeFloat(R0_min);
stream->writeFloat(R0_max);
E.serialize(stream);
for (int i=0; i<3; ++i)
rGrad[i].serialize(stream);
for (int i=0; i<3; ++i)
tGrad[i].serialize(stream);
}
/**
* Calculate contribution of this sample if it were used
* to calculate an interpolated value at the given position
*/
inline Float getWeight(const Point &p2, const Normal &n2, Float kappa) const {
Float dp = dot(n, n2);
/* Quickly discard opposite-facing samples */
if (dp < 0.0f)
return 0.0f;
else if (dp > 1.0f)
dp = 1.0f;
/* Reject illuminance values 'in front' of P2 */
if (dot(p2 - p, n + n2) < -0.05f)
return 0.0f;
/* Ad-hoc weight function (Tabellion & Lamorlette) */
Float ePI = (p-p2).length() / (.5f * R0);
Float eNI = std::sqrt(1.0f - std::abs(dp))
/ 0.12326f;
Float weight = 1 - kappa*std::max(ePI, eNI);
if (weight < 0)
return 0.0f;
return weight;
}
};
MTS_DECLARE_CLASS()
protected:
/// Release all memory
virtual ~IrradianceCache();
protected:
/* ===================================================================== */
/* Protected attributes */
/* ===================================================================== */
DynamicOctree<Record *> m_octree;
std::vector<Record *> m_records;
Float m_kappa;
Float m_sceneSize;
Float m_minDist, m_maxDist;
bool m_clampScreen, m_clampNeighbor, m_useGradients;
ref<Mutex> m_mutex;
};
MTS_NAMESPACE_END
#endif /* __MITSUBA_RENDER_IRRCACHE_H_ */