initial commit with accumulated changes of the participating medium rewrite
parent
b791e0b453
commit
22a1a37cf0
|
@ -55,12 +55,12 @@ public:
|
|||
}
|
||||
|
||||
/// Access a PDF entry
|
||||
inline Float &operator[](unsigned int entry) {
|
||||
inline Float &operator[](size_t entry) {
|
||||
return m_pdf[entry];
|
||||
}
|
||||
|
||||
/// Access a PDF entry
|
||||
inline const Float &operator[](unsigned int entry) const {
|
||||
inline const Float &operator[](size_t entry) const {
|
||||
return m_pdf[entry];
|
||||
}
|
||||
|
||||
|
@ -82,10 +82,10 @@ public:
|
|||
inline Float build() {
|
||||
SAssert(m_pdf.size() > 0);
|
||||
m_cdf[0] = 0.0f;
|
||||
for (unsigned int i=1; i<m_cdf.size(); ++i)
|
||||
for (size_t i=1; i<m_cdf.size(); ++i)
|
||||
m_cdf[i] = m_cdf[i-1] + m_pdf[i-1];
|
||||
m_originalSum = m_cdf[m_cdf.size()-1];
|
||||
for (unsigned int i=0; i<m_pdf.size(); ++i) {
|
||||
for (size_t i=0; i<m_pdf.size(); ++i) {
|
||||
m_cdf[i] /= m_originalSum;
|
||||
m_pdf[i] /= m_originalSum;
|
||||
}
|
||||
|
|
|
@ -67,7 +67,9 @@ public:
|
|||
/// Return a string representation
|
||||
virtual std::string toString() const;
|
||||
|
||||
/// @{ \name Abstract methods to be implemented by subclasses
|
||||
// ======================================================================
|
||||
/// @{ \name Abstract methods that need to be implemented by subclasses
|
||||
// ======================================================================
|
||||
|
||||
/// Read a specified amount of data from the stream
|
||||
virtual void read(void *ptr, size_t size) = 0;
|
||||
|
@ -97,8 +99,11 @@ public:
|
|||
virtual bool canRead() const = 0;
|
||||
|
||||
/// @}
|
||||
// ======================================================================
|
||||
|
||||
/// @{ \name Convenience functions
|
||||
// ======================================================================
|
||||
/// @{ \name Convenience functions with automatic endianness conversion
|
||||
// ======================================================================
|
||||
|
||||
/// Skip the given number of bytes
|
||||
void skip(size_t amount);
|
||||
|
|
|
@ -24,18 +24,31 @@
|
|||
MTS_NAMESPACE_BEGIN
|
||||
|
||||
/** \brief Abstract camera base class. A camera turns a sample on
|
||||
* the image plane into a 3D ray. For this, it requires two
|
||||
* supporting objects: a <tt>Sampler</tt> and a <tt>Film</tt> instance.
|
||||
* the image plane into a 3D ray. It uses two supporting child
|
||||
* objects: a \re Sampler and a \ref Film instance.
|
||||
*/
|
||||
class MTS_EXPORT_RENDER Camera : public ConfigurableObject {
|
||||
public:
|
||||
// =============================================================
|
||||
//! @{ \name Ray generation
|
||||
// =============================================================
|
||||
|
||||
/// Create a ray from the given sample
|
||||
virtual void generateRay(const Point2 &sample, const Point2 &lensSample,
|
||||
virtual void generateRay(const Point2 &sample,
|
||||
const Point2 &lensSample,
|
||||
Float timeSample, Ray &ray) const = 0;
|
||||
|
||||
/// Create ray differentials from the given sample
|
||||
void generateRayDifferential(const Point2 &sample,
|
||||
const Point2 &lensSample, Float timeSample, RayDifferential &ray) const;
|
||||
const Point2 &lensSample, Float timeSample,
|
||||
RayDifferential &ray) const;
|
||||
|
||||
//! @}
|
||||
// =============================================================
|
||||
|
||||
// =============================================================
|
||||
//! @{ \name Other query functions
|
||||
// =============================================================
|
||||
|
||||
/**
|
||||
* Turn a world-space position into fractional pixel coordinates.
|
||||
|
@ -50,40 +63,6 @@ public:
|
|||
/// Does generateRay() expect a proper time sample?
|
||||
inline bool needsTimeSample() const { return m_shutterOpenTime > 0; }
|
||||
|
||||
/// Return the camera position (approximate in the case of finite sensor area)
|
||||
inline const Point &getPosition() const { return m_position; }
|
||||
|
||||
/// Return the camera position at the provided screen sample
|
||||
virtual Point getPosition(const Point2 &sample) const;
|
||||
|
||||
/**
|
||||
* Calculate the pixel area density at a position on the image plane.
|
||||
* Returns zero for cameras with an infinitesimal sensor (e.g. pinhole cameras).
|
||||
*/
|
||||
virtual Float areaDensity(const Point2 &p) const = 0;
|
||||
|
||||
/**
|
||||
* \brief Return the camera's sampler.
|
||||
*
|
||||
* This is the 'root' sampler, which will later be cloned a
|
||||
* number of times to provide each participating worker thread
|
||||
* with its own instance (see \ref Scene::getSampler()).
|
||||
* Therefore, this sampler should never be used for anything
|
||||
* except creating clones.
|
||||
*/
|
||||
inline Sampler *getSampler() { return m_sampler; }
|
||||
|
||||
/**
|
||||
* \brief Return the camera's sampler.
|
||||
*
|
||||
* This is the 'root' sampler, which will later be cloned a
|
||||
* number of times to provide each participating worker thread
|
||||
* with its own instance (see \ref Scene::getSampler()).
|
||||
* Therefore, this sampler should never be used for anything
|
||||
* except creating clones.
|
||||
*/
|
||||
inline const Sampler *getSampler() const { return m_sampler.get(); }
|
||||
|
||||
/// Return the time value of the shutter opening event
|
||||
inline Float getShutterOpen() const { return m_shutterOpen; }
|
||||
|
||||
|
@ -93,10 +72,18 @@ public:
|
|||
/// Return the time value of the shutter closing event
|
||||
inline Float getShutterClose() const { return m_shutterOpen; }
|
||||
|
||||
/// Return the image plane normal
|
||||
inline Normal getImagePlaneNormal() const {
|
||||
return Normal(normalize(m_cameraToWorld(Vector(0, 0, 1))));
|
||||
}
|
||||
//! @}
|
||||
// =============================================================
|
||||
|
||||
// =============================================================
|
||||
//! @{ \name Other query functions
|
||||
// =============================================================
|
||||
|
||||
/// Return the camera position (approximate in the case of finite sensor area)
|
||||
inline const Point &getPosition() const { return m_position; }
|
||||
|
||||
/// Return the camera position at the provided screen sample
|
||||
virtual Point getPosition(const Point2 &sample) const;
|
||||
|
||||
/// Return the view transformation
|
||||
inline const Transform &getViewTransform() const { return m_worldToCamera; }
|
||||
|
@ -118,6 +105,46 @@ public:
|
|||
m_position = m_cameraToWorld(Point(0,0,0));
|
||||
}
|
||||
|
||||
/// Return the image plane normal
|
||||
inline Normal getImagePlaneNormal() const {
|
||||
return Normal(normalize(m_cameraToWorld(Vector(0, 0, 1))));
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the pixel area density at a position on the image plane.
|
||||
* Returns zero for cameras with an infinitesimal sensor (e.g. pinhole cameras).
|
||||
*/
|
||||
virtual Float areaDensity(const Point2 &p) const = 0;
|
||||
|
||||
//! @}
|
||||
// =============================================================
|
||||
|
||||
// =============================================================
|
||||
//! @{ \name Miscellaneous
|
||||
// =============================================================
|
||||
|
||||
/**
|
||||
* \brief Return the camera's sampler.
|
||||
*
|
||||
* This is the 'root' sampler, which will later be cloned a
|
||||
* number of times to provide each participating worker thread
|
||||
* with its own instance (see \ref Scene::getSampler()).
|
||||
* Therefore, this sampler should never be used for anything
|
||||
* except creating clones.
|
||||
*/
|
||||
inline Sampler *getSampler() { return m_sampler; }
|
||||
|
||||
/**
|
||||
* \brief Return the camera's sampler (const version).
|
||||
*
|
||||
* This is the 'root' sampler, which will later be cloned a
|
||||
* number of times to provide each participating worker thread
|
||||
* with its own instance (see \ref Scene::getSampler()).
|
||||
* Therefore, this sampler should never be used for anything
|
||||
* except creating clones.
|
||||
*/
|
||||
inline const Sampler *getSampler() const { return m_sampler.get(); }
|
||||
|
||||
/// Set the camera's film
|
||||
void setFilm(Film *film);
|
||||
|
||||
|
@ -127,6 +154,12 @@ public:
|
|||
/// Return the camera's film
|
||||
inline const Film *getFilm() const { return m_film.get(); }
|
||||
|
||||
/// Return a pointer to the medium that surrounds the camera
|
||||
inline Medium *getMedium() { return m_medium.get(); }
|
||||
|
||||
/// Return a pointer to the medium that surrounds the camera (const version)
|
||||
inline const Medium *getMedium() const { return m_medium.get(); }
|
||||
|
||||
/// Add a child ConfigurableObject
|
||||
virtual void addChild(const std::string &name, ConfigurableObject *child);
|
||||
|
||||
|
@ -139,6 +172,9 @@ public:
|
|||
/// Return the properties of this camera
|
||||
inline const Properties &getProperties() const { return m_properties; }
|
||||
|
||||
//! @}
|
||||
// =============================================================
|
||||
|
||||
MTS_DECLARE_CLASS()
|
||||
protected:
|
||||
/// Create a new camera
|
||||
|
@ -156,6 +192,7 @@ protected:
|
|||
Point m_position;
|
||||
Properties m_properties;
|
||||
Float m_shutterOpen, m_shutterClose, m_shutterOpenTime;
|
||||
ref<Medium> m_medium;
|
||||
};
|
||||
|
||||
class MTS_EXPORT_RENDER ProjectiveCamera : public Camera {
|
||||
|
@ -202,12 +239,6 @@ public:
|
|||
/// Calculate the image plane size for a plane of the given distance
|
||||
Vector2 getImagePlaneSize(Float dist) const;
|
||||
|
||||
/**
|
||||
* Calculate the solid angle subtended by the area [xs,xe]x[ys,ye]
|
||||
* on the image plane (in pixel coordinates starting at 0).
|
||||
*/
|
||||
Float solidAngle(Float xs, Float xe, Float ys, Float ye) const;
|
||||
|
||||
/**
|
||||
* Calculate the importance of the given image-plane point
|
||||
* expressed in fractional pixel coordinates. Note that this value
|
||||
|
|
|
@ -59,10 +59,10 @@ MTS_NAMESPACE_BEGIN
|
|||
* The class \ref OrderedChunkAllocator provides a specialized
|
||||
* memory allocator, which reserves memory in chunks of at least
|
||||
* 128KiB. An important assumption made by the allocator is that
|
||||
* memory will be released in the same order, in which it is
|
||||
* allocated. This makes it possible to create an implementation
|
||||
* with a very low memory overhead. Note that no locking is done,
|
||||
* hence each thread will need its own allocator.
|
||||
* memory will be released in the exact same order, in which it was
|
||||
* previously allocated. This makes it possible to create an
|
||||
* implementation with a very low memory overhead. Note that no locking
|
||||
* is done, hence each thread will need its own allocator.
|
||||
*
|
||||
* \author Wenzel Jakob
|
||||
*/
|
||||
|
|
|
@ -26,10 +26,11 @@
|
|||
MTS_NAMESPACE_BEGIN
|
||||
|
||||
/**
|
||||
* Abstract integrator base-class. Does not make any assumptions on
|
||||
* \brief Abstract integrator base-class; does not make any assumptions on
|
||||
* how radiance is computed.
|
||||
* Amongst other things, this allows the use of hardware-accelerated
|
||||
* rasterization, which directly operates on the camera's film and has
|
||||
*
|
||||
* Amongst other things, the generality of this class allows for hardware-accelerated
|
||||
* rasterization that directly operates on the camera's film and has
|
||||
* no global knowledge about radiance within the scene. Other possibilities
|
||||
* are sampling- or particle tracing-based integrators.
|
||||
*/
|
||||
|
@ -106,85 +107,86 @@ protected:
|
|||
Properties m_properties;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Radiance query record data structure used by \ref SampleIntegrator
|
||||
*/
|
||||
struct MTS_EXPORT_RENDER RadianceQueryRecord {
|
||||
public:
|
||||
/**
|
||||
* List of suported query types. These can be combined by a binary OR.
|
||||
*/
|
||||
/// List of suported query types. These can be combined by a binary OR.
|
||||
enum ERadianceQuery {
|
||||
/* Emitted radiance from a luminaire intersected by the ray */
|
||||
/// Emitted radiance from a luminaire intersected by the ray
|
||||
EEmittedRadiance = 0x0001,
|
||||
|
||||
/* Emitted radiance from a subsurface integrator */
|
||||
/// Emitted radiance from a subsurface integrator */
|
||||
ESubsurfaceRadiance = 0x0002,
|
||||
|
||||
/* Direct (surface) radiance */
|
||||
EDirectRadiance = 0x0004,
|
||||
/// Direct (surface) radiance */
|
||||
EDirectSurfaceRadiance = 0x0004,
|
||||
|
||||
/* Indirect (surface) radiance, where the last bounce did not go
|
||||
through a Dirac delta BSDF */
|
||||
EIndirectRadiance = 0x0008,
|
||||
/*! \brief Indirect (surface) radiance, where the last bounce did not go
|
||||
through a Dirac delta BSDF */
|
||||
EIndirectSurfaceRadiance = 0x0008,
|
||||
|
||||
/* Indirect (surface) radiance, where the last bounce went
|
||||
/*! \brief Indirect (surface) radiance, where the last bounce went
|
||||
through a Dirac delta BSDF */
|
||||
ECausticRadiance = 0x0010,
|
||||
|
||||
/* In-scattered radiance due to volumetric scattering (direct) */
|
||||
EInscatteredDirectRadiance = 0x0020,
|
||||
/// In-scattered radiance due to volumetric scattering (direct)
|
||||
EDirectMediumRadiance = 0x0020,
|
||||
|
||||
/* In-scattered radiance due to volumetric scattering (indirect) */
|
||||
EInscatteredIndirectRadiance = 0x0040,
|
||||
/// In-scattered radiance due to volumetric scattering (indirect)
|
||||
EIndirectMediumRadiance = 0x0040,
|
||||
|
||||
/* Distance to the next surface intersection */
|
||||
/// Distance to the next surface intersection
|
||||
EDistance = 0x0080,
|
||||
|
||||
/* Opacity value: 1 when a surface was hit, 0 when the ray leads
|
||||
into empty space. When there is a participating medium,
|
||||
this can also take on fractional values. */
|
||||
/*! \brief Store an opacity value, which is equal to 1 when a shape
|
||||
was intersected and 0 when the ray passes through empty space.
|
||||
When there is a participating medium, it can also take on fractional
|
||||
values. */
|
||||
EOpacity = 0x0100,
|
||||
|
||||
/* A ray intersection may need to be performed. This can be set to
|
||||
/*! \brief A ray intersection may need to be performed. This can be set to
|
||||
zero if the caller has already provided the intersection */
|
||||
EIntersection = 0x0200,
|
||||
|
||||
/* Radiance from volumes */
|
||||
EVolumeRadiance = EInscatteredDirectRadiance | EInscatteredIndirectRadiance,
|
||||
EVolumeRadiance = EDirectMediumRadiance | EIndirectMediumRadiance,
|
||||
|
||||
/* Radiance query without emitted radiance, ray intersection required */
|
||||
ERadianceNoEmission = ESubsurfaceRadiance | EDirectRadiance | EIndirectRadiance
|
||||
| ECausticRadiance | EInscatteredDirectRadiance | EInscatteredIndirectRadiance | EIntersection,
|
||||
/// Radiance query without emitted radiance, ray intersection required
|
||||
ERadianceNoEmission = ESubsurfaceRadiance | EDirectSurfaceRadiance | EIndirectSurfaceRadiance
|
||||
| ECausticRadiance | EDirectMediumRadiance | EIndirectMediumRadiance | EIntersection,
|
||||
|
||||
/* Default radiance query, ray intersection required */
|
||||
/// Default radiance query, ray intersection required
|
||||
ERadiance = ERadianceNoEmission | EEmittedRadiance,
|
||||
|
||||
/* Radiance + opacity */
|
||||
/// Radiance + opacity
|
||||
ECameraRay = ERadiance | EOpacity
|
||||
};
|
||||
|
||||
/// Construct an invalid radiance query record
|
||||
inline RadianceQueryRecord()
|
||||
: type(0), scene(NULL), sampler(NULL),
|
||||
depth(0), alpha(0), dist(-1), wavelength(-1), extra(0) {
|
||||
: type(0), scene(NULL), sampler(NULL), medium(NULL),
|
||||
depth(0), alpha(0), dist(-1), extra(0) {
|
||||
}
|
||||
|
||||
/// Construct a radiance query record for the given scene and sampler
|
||||
inline RadianceQueryRecord(const Scene *scene, Sampler *sampler)
|
||||
: type(0), scene(scene), sampler(sampler),
|
||||
depth(0), alpha(0), dist(-1), wavelength(-1), extra(0) {
|
||||
: type(0), scene(scene), sampler(sampler), medium(NULL),
|
||||
depth(0), alpha(0), dist(-1), extra(0) {
|
||||
}
|
||||
|
||||
/// Copy constructor
|
||||
inline RadianceQueryRecord(const RadianceQueryRecord &rRec)
|
||||
: type(rRec.type), scene(rRec.scene), sampler(rRec.sampler),
|
||||
depth(rRec.depth), alpha(rRec.alpha), dist(rRec.dist),
|
||||
wavelength(rRec.wavelength), extra(rRec.extra) {
|
||||
: type(rRec.type), scene(rRec.scene), sampler(rRec.sampler), medium(rRec.medium),
|
||||
depth(rRec.depth), alpha(rRec.alpha), dist(rRec.dist), extra(rRec.extra) {
|
||||
}
|
||||
|
||||
/// Begin a new query of the given type
|
||||
inline void newQuery(int _type) {
|
||||
inline void newQuery(int _type, const Medium *_medium) {
|
||||
type = _type;
|
||||
medium = _medium;
|
||||
depth = 1;
|
||||
wavelength = -1;
|
||||
extra = 0;
|
||||
}
|
||||
|
||||
|
@ -194,22 +196,24 @@ public:
|
|||
scene = parent.scene;
|
||||
sampler = parent.sampler;
|
||||
depth = parent.depth+1;
|
||||
wavelength = parent.wavelength;
|
||||
extra = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for a ray intersection. This
|
||||
* does several things at once - if the intersection has
|
||||
* already been provided, the function returns.
|
||||
* \brief Search for a ray intersection
|
||||
*
|
||||
* This function does several things at once: if the
|
||||
* intersection has already been provided, it returns.
|
||||
*
|
||||
* Otherwise, it
|
||||
* - performs the ray intersection
|
||||
* - computes the attenuation due to participating media
|
||||
* and stores it in <tt>attenuation</tt>.
|
||||
* - sets the alpha value (if <tt>EAlpha</tt> is set in <tt>type</tt>)
|
||||
* - sets the distance value (if <tt>EDistance</tt> is set in <tt>type</tt>)
|
||||
* - clears the <tt>EIntersection</tt> flag in <tt>type</tt>
|
||||
* Returns true if there is a valid intersection.
|
||||
* 1. performs the ray intersection
|
||||
* 2. computes the attenuation due to participating media
|
||||
* and stores it in \c attenuation.
|
||||
* 3. sets the alpha value (if \c EAlpha is set in \c type)
|
||||
* 4. sets the distance value (if \c EDistance is set in \c type)
|
||||
* 5. clears the \c EIntersection flag in \c type
|
||||
*
|
||||
* \return \c true if there is a valid intersection.
|
||||
*/
|
||||
inline bool rayIntersect(const RayDifferential &ray);
|
||||
|
||||
|
@ -234,6 +238,9 @@ public:
|
|||
/// Sample generator
|
||||
Sampler *sampler;
|
||||
|
||||
/// Pointer to the current medium (*)
|
||||
const Medium *medium;
|
||||
|
||||
/// Current depth value (# of light bounces) (*)
|
||||
int depth;
|
||||
|
||||
|
@ -249,16 +256,6 @@ public:
|
|||
*/
|
||||
Float dist;
|
||||
|
||||
/**
|
||||
* In some cases, the integrator may be forced to restrict
|
||||
* radiance computations to one wavelength (e.g. when intersecting
|
||||
* a dielectric material with dispersion or while path
|
||||
* tracing through a highly scattering medium with a non-constant
|
||||
* scattering coefficient). This attribute is used to store the
|
||||
* chosen wavelength. (*)
|
||||
*/
|
||||
int wavelength;
|
||||
|
||||
/**
|
||||
* Internal flag, which can be used to pass additional information
|
||||
* amonst recursive calls inside an integrator. The use
|
||||
|
@ -273,19 +270,37 @@ public:
|
|||
class MTS_EXPORT_RENDER SampleIntegrator : public Integrator {
|
||||
public:
|
||||
/**
|
||||
* Sample the incident radiance along a ray. Also requires
|
||||
* \brief Sample the incident radiance along a ray. Also requires
|
||||
* a radiance query record, which makes this request more precise.
|
||||
*/
|
||||
virtual Spectrum Li(const RayDifferential &ray,
|
||||
RadianceQueryRecord &rRec) const = 0;
|
||||
|
||||
/**
|
||||
* Estimate the irradiance at a given surface point. The
|
||||
* default implementation simply samples the hemisphere using
|
||||
* \brief Estimate the irradiance at a given surface point
|
||||
*
|
||||
* The default implementation simply samples the hemisphere using
|
||||
* cosine-weighted sampling and a configurable number of rays.
|
||||
*
|
||||
* \param scene
|
||||
* Const pointer to the underlying scene
|
||||
* \param p
|
||||
* The surface location at which to estimate the irradiance
|
||||
* \param n
|
||||
* The surface normal at which to estimate the irradiance
|
||||
* \param medium
|
||||
* Const pointer to the medium that encloses \c (p, n).
|
||||
* A value of \c NULL corresponds to vacuum
|
||||
* \param sampler
|
||||
* A pointer to a sample generator
|
||||
* \param nSamples
|
||||
* How many samples should be taken
|
||||
* \param includeIndirect
|
||||
* Include indirect illumination in the estimate?
|
||||
*/
|
||||
virtual Spectrum E(const Scene *scene, const Point &p, const
|
||||
Normal &n, Float time, Sampler *sampler, int nSamples,
|
||||
Normal &n, Float time, const Medium *medium,
|
||||
Sampler *sampler, int nSamples,
|
||||
bool includeIndirect) const;
|
||||
|
||||
/**
|
||||
|
|
|
@ -25,21 +25,13 @@
|
|||
MTS_NAMESPACE_BEGIN
|
||||
|
||||
/**
|
||||
* Data structure used to record information associated with
|
||||
* sampled shadow rays
|
||||
* \brief Data structure used by the direct illumination / shadow ray
|
||||
* sampling methods in the class \ref Luminaire.
|
||||
*/
|
||||
struct MTS_EXPORT_RENDER LuminaireSamplingRecord {
|
||||
public:
|
||||
/// Create an invalid record
|
||||
/// Create an invalid shadow ray sampling record
|
||||
inline LuminaireSamplingRecord() : luminaire(NULL) { }
|
||||
|
||||
/**
|
||||
* When a ray strikes a luminaire that is part of the scene,
|
||||
* the associated intersection record can be converted into
|
||||
* a luminaire sampling record in order to query the luminaire
|
||||
* for emitted radiance. (defined in records.inl)
|
||||
*/
|
||||
LuminaireSamplingRecord(const Intersection &its, const Vector &direction);
|
||||
|
||||
/// Return a string representation
|
||||
std::string toString() const;
|
||||
|
@ -53,23 +45,31 @@ public:
|
|||
/// Direction vector pointing away from the light source
|
||||
Vector d;
|
||||
|
||||
/// Probability density of the sampled point on the luminaire
|
||||
/**
|
||||
* \brief Probability density (wrt. solid angle) of the sampled
|
||||
* point on the luminaire
|
||||
*/
|
||||
Float pdf;
|
||||
|
||||
/**
|
||||
* Emitted radiance at 'p' into direction 'd' divided by the associated
|
||||
* probability. Already contains the geometric term and optionally
|
||||
* attenuation when generated via Scene::sampleLuminaireAttenuated.
|
||||
* \brief Emitted radiance at \c p into direction \c d divided by
|
||||
* the associated probability. Already contains the geometric term
|
||||
* and optionally also attenuation when generated via
|
||||
* \ref Scene::sampleAttenuatedLuminaire.
|
||||
*/
|
||||
Spectrum Le;
|
||||
Spectrum value;
|
||||
};
|
||||
|
||||
/**
|
||||
* Data structure used to record information associated with
|
||||
* luminaire emission sampling
|
||||
* \brief Data structure used to record information associated with
|
||||
* emission sampling in the class \ref Luminaire.
|
||||
*/
|
||||
struct MTS_EXPORT_RENDER EmissionRecord {
|
||||
public:
|
||||
/**
|
||||
* \brief This class supports a special \a preview mode when
|
||||
* sampling emissions for use in a VPL-style rendering algorithm
|
||||
*/
|
||||
enum ESamplingType {
|
||||
ENormal,
|
||||
EPreview
|
||||
|
@ -97,13 +97,14 @@ public:
|
|||
Vector d;
|
||||
|
||||
/**
|
||||
* Radiant emittance at the sampled point. When this
|
||||
* record was populated using Scene::sampleEmission(), 'P'
|
||||
* has already been multiplied by the directional
|
||||
* scattering distribution and divided by the associated
|
||||
* sampling densities.
|
||||
* \brief Stores the spatial component of the radiant
|
||||
* emittance
|
||||
*
|
||||
* When the record was populated using Scene::sampleEmission(),
|
||||
* \c P will also be modulated by the directional scattering
|
||||
* distribution and divided by the associated sampling densities.
|
||||
*/
|
||||
Spectrum P;
|
||||
Spectrum value;
|
||||
|
||||
/// Area probability density
|
||||
Float pdfArea;
|
||||
|
@ -113,159 +114,235 @@ public:
|
|||
};
|
||||
|
||||
/**
|
||||
* Abstract implementation of a luminaire. Supports emission and
|
||||
* direct illumination sampling strategies and computes related probabilities.
|
||||
* \brief Abstract implementation of a luminaire. Supports emission and
|
||||
* direct illumination sampling strategies, and computes related probabilities.
|
||||
*/
|
||||
class MTS_EXPORT_RENDER Luminaire : public ConfigurableObject, public HWResource {
|
||||
public:
|
||||
/**
|
||||
* \brief Non-exhaustive list of flags that can be used to characterize
|
||||
* light sources in \ref getType()
|
||||
*/
|
||||
enum EType {
|
||||
/// The light source has a degenerate directional density
|
||||
EDeltaDirection = 0x1,
|
||||
|
||||
/// The light source is perfectly diffuse with respect to direction.
|
||||
EDiffuseDirection = 0x02,
|
||||
|
||||
/// The light source is associated with a surface
|
||||
EOnSurface = 0x04,
|
||||
|
||||
/// The light source has a degenerate spatial density
|
||||
EDeltaPosition = 0x8,
|
||||
|
||||
/// The light source has a degenerate spatial \a or directional density
|
||||
EDelta = EDeltaDirection | EDeltaPosition
|
||||
};
|
||||
|
||||
// =============================================================
|
||||
//! @{ \name General information
|
||||
// =============================================================
|
||||
|
||||
/// Return the name of this luminaire
|
||||
inline const std::string &getName() const { return m_name; }
|
||||
|
||||
/// Return the luminaire type (a combination of the properties in \ref EType)
|
||||
inline int getType() const { return m_type; }
|
||||
|
||||
/// ================= Direct illumination sampling =================
|
||||
/**
|
||||
* Return the radiant emittance into a given direction. This is
|
||||
* primarily used when an area light source has been hit by a ray,
|
||||
* and it subsequently needs to be queried for the emitted radiance
|
||||
* passing into the opposite direction.
|
||||
* \brief Return an estimate of the total amount of power emitted
|
||||
* by this luminaire.
|
||||
*/
|
||||
virtual Spectrum Le(const LuminaireSamplingRecord &lRec) const = 0;
|
||||
virtual Spectrum getPower() const = 0;
|
||||
|
||||
/// Is this luminaire intersectable (e.g. can it be encountered by a tracing a ray)?
|
||||
inline bool isIntersectable() const { return m_intersectable; }
|
||||
|
||||
/// Return a pointer to the medium that surrounds the luminaire
|
||||
inline Medium *getMedium() { return m_medium.get(); }
|
||||
|
||||
/// Return a pointer to the medium that surrounds the luminaire (const version)
|
||||
inline const Medium *getMedium() const { return m_medium.get(); }
|
||||
|
||||
/**
|
||||
* Return the radiant emittance along a ray which does not
|
||||
* intersect any scene objects. The default implementation
|
||||
* returns zero - this can be used to implement background light sources.
|
||||
* \brief Return the luminaire's sampling weight
|
||||
*
|
||||
* This is used by the luminaire importance sampling
|
||||
* routines in \ref Scene.
|
||||
*/
|
||||
virtual Spectrum Le(const Ray &ray) const;
|
||||
inline Float getSamplingWeight() const { return m_samplingWeight; }
|
||||
|
||||
//! @}
|
||||
// =============================================================
|
||||
|
||||
// =============================================================
|
||||
//! @{ \name Direct illumination sampling strategies
|
||||
// =============================================================
|
||||
|
||||
/**
|
||||
* Shadow ray sampling routine: Given an arbitrary 3D position
|
||||
* generate a sample point on the luminaire and fill the
|
||||
* sampling record with all associated information.
|
||||
* Sampling is ideally wrt. solid angle at 'p'.
|
||||
* \brief Shadow ray sampling routine: Given an arbitrary 3D position,
|
||||
* generate a sample point on the luminaire and fill the supplied sampling
|
||||
* record with relevant information.
|
||||
*
|
||||
* Sampling is ideally done with respect to solid angle at \c p.
|
||||
*/
|
||||
virtual void sample(const Point &p,
|
||||
LuminaireSamplingRecord &lRec, const Point2 &sample) const = 0;
|
||||
|
||||
/**
|
||||
* Shadow ray sampling routine: Given a surface intersection,
|
||||
* generate a sample point on the luminaire and fill the
|
||||
* sampling record with all associated information.
|
||||
* Sampling is ideally wrt. solid angle at 'its'.
|
||||
*/
|
||||
virtual void sample(const Intersection &its,
|
||||
LuminaireSamplingRecord &lRec, const Point2 &sample) const = 0;
|
||||
|
||||
/**
|
||||
* Calculate the probability of generating this sample using
|
||||
* the luminaire sampling strategy implemented by this class.
|
||||
* \brief Calculate the solid angle density for generating this sample
|
||||
* using the luminaire sampling strategy implemented by this class.
|
||||
*
|
||||
* When \c delta is set to true, only components with a Dirac delta density
|
||||
* are considered in the query. Otherwise, they are left out.
|
||||
*/
|
||||
virtual Float pdf(const Point &p,
|
||||
const LuminaireSamplingRecord &lRec, bool delta) const = 0;
|
||||
|
||||
//! @}
|
||||
// =============================================================
|
||||
|
||||
// =============================================================
|
||||
//! @{ \name Emission sampling strategies
|
||||
// =============================================================
|
||||
|
||||
/**
|
||||
* Calculate the probability of generating this sample using
|
||||
* the luminaire sampling strategy implemented by this class
|
||||
* (as above, but for surface interactions)
|
||||
*/
|
||||
virtual Float pdf(const Intersection &its,
|
||||
const LuminaireSamplingRecord &lRec, bool delta) const = 0;
|
||||
|
||||
/// ================= Emission sampling =================
|
||||
|
||||
/**
|
||||
* Sample a particle leaving this luminaire and return a ray describing its path
|
||||
* as well as record containing detailed probability density information.
|
||||
* Two uniformly distributed 2D samples are required.
|
||||
* \brief Sample a particle leaving this luminaire and return a ray
|
||||
* describing its path as well as record containing detailed probability
|
||||
* density information.
|
||||
*
|
||||
* Two uniformly distributed 2D samples are required. This method does
|
||||
* exactly the same as calling \c sampleEmissionArea and
|
||||
* \c sampleEmissionDirection in sequence, modulating \c eRec.Le
|
||||
* by the return value of the latter and dividing by the product
|
||||
* of the spatial and directional sampling densities.
|
||||
*/
|
||||
virtual void sampleEmission(EmissionRecord &eRec,
|
||||
const Point2& areaSample, const Point2 &dirSample) const = 0;
|
||||
|
||||
/**
|
||||
* Sample only the spatial dimension of the emission sampling strategy
|
||||
* implemented in <tt>sampleEmission</tt>. An examplary use of this is in
|
||||
* bidirectional path tracing or MLT, where the area and direction sampling
|
||||
* steps take place in different vertices (essentially, the directional
|
||||
* variation is similar to a BSDF that modulates the spatially dependent
|
||||
* radiance component). After the function call terminates, the area density
|
||||
* as well as the spatially dependent emittance will be stored in <tt>eRec</tt>.
|
||||
* \brief Sample only the spatial part of the emission sampling strategy
|
||||
* implemented in \c sampleEmission.
|
||||
*
|
||||
* An examplary use of this method is bidirectional path tracing or MLT,
|
||||
* where the area and direction sampling steps take place in different
|
||||
* vertices.
|
||||
*
|
||||
* After the function call terminates, the area density as well as the
|
||||
* spatially dependent emittance component will be stored in \c eRec.
|
||||
*/
|
||||
virtual void sampleEmissionArea(EmissionRecord &lRec, const Point2 &sample) const = 0;
|
||||
virtual void sampleEmissionArea(EmissionRecord &lRec,
|
||||
const Point2 &sample) const = 0;
|
||||
|
||||
/**
|
||||
* As above, but handles only the directional part. Must be called *after*
|
||||
* sampleEmissionArea(). The return value is to be understood as a BRDF,
|
||||
* which modulates the radiant emittance.
|
||||
* \brief Sample only the directional part of the emission sampling strategy
|
||||
* implemented in \c sampleEmission.
|
||||
*
|
||||
* Can only be called \a after a preceding invocation of
|
||||
* \ref sampleEmissionArea() with the same emission sampling record.
|
||||
*
|
||||
* The return value of this function should be used to modulate the spatial
|
||||
* component of the radiant emittance obtained in \ref sampleEmissionArea.
|
||||
*/
|
||||
virtual Spectrum sampleEmissionDirection(EmissionRecord &lRec, const Point2 &sample) const = 0;
|
||||
virtual Spectrum sampleEmissionDirection(EmissionRecord &lRec,
|
||||
const Point2 &sample) const = 0;
|
||||
|
||||
/**
|
||||
* Given an emitted particle, populate the emission record with the relevant
|
||||
* probability densities. When \a delta is set to true, only components
|
||||
* with a Dirac delta density are queried.
|
||||
* \brief Given an emitted particle, populate the emission record with the
|
||||
* relevant probability densities.
|
||||
*
|
||||
* When \c delta is set to true, only components with a Dirac delta density
|
||||
* are considered in the query. Otherwise, they are left out.
|
||||
*/
|
||||
virtual void pdfEmission(EmissionRecord &eRec, bool delta) const = 0;
|
||||
|
||||
/**
|
||||
* Evaluate the directional scattering distribution of this light source
|
||||
* at a given point. Similar to Le(), except that this function is
|
||||
* normalized like a BSDF.
|
||||
*/
|
||||
virtual Spectrum f(const EmissionRecord &eRec) const = 0;
|
||||
|
||||
/**
|
||||
* Evaluate the radiant emittance at a point on the luminaire
|
||||
* (ignoring any directional variations).
|
||||
* \brief Evaluate the spatial component of the radiant emittance at a
|
||||
* point on the luminaire (ignoring any directional variations).
|
||||
*/
|
||||
virtual Spectrum fArea(const EmissionRecord &eRec) const = 0;
|
||||
|
||||
/// ================= Misc. =================
|
||||
/**
|
||||
* \brief Evaluate the directional emission distribution of this light source
|
||||
* at a given point (ignoring the spatial component).
|
||||
*
|
||||
* This function is normalized so that it integrates to one.
|
||||
*/
|
||||
virtual Spectrum fDirection(const EmissionRecord &eRec) const = 0;
|
||||
|
||||
//! @}
|
||||
// =============================================================
|
||||
|
||||
// =============================================================
|
||||
//! @{ \name Area luminaire support
|
||||
// =============================================================
|
||||
|
||||
/**
|
||||
* \brief Fill the supplied emission record with information matching
|
||||
* the associated ray.
|
||||
* \brief This function is specific to area luminaires (i.e. luminaires
|
||||
* that can be intersected by a \ref Scene::rayIntersect). It returns the
|
||||
* radiant emittance into a given direction.
|
||||
*
|
||||
* This function is only relevant to background luminaires. The default
|
||||
* implementation throws an exception, which states that the method is
|
||||
* unimplemented.
|
||||
*
|
||||
* \return \a true upon success
|
||||
* This is function is used when an area light source has been hit by a
|
||||
* ray in a path tracing-style integrator, and it subsequently needs to
|
||||
* be queried for the emitted radiance along the negative ray direction.
|
||||
|
||||
* The default implementation throws an exception, which states that
|
||||
* the method is not implemented.
|
||||
*/
|
||||
virtual bool createEmissionRecord(EmissionRecord &eRec, const Ray &ray) const;
|
||||
virtual Spectrum Le(const Intersection &its, const Vector &d) const;
|
||||
|
||||
/**
|
||||
* Return an estimate of the total amount of power emitted
|
||||
* by this luminaire.
|
||||
*/
|
||||
virtual Spectrum getPower() const = 0;
|
||||
//! @}
|
||||
// =============================================================
|
||||
|
||||
// =============================================================
|
||||
//! @{ \name Background luminaire support
|
||||
// =============================================================
|
||||
|
||||
/// Is this a background luminaire (e.g. an environment map?)
|
||||
virtual bool isBackgroundLuminaire() const;
|
||||
|
||||
/// Is this luminaire intersetable (e.g. can it be found by a tracing a ray)?
|
||||
inline bool isIntersectable() const { return m_intersectable; }
|
||||
/**
|
||||
* \brief Return the radiant emittance along a ray which does not
|
||||
* intersect any scene objects.
|
||||
*
|
||||
* The default implementation throws an exception, which states that
|
||||
* the method is not implemented.
|
||||
*/
|
||||
virtual Spectrum Le(const Ray &ray) const;
|
||||
|
||||
/**
|
||||
* \brief This function fills an emission sampling record with relevant
|
||||
* information for the supplied ray (which doesn't intersect \a any
|
||||
* scene objects).
|
||||
*
|
||||
* The record will be populated with the radiant emittance, as well as
|
||||
* the spatial and directional densities for sampling an emitted ray along
|
||||
* the opposite ray direction.
|
||||
*
|
||||
* This function is only relevant to background luminaires. The
|
||||
* default implementation throws an exception, which states that
|
||||
* the method is not implemented.
|
||||
*
|
||||
* \return \c true upon success
|
||||
*/
|
||||
virtual bool createEmissionRecord(EmissionRecord &eRec, const Ray &ray) const;
|
||||
|
||||
//! @}
|
||||
// =============================================================
|
||||
|
||||
// =============================================================
|
||||
//! @{ \name Miscellaneous
|
||||
// =============================================================
|
||||
|
||||
/// Serialize this luminaire to disk
|
||||
virtual void serialize(Stream *stream, InstanceManager *manager) const;
|
||||
|
||||
/// Return the name of this luminaire
|
||||
inline const std::string &getName() const { return m_name; }
|
||||
|
||||
/// Return the luminaire type
|
||||
inline int getType() const { return m_type; }
|
||||
|
||||
/// Optional pre-process step before rendering starts
|
||||
virtual void preprocess(const Scene *scene);
|
||||
|
||||
/**
|
||||
* \brief Return the luminaire's sampling weight. This is used by
|
||||
* the luminaire importance sampling routines in \ref Scene.
|
||||
*/
|
||||
inline Float getSamplingWeight() const { return m_samplingWeight; }
|
||||
//! @}
|
||||
// =============================================================
|
||||
|
||||
MTS_DECLARE_CLASS()
|
||||
protected:
|
||||
|
@ -280,6 +357,7 @@ protected:
|
|||
protected:
|
||||
Transform m_worldToLuminaire, m_luminaireToWorld;
|
||||
Float m_samplingWeight;
|
||||
ref<Medium> m_medium;
|
||||
int m_type;
|
||||
bool m_intersectable;
|
||||
std::string m_name;
|
||||
|
|
|
@ -29,12 +29,12 @@ MTS_NAMESPACE_BEGIN
|
|||
*/
|
||||
struct MTS_EXPORT_RENDER MediumSamplingRecord {
|
||||
public:
|
||||
/// Create an invalid medium sampling record
|
||||
inline MediumSamplingRecord() : medium(NULL) { }
|
||||
inline MediumSamplingRecord() { }
|
||||
|
||||
/// Return a string representation
|
||||
std::string toString() const;
|
||||
public:
|
||||
|
||||
/// Traveled distance
|
||||
Float t;
|
||||
|
||||
|
@ -44,9 +44,6 @@ public:
|
|||
/// Local particle orientation at \ref p
|
||||
Vector orientation;
|
||||
|
||||
/// Reference to the associated medium
|
||||
const Medium *medium;
|
||||
|
||||
/**
|
||||
* \brief Specifies the attenuation along the segment [mint, t]
|
||||
*
|
||||
|
@ -94,16 +91,6 @@ public:
|
|||
*/
|
||||
class MTS_EXPORT_RENDER Medium : public NetworkedObject {
|
||||
public:
|
||||
/**
|
||||
* \brief Possibly perform a pre-process task.
|
||||
*
|
||||
* The last three parameters are resource IDs of the associated scene,
|
||||
* camera and sample generator, which have been made available to all
|
||||
* local and remote workers.
|
||||
*/
|
||||
virtual void preprocess(const Scene *scene, RenderQueue *queue,
|
||||
const RenderJob *job, int sceneResID, int cameraResID, int samplerResID);
|
||||
|
||||
/**
|
||||
* \brief Compute the attenuation along a ray segment
|
||||
*
|
||||
|
|
|
@ -113,7 +113,7 @@ public:
|
|||
protected:
|
||||
/// Protected constructor
|
||||
inline ParticleTracer(int maxDepth, bool multipleScattering, int rrDepth)
|
||||
: m_maxDepth(maxDepth), m_multipleScattering(multipleScattering),
|
||||
: m_maxDepth(maxDepth),
|
||||
m_rrDepth(rrDepth) {
|
||||
}
|
||||
/// Protected constructor
|
||||
|
@ -124,7 +124,6 @@ protected:
|
|||
ref<Scene> m_scene;
|
||||
ref<Sampler> m_sampler;
|
||||
int m_maxDepth;
|
||||
bool m_multipleScattering;
|
||||
int m_rrDepth;
|
||||
};
|
||||
|
||||
|
|
|
@ -51,31 +51,35 @@ inline BSDFQueryRecord::BSDFQueryRecord(const Intersection &its, const Vector &w
|
|||
inline bool Intersection::hasSubsurface() const {
|
||||
return shape->hasSubsurface();
|
||||
}
|
||||
|
||||
inline bool Intersection::isLuminaire() const {
|
||||
return shape->isLuminaire();
|
||||
}
|
||||
|
||||
inline Spectrum Intersection::Le(const Vector &d) const {
|
||||
return shape->getLuminaire()->Le(
|
||||
LuminaireSamplingRecord(*this, d));
|
||||
return shape->getLuminaire()->Le(*this, d);
|
||||
}
|
||||
|
||||
inline Spectrum Intersection::LoSub(const Scene *scene, const Vector &d) const {
|
||||
return shape->getSubsurface()->Lo(scene, *this, d);
|
||||
}
|
||||
|
||||
|
||||
inline const BSDF *Intersection::getBSDF(const RayDifferential &ray) {
|
||||
const BSDF *bsdf = shape->getBSDF();
|
||||
if (bsdf->usesRayDifferentials() && !hasUVPartials)
|
||||
if (bsdf && bsdf->usesRayDifferentials() && !hasUVPartials)
|
||||
computePartials(ray);
|
||||
return bsdf;
|
||||
}
|
||||
|
||||
inline LuminaireSamplingRecord::LuminaireSamplingRecord(const Intersection &its, const Vector &dir) {
|
||||
sRec.p = its.p;
|
||||
sRec.n = its.geoFrame.n;
|
||||
d = dir;
|
||||
luminaire = its.shape->getLuminaire();
|
||||
inline bool Intersection::isMediumTransition() const {
|
||||
return shape->isMediumTransition();
|
||||
}
|
||||
|
||||
inline const Medium *Intersection::getTargetMedium(const Ray &ray) const {
|
||||
if (dot(ray.d, geoFrame.n) > 0)
|
||||
return shape->getExteriorMedium();
|
||||
else
|
||||
return shape->getInteriorMedium();
|
||||
}
|
||||
|
||||
inline bool RadianceQueryRecord::rayIntersect(const RayDifferential &ray) {
|
||||
|
@ -83,8 +87,8 @@ inline bool RadianceQueryRecord::rayIntersect(const RayDifferential &ray) {
|
|||
if (type & EIntersection) {
|
||||
scene->rayIntersect(ray, its);
|
||||
if (type & EOpacity)
|
||||
alpha = its.isValid() ? 1 : (1 - scene->getAttenuation(
|
||||
Ray(ray.o, ray.d, 0, its.t, ray.time)).average());
|
||||
// alpha = its.isValid() ? 1 : (1 - scene->getAttenuation(
|
||||
// Ray(ray.o, ray.d, 0, its.t, ray.time)).average());
|
||||
if (type & EDistance)
|
||||
dist = its.t;
|
||||
type ^= EIntersection; // unset the intersection bit
|
||||
|
|
|
@ -58,9 +58,9 @@ public:
|
|||
ERelativeError
|
||||
};
|
||||
|
||||
/* ==================================================================== */
|
||||
/* Initialization & Rendering */
|
||||
/* ==================================================================== */
|
||||
// =============================================================
|
||||
//! @{ \name Initialization and rendering
|
||||
// =============================================================
|
||||
|
||||
/// Construct a new, empty scene
|
||||
Scene(const Properties &props);
|
||||
|
@ -72,8 +72,8 @@ public:
|
|||
Scene(Stream *stream, InstanceManager *manager);
|
||||
|
||||
/**
|
||||
* Initialization step - _must_ be called before using any of the
|
||||
* methods provided by <tt>Scene</tt>.
|
||||
* \brief Initialize the scene. This function \a must be called
|
||||
* before using any of the methods in this class.
|
||||
*/
|
||||
void initialize();
|
||||
|
||||
|
@ -102,20 +102,22 @@ public:
|
|||
bool render(RenderQueue *queue, const RenderJob *job,
|
||||
int sceneResID, int cameraResID, int samplerResID);
|
||||
|
||||
/// Post-process step after rendering. Parameters are explained above
|
||||
void postprocess(RenderQueue *queue, const RenderJob *job,
|
||||
int sceneResID, int cameraResID, int samplerResID);
|
||||
|
||||
/// Write out the current (partially rendered) image
|
||||
void flush();
|
||||
|
||||
/**
|
||||
* This can be called asynchronously to cancel a running render job.
|
||||
* In this case, <tt>render()</tt> will quit with a return value of
|
||||
* <tt>false</tt>.
|
||||
* \brief Cancel a running rendering job
|
||||
*
|
||||
* This function can be called asynchronously, e.g. from a GUI.
|
||||
* In this case, \ref render() will quit with a return value of
|
||||
* \c false.
|
||||
*/
|
||||
void cancel();
|
||||
|
||||
/// Post-process step after rendering. Parameters are explained above
|
||||
void postprocess(RenderQueue *queue, const RenderJob *job,
|
||||
int sceneResID, int cameraResID, int samplerResID);
|
||||
|
||||
/// Add a child node to the scene
|
||||
void addChild(const std::string &name, ConfigurableObject *child);
|
||||
|
||||
|
@ -123,15 +125,58 @@ public:
|
|||
and addition of all child ConfigurableObjects.) */
|
||||
void configure();
|
||||
|
||||
/* ==================================================================== */
|
||||
/* Integrator interface */
|
||||
/* ==================================================================== */
|
||||
//! @}
|
||||
// =============================================================
|
||||
|
||||
/// Check for an intersection with scene objects
|
||||
inline bool rayIntersect(const Ray &pRay, Intersection &its) const {
|
||||
// =============================================================
|
||||
//! @{ \name Ray tracing
|
||||
// =============================================================
|
||||
|
||||
/**
|
||||
* \brief Intersect a ray against all primitives stored in the scene
|
||||
* and return detailed intersection information
|
||||
*
|
||||
* \param ray
|
||||
* A 3-dimensional ray data structure with minimum/maximum
|
||||
* extent information, as well as a time (which applies when
|
||||
* the shapes are animated)
|
||||
*
|
||||
* \param its
|
||||
* A detailed intersection record, which will be filled by the
|
||||
* intersection query
|
||||
*
|
||||
* \return \c true if an intersection was found
|
||||
*/
|
||||
inline bool rayIntersect(const Ray &ray, Intersection &its) const {
|
||||
return m_kdtree->rayIntersect(pRay, its);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Intersect a ray against all primitives stored in the scene
|
||||
* and return the traveled distance and intersected shape
|
||||
*
|
||||
* This function represents a performance compromise when the
|
||||
* intersected shape must be known, but there is no need for
|
||||
* a detailed intersection record.
|
||||
*
|
||||
* \param ray
|
||||
* A 3-dimensional ray data structure with minimum/maximum
|
||||
* extent information, as well as a time (which applies when
|
||||
* the shapes are animated)
|
||||
*
|
||||
* \param t
|
||||
* The traveled ray distance will be stored in this parameter
|
||||
|
||||
* \param t
|
||||
* A pointer to the intersected shape will be stored in this
|
||||
* parameter
|
||||
*
|
||||
* \return \c true if an intersection was found
|
||||
*/
|
||||
inline bool rayIntersect(const Ray &ray, Float &t, ConstShapePtr &shape) const {
|
||||
return m_kdtree->rayIntersect(ray, t, shape);
|
||||
}
|
||||
|
||||
/// Cast a shadow ray
|
||||
inline bool isOccluded(const Point &p1, const Point &p2, Float time) const {
|
||||
Ray ray(p1, p2-p1, time);
|
||||
|
@ -149,176 +194,138 @@ public:
|
|||
inline const BSphere &getBSphere() const {
|
||||
return m_bsphere;
|
||||
}
|
||||
|
||||
//! @}
|
||||
// =============================================================
|
||||
|
||||
// =============================================================
|
||||
//! @{ \name Luminaire query & sampling functions
|
||||
// =============================================================
|
||||
|
||||
/**
|
||||
* Sample a visible point on a luminaire (ideally uniform wrt. the solid angle of p)
|
||||
* @param p
|
||||
* Sample a visible point on a luminaire (ideally uniform wrt. solid angle at \c p)
|
||||
* \param p
|
||||
* An arbitrary point in the scene
|
||||
* @param lRec
|
||||
* A luminaire sampling record, which will hold information such as the
|
||||
* probability density, associated measure etc.
|
||||
* @param time
|
||||
* \param time
|
||||
* Associated time value -- this is needed to check the visibility when
|
||||
* objects are potentially moving over time
|
||||
* @param testVisibility
|
||||
* If this is true, a shadow-ray will be cast to ensure that no surface
|
||||
* blocks the path lRec.sRec.p <-> p.
|
||||
* @return
|
||||
* true if sampling was successful
|
||||
*/
|
||||
bool sampleLuminaire(const Point &p,
|
||||
LuminaireSamplingRecord &lRec, Float time,
|
||||
const Point2 &sample, bool testVisibility = true) const;
|
||||
|
||||
/**
|
||||
* Convenience method - similar to \a sampleLuminaire(), but also attenuates
|
||||
* lRec.Le by the integrated extinction coefficient on the path lRec.sRec.p <-> p.
|
||||
*/
|
||||
inline bool sampleLuminaireAttenuated(const Point &p,
|
||||
LuminaireSamplingRecord &lRec, Float time,
|
||||
const Point2 &sample, bool testVisibility = true) const {
|
||||
if (sampleLuminaire(p, lRec, time, sample, testVisibility)) {
|
||||
lRec.Le *= getAttenuation(Ray(p, lRec.sRec.p-p, 0, 1, time));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the probability of generating a sample on a luminaire if
|
||||
* \ref sampleLuminaire() is called with the point 'p'.
|
||||
* When \a delta is set to true, only components
|
||||
* with a Dirac delta density are queried.
|
||||
*/
|
||||
Float pdfLuminaire(const Point &p,
|
||||
const LuminaireSamplingRecord &lRec, bool delta = false) const;
|
||||
|
||||
/**
|
||||
* Sample a visible point on a luminaire (ideally uniform wrt. the solid angle of p). Takes
|
||||
* a surface intersection record (some luminaires make use of this to provide improved sampling)
|
||||
* @param its
|
||||
* An arbitrary surface intersection
|
||||
* @param lRec
|
||||
* \param lRec
|
||||
* A luminaire sampling record, which will hold information such as the
|
||||
* probability density, associated measure etc.
|
||||
* @param testVisibility
|
||||
* probability density, etc.
|
||||
* \param testVisibility
|
||||
* If this is true, a shadow-ray will be cast to ensure that no surface
|
||||
* blocks the path lRec.sRec.p <-> p.
|
||||
* @return
|
||||
* true if sampling was successful
|
||||
* \return
|
||||
* \c true if sampling was successful
|
||||
*/
|
||||
bool sampleLuminaire(const Intersection &its,
|
||||
LuminaireSamplingRecord &lRec, const Point2 &sample,
|
||||
bool testVisibility = true) const;
|
||||
bool sampleLuminaire(const Point &p, Float time, LuminaireSamplingRecord &lRec,
|
||||
const Point2 &sample, bool testVisibility = true) const;
|
||||
|
||||
/**
|
||||
* Convenience method - similar to sampleLuminaire(), but also attenuates
|
||||
* lRec.Le by the integrated extinction coefficient on the path lRec.sRec.p <-> p.
|
||||
* \brief Convenience method - similar to \ref sampleLuminaire(), but also attenuates
|
||||
* \c lRec.value by the integrated extinction coefficient along the connection path.
|
||||
* A visibility test is always performed.
|
||||
*
|
||||
* \param p
|
||||
* An arbitrary point in the scene
|
||||
* \param time
|
||||
* Associated time value -- this is needed to check the visibility when
|
||||
* objects are potentially moving over time
|
||||
* \param medium
|
||||
* The medium located at the ray origin (or \c NULL for vacuum).
|
||||
* \param lRec
|
||||
* A luminaire sampling record, which will hold information such
|
||||
* as the probability density, etc.
|
||||
* \return
|
||||
* \c true if sampling was successful
|
||||
*/
|
||||
inline bool sampleLuminaireAttenuated(const Intersection &its,
|
||||
LuminaireSamplingRecord &lRec, const Point2 &sample,
|
||||
bool testVisibility = true) const {
|
||||
if (sampleLuminaire(its, lRec, sample, testVisibility)) {
|
||||
lRec.Le *= getAttenuation(Ray(its.p, lRec.sRec.p-its.p, 0, 1, its.time));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool sampleAttenuatedLuminaire(const Point &p, Float time,
|
||||
const Medium *medium, LuminaireSamplingRecord &lRec,
|
||||
const Point2 &sample) const;
|
||||
|
||||
/**
|
||||
* Return the probability of generating a sample on a luminaire if
|
||||
* sampleLuminaire() is called with the surface interation 'its'
|
||||
* \brief Return the probability density associated with the sample \c lRec
|
||||
* when calling \ref sampleLuminaire() with the point \c p.
|
||||
*
|
||||
* \param p
|
||||
* An arbitrary point in the scene
|
||||
* \param lRec
|
||||
* Luminaire sampling record, whose associated density is to
|
||||
* be determined
|
||||
* \param delta
|
||||
* When this parameter is set to true, only components with a
|
||||
* Dirac delta density are queried. Otherwise, they are left out.
|
||||
*/
|
||||
Float pdfLuminaire(const Intersection &its,
|
||||
const LuminaireSamplingRecord &lRec, bool delta = false) const;
|
||||
Float pdfLuminaire(const Point &p, const LuminaireSamplingRecord &lRec,
|
||||
bool delta = false) const;
|
||||
|
||||
/**
|
||||
* Sample a particle leaving a luminaire and return a ray describing its path
|
||||
* as well as record containing detailed probability density information.
|
||||
* Two uniformly distributed 2D samples are required.
|
||||
* \brief Sample a particle leaving one of the scene's luminaires and
|
||||
* return a ray describing its path as well as record containing detailed
|
||||
* probability density information.
|
||||
*
|
||||
* Two uniformly distributed 2D samples are required. This method does
|
||||
* exactly the same as calling \c sampleEmissionArea and
|
||||
* \c eRec.luminaire->sampleEmissionDirection(..) in sequence,
|
||||
* modulating \c eRec.Le by the return value of the latter and dividing
|
||||
* by the product of the spatial and directional sampling densities.
|
||||
*/
|
||||
|
||||
void sampleEmission(EmissionRecord &lRec, Point2 &sample1, Point2 &sample2) const;
|
||||
|
||||
/**
|
||||
* Sample only the spatial dimension of the emission sampling strategy
|
||||
* implemented in <tt>sampleEmission</tt>. An examplary use of this
|
||||
* is bidirectional path tracing, where the directionally varying part
|
||||
* is handed similarly to a BSDF, which modulates the spatially
|
||||
* dependent radiance component. After this returns, the area density
|
||||
* as well as the spatially dependent radiance component
|
||||
* will be stored in <tt>eRec</tt>.
|
||||
* \brief Sample only the spatial part of the emission sampling strategy
|
||||
* implemented in \c sampleEmission.
|
||||
*
|
||||
* An examplary use of this method is bidirectional path tracing or MLT,
|
||||
* where the area and direction sampling steps take place in different
|
||||
* vertices.
|
||||
*
|
||||
* After the function call terminates, the area density as well as the
|
||||
* spatially dependent emittance component will be stored in \c eRec.
|
||||
*/
|
||||
void sampleEmissionArea(EmissionRecord &lRec, Point2 &sample) const;
|
||||
|
||||
/**
|
||||
* As above, but handles only the directional part. Must be called *after*
|
||||
* sampleEmissionArea(). The return value is to be understood as a BRDF,
|
||||
* which modulates the emitted energy.
|
||||
*/
|
||||
Spectrum sampleEmissionDirection(EmissionRecord &lRec, Point2 &sample) const;
|
||||
|
||||
/**
|
||||
* Given an emitted particle, populate the emission record with the
|
||||
* relevant spectra and probability densities.
|
||||
* When \a delta is set to true, only components
|
||||
* with a Dirac delta density are queried.
|
||||
* \brief Given an emitted particle, populate the emission record with the
|
||||
* relevant probability densities.
|
||||
*
|
||||
* When \c delta is set to true, only components with a Dirac delta density
|
||||
* are considered in the query. Otherwise, they are left out.
|
||||
*/
|
||||
void pdfEmission(EmissionRecord &eRec, bool delta) const;
|
||||
|
||||
/**
|
||||
* Return the background radiance for a ray, which did not hit anything.
|
||||
* \brief Return the background radiance for a ray that did not intersect
|
||||
* any of the scene objects.
|
||||
*
|
||||
* This is primarily meant for path tracing-style integrators.
|
||||
*/
|
||||
Spectrum LeBackground(const Ray &ray) const;
|
||||
|
||||
/**
|
||||
* Return the background radiance for a ray, which did not hit anything.
|
||||
* (attenuated by any participating media along the ray)
|
||||
* \brief Return the background radiance for a ray that did not intersect
|
||||
* any of the scene objects. This method additionally considers
|
||||
* attenuation by participating media
|
||||
*
|
||||
* This is primarily meant for path tracing-style integrators.
|
||||
*/
|
||||
inline Spectrum LeBackgroundAttenuated(const Ray &ray) const {
|
||||
inline Spectrum LeAttenuatedBackground(const Ray &ray, const Medium *medium) const {
|
||||
if (!m_backgroundLuminaire)
|
||||
return Spectrum(0.0f);
|
||||
return LeBackground(ray) * getAttenuation(ray);
|
||||
return LeBackground(ray) * medium->tau(ray);
|
||||
}
|
||||
|
||||
//! @}
|
||||
// =============================================================
|
||||
|
||||
/**
|
||||
* \brief Calculate the attenuation along the ray segment [mint, maxt]
|
||||
*
|
||||
* As a special exception, this function doesn't require
|
||||
* the ray distance field to be normalized
|
||||
*/
|
||||
Spectrum getAttenuation(const Ray &ray) const;
|
||||
// =============================================================
|
||||
//! @{ \name Miscellaneous
|
||||
// =============================================================
|
||||
|
||||
/// Does the scene contain participating media?
|
||||
inline bool hasMedia() const { return !m_media.empty(); }
|
||||
|
||||
/**
|
||||
* \brief In the presence of participating media, sample the
|
||||
* in-scattering line integral of the RTE.
|
||||
*
|
||||
* Should ideally importance sample with respect to the
|
||||
* sample contribution. The ray direction is required to
|
||||
* be normalized.
|
||||
*
|
||||
* \return false if the ray passes beyond \a ray.maxt (or
|
||||
* if there were no participating media at all).
|
||||
*/
|
||||
bool sampleDistance(const Ray &ray,
|
||||
MediumSamplingRecord &mRec, Sampler *sampler) const;
|
||||
|
||||
/**
|
||||
* \brief Compute the density of sampling distance \a t along the
|
||||
* ray using the sampling strategy implemented by \a sampleDistance.
|
||||
*
|
||||
* The function computes the continuous densities in the case of
|
||||
* a successful \ref sampleDistance() invocation (in both directions),
|
||||
* as well as the Dirac delta density associated with a failure.
|
||||
* For convenience, it also stores the attenuation along the ray
|
||||
* segment in \a mRec.
|
||||
*/
|
||||
void pdfDistance(const Ray &ray, Float t,
|
||||
MediumSamplingRecord &mRec) const;
|
||||
|
||||
/// Return the scene's test mode
|
||||
inline ETestType getTestType() const { return m_testType; }
|
||||
/// Return the scene's test threshold
|
||||
|
@ -440,6 +447,9 @@ public:
|
|||
/// Return a string representation
|
||||
std::string toString() const;
|
||||
|
||||
//! @}
|
||||
// =============================================================
|
||||
|
||||
MTS_DECLARE_CLASS()
|
||||
protected:
|
||||
/// Virtual destructor
|
||||
|
|
|
@ -79,6 +79,15 @@ public:
|
|||
|
||||
/// Does the intersected shape have a subsurface integrator?
|
||||
inline bool hasSubsurface() const;
|
||||
|
||||
/// Does the surface mark a transition between two media?
|
||||
inline bool isMediumTransition() const;
|
||||
|
||||
/**
|
||||
* \brief When \c isMediumTransition() = \c true,
|
||||
* determine the target medium based on \c ray
|
||||
*/
|
||||
inline const Medium *getTargetMedium(const Ray &ray) const;
|
||||
|
||||
/**
|
||||
* Returns the BSDF of the intersected shape. The
|
||||
|
@ -152,6 +161,13 @@ public:
|
|||
*/
|
||||
class MTS_EXPORT_RENDER Shape : public ConfigurableObject {
|
||||
public:
|
||||
// =============================================================
|
||||
//! @{ \name Query functions to be implemented in subclasses
|
||||
// =============================================================
|
||||
|
||||
/// Return the name of this shape
|
||||
virtual std::string getName() const;
|
||||
|
||||
/// Is this a compound shape consisting of several sub-objects?
|
||||
virtual bool isCompound() const;
|
||||
|
||||
|
@ -164,6 +180,7 @@ public:
|
|||
*/
|
||||
virtual Shape *getElement(int i);
|
||||
|
||||
|
||||
/// Return the shape's surface area
|
||||
virtual Float getSurfaceArea() const = 0;
|
||||
|
||||
|
@ -180,6 +197,23 @@ public:
|
|||
*/
|
||||
virtual AABB getClippedAABB(const AABB &box) const;
|
||||
|
||||
/**
|
||||
* \brief Create a triangle mesh approximation of this shape
|
||||
*
|
||||
* This function is used by the realtime preview and
|
||||
* certain integrators, which rely on hardware rasterization.
|
||||
*
|
||||
* The default implementation simply returns \a NULL.
|
||||
*/
|
||||
virtual ref<TriMesh> createTriMesh();
|
||||
|
||||
//! @}
|
||||
// =============================================================
|
||||
|
||||
// =============================================================
|
||||
//! @{ \name Ray tracing routines
|
||||
// =============================================================
|
||||
|
||||
/**
|
||||
* \brief Fast ray intersection test
|
||||
*
|
||||
|
@ -212,6 +246,21 @@ public:
|
|||
virtual void fillIntersectionRecord(const Ray &ray,
|
||||
const void *temp, Intersection &its) const;
|
||||
|
||||
/**
|
||||
* \brief Return the internal kd-tree of this shape (if any)
|
||||
*
|
||||
* This function is used by the kd-tree visualization in
|
||||
* the interactive walkthrough. The default implementation
|
||||
* simply returns NULL.
|
||||
*/
|
||||
virtual const KDTreeBase<AABB> *getKDTree() const;
|
||||
|
||||
//! @}
|
||||
// =============================================================
|
||||
|
||||
// =============================================================
|
||||
//! @{ \name Sampling routines
|
||||
// =============================================================
|
||||
/**
|
||||
* \brief Sample a point on the shape
|
||||
*
|
||||
|
@ -229,11 +278,11 @@ public:
|
|||
|
||||
/**
|
||||
* \brief Sample a point on the shape and return the associated
|
||||
* probability wrt. solid angle.
|
||||
* probability with respect to solid angle at \c x.
|
||||
*
|
||||
* Should ideally be uniform wrt. solid angle as seen
|
||||
* from \a x. The default implementation, just uses
|
||||
* \ref sampleArea, which can lead to lots of noise.
|
||||
* \ref sampleArea, which can produce high variance.
|
||||
*/
|
||||
virtual Float sampleSolidAngle(ShapeSamplingRecord &sRec,
|
||||
const Point &x, const Point2 &sample) const;
|
||||
|
@ -245,34 +294,26 @@ public:
|
|||
virtual Float pdfSolidAngle(const ShapeSamplingRecord &sRec,
|
||||
const Point &x) const;
|
||||
|
||||
/**
|
||||
* \brief Return the internal kd-tree of this shape (if any)
|
||||
*
|
||||
* This function is used by the kd-tree visualization in
|
||||
* the interactive walkthrough. The default implementation
|
||||
* simply returns NULL.
|
||||
*/
|
||||
virtual const KDTreeBase<AABB> *getKDTree() const;
|
||||
//! @}
|
||||
// =============================================================
|
||||
|
||||
// =============================================================
|
||||
//! @{ \name Miscellaneous
|
||||
// =============================================================
|
||||
|
||||
/**
|
||||
* \brief Create a triangle mesh approximation of this shape
|
||||
*
|
||||
* This function is used by the realtime preview and
|
||||
* certain integrators, which rely on hardware rasterization.
|
||||
*
|
||||
* The default implementation simply returns \a NULL.
|
||||
*/
|
||||
virtual ref<TriMesh> createTriMesh();
|
||||
|
||||
/// Return the shape's BSDF
|
||||
inline const BSDF *getBSDF() const { return m_bsdf.get(); }
|
||||
/// Return the shape's BSDF
|
||||
inline BSDF *getBSDF() { return m_bsdf.get(); }
|
||||
/// Set the BSDF of this shape
|
||||
inline void setBSDF(BSDF *bsdf) { m_bsdf = bsdf; }
|
||||
|
||||
/// Return the name of this shape
|
||||
virtual std::string getName() const;
|
||||
/// Does this surface act as an occluder?
|
||||
inline bool isOccluder() const { return m_bsdf != NULL; }
|
||||
|
||||
/// Does the surface of this shape mark a medium transition?
|
||||
inline bool isMediumTransition() const { return m_interiorMedium.get() || m_exteriorMedium.get(); }
|
||||
/// Return the medium that lies on the interior of this shape (\c NULL == vacuum)
|
||||
inline Medium *getInteriorMedium() { return m_interiorMedium; }
|
||||
/// Return the medium that lies on the interior of this shape (\c NULL == vacuum, const version)
|
||||
inline const Medium *getInteriorMedium() const { return m_interiorMedium.get(); }
|
||||
/// Return the medium that lies on the exterior of this shape (\c NULL == vacuum)
|
||||
inline Medium *getExteriorMedium() { return m_exteriorMedium; }
|
||||
/// Return the medium that lies on the exterior of this shape (\c NULL == vacuum, const version)
|
||||
inline const Medium *getExteriorMedium() const { return m_exteriorMedium.get(); }
|
||||
|
||||
/// Does this shape have a sub-surface integrator?
|
||||
inline bool hasSubsurface() const { return m_subsurface.get() != NULL; }
|
||||
|
@ -290,6 +331,13 @@ public:
|
|||
/// Set the luminaire of this shape
|
||||
inline void setLuminaire(Luminaire *luminaire) { m_luminaire = luminaire; }
|
||||
|
||||
/// Return the shape's BSDF
|
||||
inline const BSDF *getBSDF() const { return m_bsdf.get(); }
|
||||
/// Return the shape's BSDF
|
||||
inline BSDF *getBSDF() { return m_bsdf.get(); }
|
||||
/// Set the BSDF of this shape
|
||||
inline void setBSDF(BSDF *bsdf) { m_bsdf = bsdf; }
|
||||
|
||||
/// Called once after parsing
|
||||
virtual void configure();
|
||||
/// Serialize this shape to a stream
|
||||
|
@ -298,6 +346,9 @@ public:
|
|||
/// Add a child (e.g. a luminaire/sub surface integrator) to this shape
|
||||
void addChild(const std::string &name, ConfigurableObject *child);
|
||||
|
||||
//! @}
|
||||
// =============================================================
|
||||
|
||||
MTS_DECLARE_CLASS()
|
||||
protected:
|
||||
/// Create a new shape
|
||||
|
@ -312,6 +363,7 @@ protected:
|
|||
ref<BSDF> m_bsdf;
|
||||
ref<Subsurface> m_subsurface;
|
||||
ref<Luminaire> m_luminaire;
|
||||
ref<Medium> m_interiorMedium, m_exteriorMedium;
|
||||
};
|
||||
|
||||
MTS_NAMESPACE_END
|
||||
|
|
|
@ -55,7 +55,7 @@ MTS_NAMESPACE_BEGIN
|
|||
* and Interactive Global Illumination". This adds an overhead of 48 bytes per
|
||||
* triangle.
|
||||
*
|
||||
* When compiled with MTS_KD_CONSERVE_MEMORY, the Moeller-Trumbore intersection
|
||||
* When compiled with \c MTS_KD_CONSERVE_MEMORY, the Moeller-Trumbore intersection
|
||||
* test is used instead, which doesn't need any extra storage. However, it also
|
||||
* tends to be quite a bit slower.
|
||||
*
|
||||
|
@ -68,6 +68,11 @@ class MTS_EXPORT_RENDER ShapeKDTree : public SAHKDTree3D<ShapeKDTree> {
|
|||
friend class Instance;
|
||||
friend class AnimatedInstance;
|
||||
public:
|
||||
typedef const Shape * ConstShapePtr;
|
||||
|
||||
// =============================================================
|
||||
//! @{ \name Initialization and tree construction
|
||||
// =============================================================
|
||||
/// Create an empty kd-tree
|
||||
ShapeKDTree();
|
||||
|
||||
|
@ -78,23 +83,86 @@ public:
|
|||
inline const std::vector<const Shape *> &getShapes() const { return m_shapes; }
|
||||
|
||||
/**
|
||||
* \brief Return an axis-aligned bounding box containing all primitives
|
||||
* \brief Return the total number of low-level primitives (triangles
|
||||
* and other low-level primitives)
|
||||
*/
|
||||
inline size_type getPrimitiveCount() const {
|
||||
return m_shapeMap[m_shapeMap.size()-1];
|
||||
}
|
||||
|
||||
/// Return an axis-aligned bounding box containing all primitives
|
||||
inline const AABB &getAABB() const { return m_aabb; }
|
||||
|
||||
|
||||
/// Return an bounding sphere containing all primitives
|
||||
inline const BSphere &getBSphere() const { return m_bsphere; }
|
||||
|
||||
/// Build the kd-tree (needs to be called before tracing any rays)
|
||||
void build();
|
||||
|
||||
//! @}
|
||||
// =============================================================
|
||||
|
||||
// =============================================================
|
||||
//! @{ \name Ray tracing routines
|
||||
// =============================================================
|
||||
|
||||
/**
|
||||
* \brief Intersect a ray against all primitives stored in the kd-tree
|
||||
* and return detailed intersection information
|
||||
*
|
||||
* \param ray
|
||||
* A 3-dimensional ray data structure with minimum/maximum
|
||||
* extent information, as well as a time (which applies when
|
||||
* the shapes are animated)
|
||||
*
|
||||
* \param its
|
||||
* A detailed intersection record, which will be filled by the
|
||||
* intersection query
|
||||
*
|
||||
* \return \c true if an intersection was found
|
||||
*/
|
||||
bool rayIntersect(const Ray &ray, Intersection &its) const;
|
||||
|
||||
/**
|
||||
* \brief Test a ray for intersection against all primitives stored in the kd-tree
|
||||
* \brief Intersect a ray against all primitives stored in the kd-tree
|
||||
* and return the traveled distance and intersected shape
|
||||
*
|
||||
* This function represents a performance compromise when the
|
||||
* intersected shape must be known, but there is no need for
|
||||
* a detailed intersection record.
|
||||
*
|
||||
* \param ray
|
||||
* A 3-dimensional ray data structure with minimum/maximum
|
||||
* extent information, as well as a time (which applies when
|
||||
* the shapes are animated)
|
||||
*
|
||||
* \param t
|
||||
* The traveled ray distance will be stored in this parameter
|
||||
|
||||
* \param t
|
||||
* A pointer to the intersected shape will be stored in this
|
||||
* parameter
|
||||
*
|
||||
* \return \c true if an intersection was found
|
||||
*/
|
||||
bool rayIntersect(const Ray &ray, Float &t, ConstShapePtr &shape) const;
|
||||
|
||||
/**
|
||||
* \brief Test a ray for occlusion with respect to all primitives
|
||||
* stored in the kd-tree.
|
||||
*
|
||||
* This function does not compute a detailed intersection record,
|
||||
* and it never determines the closest intersection, which makes
|
||||
* it quite a bit faster than the other two \c rayIntersect() methods.
|
||||
* However, for this reason, it can only be used to check whether
|
||||
* there is \a any occlusion along a ray or ray segment.
|
||||
*
|
||||
* \param ray
|
||||
* A 3-dimensional ray data structure with minimum/maximum
|
||||
* extent information, as well as a time (which applies when
|
||||
* the shapes are animated)
|
||||
*
|
||||
* \return \c true if there is occlusion
|
||||
*/
|
||||
bool rayIntersect(const Ray &ray) const;
|
||||
|
||||
|
@ -113,22 +181,21 @@ public:
|
|||
void rayIntersectPacketIncoherent(const RayPacket4 &packet,
|
||||
const RayInterval4 &interval, Intersection4 &its, void *temp) const;
|
||||
#endif
|
||||
|
||||
FINLINE size_type getPrimitiveCount() const {
|
||||
return m_shapeMap[m_shapeMap.size()-1];
|
||||
}
|
||||
//! @}
|
||||
// =============================================================
|
||||
|
||||
MTS_DECLARE_CLASS()
|
||||
protected:
|
||||
/**
|
||||
* \brief Return the shape index corresponding to a primitive index
|
||||
* seen by the generic kd-tree implementation. When this is a triangle
|
||||
* mesh, the \a idx parameter is updated to the triangle index within
|
||||
* the mesh.
|
||||
* seen by the generic kd-tree implementation.
|
||||
*
|
||||
* When this is a triangle mesh, the \a idx parameter is updated to the
|
||||
* triangle index within the mesh.
|
||||
*/
|
||||
index_type findShape(index_type &idx) const {
|
||||
FINLINE index_type findShape(index_type &idx) const {
|
||||
std::vector<index_type>::const_iterator it = std::lower_bound(
|
||||
m_shapeMap.begin(), m_shapeMap.end(), idx+1) - 1;
|
||||
m_shapeMap.begin(), m_shapeMap.end(), idx + 1) - 1;
|
||||
idx -= *it;
|
||||
return (index_type) (it - m_shapeMap.begin());
|
||||
}
|
||||
|
@ -195,7 +262,7 @@ protected:
|
|||
} else {
|
||||
const Shape *shape = m_shapes[shapeIdx];
|
||||
if (shape->rayIntersect(ray, mint, maxt, t,
|
||||
reinterpret_cast<uint8_t*>(temp) + 8)) {
|
||||
reinterpret_cast<uint8_t*>(temp) + 8)) {
|
||||
cache->shapeIndex = shapeIdx;
|
||||
cache->primIndex = KNoTriangleFlag;
|
||||
return true;
|
||||
|
@ -217,7 +284,7 @@ protected:
|
|||
uint32_t shapeIndex = ta.shapeIndex;
|
||||
const Shape *shape = m_shapes[shapeIndex];
|
||||
if (shape->rayIntersect(ray, mint, maxt, t,
|
||||
reinterpret_cast<uint8_t*>(temp) + 8)) {
|
||||
reinterpret_cast<uint8_t*>(temp) + 8)) {
|
||||
cache->shapeIndex = shapeIndex;
|
||||
cache->primIndex = KNoTriangleFlag;
|
||||
return true;
|
||||
|
@ -243,22 +310,25 @@ protected:
|
|||
if (tri.rayIntersect(mesh->getVertexPositions(), ray,
|
||||
tempU, tempV, tempT)) {
|
||||
if (tempT >= mint && tempT <= maxt)
|
||||
return true;
|
||||
return mesh->isOccluder();
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
const Shape *shape = m_shapes[shapeIdx];
|
||||
return shape->rayIntersect(ray, mint, maxt);
|
||||
return shape->isOccluder() &&
|
||||
shape->rayIntersect(ray, mint, maxt);
|
||||
}
|
||||
#else
|
||||
const TriAccel &ta = m_triAccel[idx];
|
||||
uint32_t shapeIndex = ta.shapeIndex;
|
||||
const Shape *shape = m_shapes[shapeIndex];
|
||||
if (EXPECT_TAKEN(m_triAccel[idx].k != KNoTriangleFlag)) {
|
||||
Float tempU, tempV, tempT;
|
||||
return ta.rayIntersect(ray, mint, maxt, tempU, tempV, tempT);
|
||||
return shape->isOccluder() &&
|
||||
ta.rayIntersect(ray, mint, maxt, tempU, tempV, tempT);
|
||||
} else {
|
||||
uint32_t shapeIndex = ta.shapeIndex;
|
||||
const Shape *shape = m_shapes[shapeIndex];
|
||||
return shape->rayIntersect(ray, mint, maxt);
|
||||
return shape->isOccluder() &&
|
||||
shape->rayIntersect(ray, mint, maxt);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -67,6 +67,10 @@ public:
|
|||
* the specified index determines which one to load.
|
||||
*/
|
||||
TriMesh(Stream *stream, int idx = 0);
|
||||
|
||||
// =============================================================
|
||||
//! @{ \name General query functions
|
||||
// =============================================================
|
||||
|
||||
/// Return the name of this mesh
|
||||
virtual std::string getName() const;
|
||||
|
@ -77,8 +81,25 @@ public:
|
|||
/// Return a bounding box containing the mesh
|
||||
virtual AABB getAABB() const;
|
||||
|
||||
/**
|
||||
* \brief Create a triangle mesh approximation of this shape
|
||||
*
|
||||
* Since instances are already triangle meshes, the implementation
|
||||
* just returns a pointer to \a this.
|
||||
*/
|
||||
ref<TriMesh> createTriMesh();
|
||||
|
||||
//! @}
|
||||
// =============================================================
|
||||
|
||||
// =============================================================
|
||||
//! @{ \name Access to the stored triangle mesh
|
||||
// =============================================================
|
||||
|
||||
/// Return the number of triangles
|
||||
inline size_t getTriangleCount() const { return m_triangleCount; }
|
||||
/// Return the number of vertices
|
||||
inline size_t getVertexCount() const { return m_vertexCount; }
|
||||
|
||||
/// Return the triangle list (const version)
|
||||
inline const Triangle *getTriangles() const { return m_triangles; };
|
||||
|
@ -118,22 +139,12 @@ public:
|
|||
/// Does the mesh have vertex tangents?
|
||||
inline bool hasVertexTangents() const { return m_tangents != NULL; };
|
||||
|
||||
/// Return the number of vertices
|
||||
inline size_t getVertexCount() const { return m_vertexCount; }
|
||||
//! @}
|
||||
// =============================================================
|
||||
|
||||
/**
|
||||
* \brief Create a triangle mesh approximation of this shape
|
||||
*
|
||||
* Since instances are already triangle meshes, the implementation
|
||||
* just returns a pointer to \a this.
|
||||
*/
|
||||
ref<TriMesh> createTriMesh();
|
||||
|
||||
/// Export an Wavefront OBJ version of this file
|
||||
void writeOBJ(const fs::path &path) const;
|
||||
|
||||
/// Return the shape's BSDF
|
||||
inline const BSDF *getBSDF() const { return m_bsdf.get(); }
|
||||
// =============================================================
|
||||
//! @{ \name Sampling routines
|
||||
// =============================================================
|
||||
|
||||
/// Sample a point on the mesh
|
||||
Float sampleArea(ShapeSamplingRecord &sRec, const Point2 &sample) const;
|
||||
|
@ -143,6 +154,13 @@ public:
|
|||
* given point using \ref sampleArea()
|
||||
*/
|
||||
Float pdfArea(const ShapeSamplingRecord &sRec) const;
|
||||
|
||||
//! @}
|
||||
// =============================================================
|
||||
|
||||
// =============================================================
|
||||
//! @{ \name Miscellaneous
|
||||
// =============================================================
|
||||
|
||||
/**
|
||||
* \brief Generate tangent space basis vectors.
|
||||
|
@ -174,10 +192,11 @@ public:
|
|||
void serialize(Stream *stream, InstanceManager *manager) const;
|
||||
|
||||
/**
|
||||
* Serialize to a file/network stream - this is an alternative
|
||||
* routine, which only loads triangle data (no BSDF,
|
||||
* Sub-surface integrator, etc.) in a format that
|
||||
* will remain stable as mitsuba evolves.
|
||||
* \brief Serialize to a file/network stream
|
||||
*
|
||||
* This is an alternative routine, which \a only loads triangle
|
||||
* data (no BSDF, Sub-surface integrator, etc.) in a format that
|
||||
* will remain stable as Mitsuba evolves.
|
||||
*/
|
||||
void serialize(Stream *stream) const;
|
||||
|
||||
|
@ -189,9 +208,15 @@ public:
|
|||
*/
|
||||
virtual void configure();
|
||||
|
||||
/// Export an Wavefront OBJ version of this file
|
||||
void writeOBJ(const fs::path &path) const;
|
||||
|
||||
/// Return a string representation
|
||||
std::string toString() const;
|
||||
|
||||
//! @}
|
||||
// =============================================================
|
||||
|
||||
MTS_DECLARE_CLASS()
|
||||
protected:
|
||||
/// Create a new triangle mesh
|
||||
|
|
|
@ -26,8 +26,7 @@ const bool importanceSampleComponents = true;
|
|||
/**
|
||||
* Models an interface between two materials with non-matched indices of refraction.
|
||||
* The microscopic surface structure is assumed to be perfectly flat, resulting
|
||||
* in a BSDF equal to a Dirac delta function. When spectral rendering is active,
|
||||
* dispersion is simulated using Cauchy's equation.
|
||||
* in a BSDF equal to a Dirac delta function.
|
||||
*
|
||||
* The default settings are set to a borosilicate glass BK7/air interface.
|
||||
*/
|
||||
|
@ -39,10 +38,6 @@ public:
|
|||
m_intIOR = props.getFloat("intIOR", 1.5046f);
|
||||
/* Specifies the external index of refraction at the interface */
|
||||
m_extIOR = props.getFloat("extIOR", 1);
|
||||
/* B term from Cauchy's equation (units: um^2) - internal */
|
||||
m_intB = props.getFloat("intB", 0.00420f);
|
||||
/* B term from Cauchy's equation (units: um^2) - external */
|
||||
m_extB = props.getFloat("extB", 0);
|
||||
/* Reflectance modulation term */
|
||||
m_reflectance = props.getSpectrum("specularReflectance", Spectrum(1.0f));
|
||||
/* Transmittance modulation term */
|
||||
|
@ -60,8 +55,6 @@ public:
|
|||
: BSDF(stream, manager) {
|
||||
m_intIOR = stream->readFloat();
|
||||
m_extIOR = stream->readFloat();
|
||||
m_intB = stream->readFloat();
|
||||
m_extB = stream->readFloat();
|
||||
m_transmittance = Spectrum(stream);
|
||||
m_reflectance = Spectrum(stream);
|
||||
|
||||
|
@ -82,8 +75,6 @@ public:
|
|||
|
||||
stream->writeFloat(m_intIOR);
|
||||
stream->writeFloat(m_extIOR);
|
||||
stream->writeFloat(m_intB);
|
||||
stream->writeFloat(m_extB);
|
||||
m_transmittance.serialize(stream);
|
||||
m_reflectance.serialize(stream);
|
||||
}
|
||||
|
@ -155,36 +146,7 @@ public:
|
|||
bool sampleTransmission = (bRec.typeMask & EDeltaTransmission)
|
||||
&& (bRec.component == -1 || bRec.component == 1);
|
||||
|
||||
#if SPECTRUM_SAMPLES != 3
|
||||
Spectrum result(0.0f);
|
||||
Float intIOR, extIOR;
|
||||
int wavelengthSample;
|
||||
|
||||
/* Sample a wavelength */
|
||||
if (bRec.rRec) {
|
||||
if (bRec.rRec->wavelength == -1) {
|
||||
/* If we are already tracing a monochromatic ray,
|
||||
keep it that way */
|
||||
wavelengthSample = bRec.rRec->wavelength
|
||||
= (int) std::min(sample.y * SPECTRUM_SAMPLES,
|
||||
SPECTRUM_SAMPLES-1);
|
||||
} else {
|
||||
wavelengthSample = bRec.rRec->wavelength;
|
||||
}
|
||||
} else {
|
||||
wavelengthSample = (int) std::min(sample.y * SPECTRUM_SAMPLES,
|
||||
SPECTRUM_SAMPLES-1);
|
||||
}
|
||||
|
||||
/* Wavelength in um */
|
||||
Float wavelength = Spectrum::getWavelength(bRec.rRec->wavelength) * 0.001f;
|
||||
intIOR = m_intIOR + m_intB / (wavelength*wavelength);
|
||||
extIOR = m_extIOR + m_extB / (wavelength*wavelength);
|
||||
result[wavelengthEntry] = SPECTRUM_SAMPLES;
|
||||
#else
|
||||
Float intIOR = m_intIOR, extIOR = m_extIOR;
|
||||
#endif
|
||||
Float fr = fresnel(Frame::cosTheta(bRec.wi), extIOR, intIOR);
|
||||
Float fr = fresnel(Frame::cosTheta(bRec.wi), m_extIOR, m_intIOR);
|
||||
|
||||
/* Calculate the refracted/reflected vectors+coefficients */
|
||||
if (sampleTransmission && sampleReflection) {
|
||||
|
@ -201,7 +163,7 @@ public:
|
|||
bRec.sampledComponent = 1;
|
||||
bRec.sampledType = EDeltaTransmission;
|
||||
|
||||
Float result = refract(intIOR, extIOR, bRec.wi, bRec.wo, bRec.quantity);
|
||||
Float result = refract(m_intIOR, m_extIOR, bRec.wi, bRec.wo, bRec.quantity);
|
||||
if (result == 0)
|
||||
return Spectrum(0.0f);
|
||||
pdf *= std::abs(Frame::cosTheta(bRec.wo));
|
||||
|
@ -219,7 +181,7 @@ public:
|
|||
bRec.sampledType = EDeltaTransmission;
|
||||
pdf = std::abs(Frame::cosTheta(bRec.wo));
|
||||
|
||||
Float result = refract(intIOR, extIOR, bRec.wi, bRec.wo, bRec.quantity);
|
||||
Float result = refract(m_intIOR, m_extIOR, bRec.wi, bRec.wo, bRec.quantity);
|
||||
if (result == 0)
|
||||
return Spectrum(0.0f);
|
||||
|
||||
|
@ -260,11 +222,7 @@ public:
|
|||
bool sampleTransmission = (bRec.typeMask & EDeltaTransmission)
|
||||
&& (bRec.component == -1 || bRec.component == 1);
|
||||
bool reflection = bRec.wo.z * bRec.wi.z > 0;
|
||||
#if SPECTRUM_SAMPLES != 3
|
||||
#error fixme
|
||||
#else
|
||||
Float intIOR = m_intIOR, extIOR = m_extIOR;
|
||||
#endif
|
||||
Float fr = fresnel(Frame::cosTheta(bRec.wi), extIOR, intIOR);
|
||||
if (sampleReflection && !sampleTransmission && !reflection)
|
||||
return Spectrum(0.0f);
|
||||
|
@ -289,9 +247,7 @@ public:
|
|||
std::ostringstream oss;
|
||||
oss << "Dielectric[" << endl
|
||||
<< " intIOR=" << m_intIOR << "," << endl
|
||||
<< " extIOR=" << m_extIOR << "," << endl
|
||||
<< " intB=" << m_intB << "," << endl
|
||||
<< " extB=" << m_extB << endl
|
||||
<< " extIOR=" << m_extIOR << endl
|
||||
<< "]";
|
||||
return oss.str();
|
||||
}
|
||||
|
@ -299,7 +255,6 @@ public:
|
|||
MTS_DECLARE_CLASS()
|
||||
private:
|
||||
Float m_intIOR, m_extIOR;
|
||||
Float m_intB, m_extB;
|
||||
Spectrum m_reflectance;
|
||||
Spectrum m_transmittance;
|
||||
};
|
||||
|
|
|
@ -98,7 +98,7 @@ public:
|
|||
Li += its.LoSub(scene, -ray.d);
|
||||
|
||||
/* Leave here if direct illumination was not requested */
|
||||
if (!(rRec.type & RadianceQueryRecord::EDirectRadiance))
|
||||
if (!(rRec.type & RadianceQueryRecord::EDirectSurfaceRadiance))
|
||||
return Li;
|
||||
|
||||
/* ==================================================================== */
|
||||
|
@ -143,7 +143,7 @@ public:
|
|||
|
||||
/* Weight using the power heuristic */
|
||||
const Float weight = miWeight(lRec.pdf * fracLum, bsdfPdf * fracBSDF) * weightLum;
|
||||
Li += lRec.Le * bsdfVal * weight;
|
||||
Li += lRec.value * bsdfVal * weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -191,13 +191,13 @@ public:
|
|||
|
||||
/* If a luminaire was hit, estimate the local illumination and
|
||||
sample weight using the power heuristic */
|
||||
if (hitLuminaire && (rRec.type & RadianceQueryRecord::EDirectRadiance)) {
|
||||
lRec.Le = lRec.luminaire->Le(lRec);
|
||||
if (hitLuminaire && (rRec.type & RadianceQueryRecord::EDirectSurfaceRadiance)) {
|
||||
lRec.value = lRec.luminaire->Le(lRec);
|
||||
Float lumPdf = scene->pdfLuminaire(its, lRec);
|
||||
if (bRec.sampledType & BSDF::EDelta)
|
||||
lumPdf = 0;
|
||||
const Float weight = miWeight(bsdfPdf * fracBSDF, lumPdf * fracLum) * weightBSDF;
|
||||
Li += lRec.Le * bsdfVal * weight;
|
||||
Li += lRec.value * bsdfVal * weight;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -223,14 +223,14 @@ public:
|
|||
|
||||
Spectrum Li(const RayDifferential &ray, RadianceQueryRecord &rRec) const {
|
||||
Intersection &its = rRec.its;
|
||||
if (!m_direct && (rRec.type & RadianceQueryRecord::EDirectRadiance))
|
||||
rRec.type ^= RadianceQueryRecord::EDirectRadiance;
|
||||
if (!m_direct && (rRec.type & RadianceQueryRecord::EDirectSurfaceRadiance))
|
||||
rRec.type ^= RadianceQueryRecord::EDirectSurfaceRadiance;
|
||||
|
||||
if (rRec.rayIntersect(ray)) {
|
||||
const BSDF *bsdf = its.getBSDF(ray);
|
||||
|
||||
if (bsdf->getType() == BSDF::EDiffuseReflection &&
|
||||
(rRec.type & RadianceQueryRecord::EIndirectRadiance)) {
|
||||
(rRec.type & RadianceQueryRecord::EIndirectSurfaceRadiance)) {
|
||||
Spectrum E;
|
||||
if (!m_irrCache->get(its, E)) {
|
||||
handleMiss(ray, rRec, E);
|
||||
|
@ -239,7 +239,7 @@ public:
|
|||
E.fromLinearRGB(1e3, 0, 0);
|
||||
}
|
||||
|
||||
rRec.type ^= RadianceQueryRecord::EIndirectRadiance;
|
||||
rRec.type ^= RadianceQueryRecord::EIndirectSurfaceRadiance;
|
||||
|
||||
return E * bsdf->getDiffuseReflectance(its) * INV_PI +
|
||||
m_subIntegrator->Li(ray, rRec);
|
||||
|
@ -298,10 +298,10 @@ public:
|
|||
for (int i=0; i<irrSamples; i++) {
|
||||
rRec.newQuery(RadianceQueryRecord::ERadianceNoEmission);
|
||||
|
||||
if (scene->sampleLuminaireAttenuated(p, lRec, time, rRec.nextSample2D())) {
|
||||
if (scene->sampleAttenuatedLuminaire(p, lRec, time, rRec.nextSample2D())) {
|
||||
Float dp = dot(lRec.d, n);
|
||||
if (dp < 0)
|
||||
EDir -= lRec.Le * dp;
|
||||
EDir -= lRec.value * dp;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -83,7 +83,7 @@ public:
|
|||
/* ==================================================================== */
|
||||
|
||||
/* Estimate the direct illumination if this is requested */
|
||||
if (rRec.type & RadianceQueryRecord::EDirectRadiance &&
|
||||
if (rRec.type & RadianceQueryRecord::EDirectSurfaceRadiance &&
|
||||
scene->sampleLuminaire(its, lRec, rRec.nextSample2D())) {
|
||||
/* Allocate a record for querying the BSDF */
|
||||
const BSDFQueryRecord bRec(rRec, its, its.toLocal(-lRec.d));
|
||||
|
@ -100,7 +100,7 @@ public:
|
|||
|
||||
/* Weight using the power heuristic */
|
||||
const Float weight = miWeight(lRec.pdf, bsdfPdf);
|
||||
Li += pathThroughput * lRec.Le * bsdfVal * weight;
|
||||
Li += pathThroughput * lRec.value * bsdfVal * weight;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -142,14 +142,14 @@ public:
|
|||
/* If a luminaire was hit, estimate the local illumination and
|
||||
weight using the power heuristic */
|
||||
if (hitLuminaire &&
|
||||
(rRec.type & RadianceQueryRecord::EDirectRadiance)) {
|
||||
lRec.Le = lRec.luminaire->Le(lRec);
|
||||
(rRec.type & RadianceQueryRecord::EDirectSurfaceRadiance)) {
|
||||
lRec.value = lRec.luminaire->Le(lRec);
|
||||
|
||||
/* Prob. of having generated this sample using luminaire sampling */
|
||||
const Float lumPdf = (!(bRec.sampledType & BSDF::EDelta)) ?
|
||||
scene->pdfLuminaire(prevIts, lRec) : 0;
|
||||
const Float weight = miWeight(bsdfPdf, lumPdf);
|
||||
Li += pathThroughput * lRec.Le * bsdfVal * weight;
|
||||
Li += pathThroughput * lRec.value * bsdfVal * weight;
|
||||
}
|
||||
|
||||
/* ==================================================================== */
|
||||
|
@ -159,7 +159,7 @@ public:
|
|||
/* Set the recursive query type */
|
||||
/* Stop if no surface was hit by the BSDF sample or if indirect illumination
|
||||
was not requested */
|
||||
if (!its.isValid() || !(rRec.type & RadianceQueryRecord::EIndirectRadiance))
|
||||
if (!its.isValid() || !(rRec.type & RadianceQueryRecord::EIndirectSurfaceRadiance))
|
||||
break;
|
||||
rRec.type = RadianceQueryRecord::ERadianceNoEmission;
|
||||
|
||||
|
|
|
@ -36,16 +36,11 @@ static StatsCounter avgPathLength("Volumetric path tracer", "Average path length
|
|||
*/
|
||||
class VolumetricPathTracer : public MonteCarloIntegrator {
|
||||
public:
|
||||
VolumetricPathTracer(const Properties &props) : MonteCarloIntegrator(props) {
|
||||
/* Beta factor for the power heuristic */
|
||||
m_beta = props.getFloat("beta", 2.0f);
|
||||
}
|
||||
VolumetricPathTracer(const Properties &props) : MonteCarloIntegrator(props) { }
|
||||
|
||||
/// Unserialize from a binary data stream
|
||||
VolumetricPathTracer(Stream *stream, InstanceManager *manager)
|
||||
: MonteCarloIntegrator(stream, manager) {
|
||||
m_beta = stream->readFloat();
|
||||
}
|
||||
: MonteCarloIntegrator(stream, manager) { }
|
||||
|
||||
Spectrum Li(const RayDifferential &r, RadianceQueryRecord &rRec) const {
|
||||
/* Some aliases and local variables */
|
||||
|
@ -65,8 +60,8 @@ public:
|
|||
/* ==================================================================== */
|
||||
/* Radiative Transfer Equation sampling */
|
||||
/* ==================================================================== */
|
||||
if (scene->sampleDistance(Ray(ray, 0, its.t), mRec, rRec.sampler)) {
|
||||
const PhaseFunction *phase = mRec.medium->getPhaseFunction();
|
||||
if (rRec.medium && rRec.medium->sampleDistance(Ray(ray, 0, its.t), mRec, rRec.sampler)) {
|
||||
const PhaseFunction *phase = rRec.medium->getPhaseFunction();
|
||||
|
||||
if (rRec.depth == m_maxDepth && m_maxDepth > 0) // No more scattering events allowed
|
||||
break;
|
||||
|
@ -81,8 +76,8 @@ public:
|
|||
/* ==================================================================== */
|
||||
|
||||
/* Estimate the single scattering component if this is requested */
|
||||
if (rRec.type & RadianceQueryRecord::EInscatteredDirectRadiance &&
|
||||
scene->sampleLuminaireAttenuated(mRec.p, lRec, ray.time, rRec.nextSample2D())) {
|
||||
if (rRec.type & RadianceQueryRecord::EDirectMediumRadiance &&
|
||||
scene->sampleAttenuatedLuminaire(mRec.p, lRec, ray.time, rRec.nextSample2D())) {
|
||||
/* Evaluate the phase function */
|
||||
Spectrum phaseVal = phase->f(PhaseFunctionQueryRecord(mRec, -ray.d, -lRec.d));
|
||||
|
||||
|
@ -95,7 +90,7 @@ public:
|
|||
|
||||
/* Weight using the power heuristic */
|
||||
const Float weight = miWeight(lRec.pdf, phasePdf);
|
||||
Li += pathThroughput * lRec.Le * phaseVal * weight;
|
||||
Li += pathThroughput * lRec.value * phaseVal * weight;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -128,18 +123,23 @@ public:
|
|||
hitLuminaire = true;
|
||||
}
|
||||
}
|
||||
ray.mint = 0; ray.maxt = its.t;
|
||||
Spectrum attenuation = scene->getAttenuation(ray);
|
||||
|
||||
/* If a luminaire was hit, estimate the local illumination and
|
||||
weight using the power heuristic */
|
||||
if (hitLuminaire && (rRec.type & RadianceQueryRecord::EInscatteredDirectRadiance)) {
|
||||
lRec.Le = lRec.luminaire->Le(lRec);
|
||||
if (hitLuminaire && (rRec.type & RadianceQueryRecord::EDirectMediumRadiance)) {
|
||||
lRec.value = lRec.luminaire->Le(lRec);
|
||||
|
||||
/* Prob. of having generated this sample using luminaire sampling */
|
||||
const Float lumPdf = scene->pdfLuminaire(mRec.p, lRec);
|
||||
Float weight = miWeight(phasePdf, lumPdf);
|
||||
Li += pathThroughput * attenuation * lRec.Le * phaseVal * weight;
|
||||
Spectrum contrib = pathThroughput * lRec.value * phaseVal * weight;
|
||||
|
||||
if (rRec.medium) {
|
||||
ray.mint = 0; ray.maxt = its.t;
|
||||
contrib *= rRec.medium->tau(ray);
|
||||
}
|
||||
|
||||
contrib += Li;
|
||||
}
|
||||
|
||||
/* ==================================================================== */
|
||||
|
@ -147,7 +147,7 @@ public:
|
|||
/* ==================================================================== */
|
||||
|
||||
/* Stop if multiple scattering was not requested */
|
||||
if (!(rRec.type & RadianceQueryRecord::EInscatteredIndirectRadiance))
|
||||
if (!(rRec.type & RadianceQueryRecord::EIndirectMediumRadiance))
|
||||
break;
|
||||
rRec.type = RadianceQueryRecord::ERadianceNoEmission;
|
||||
|
||||
|
@ -166,7 +166,8 @@ public:
|
|||
tau(x, y) (Surface integral). This happens with probability mRec.pdfFailure
|
||||
Divide this out and multiply with the proper per-color-channel attenuation.
|
||||
*/
|
||||
pathThroughput *= mRec.attenuation / mRec.pdfFailure;
|
||||
if (rRec.medium)
|
||||
pathThroughput *= mRec.attenuation / mRec.pdfFailure;
|
||||
|
||||
if (!its.isValid()) {
|
||||
/* If no intersection could be found, possibly return
|
||||
|
@ -176,7 +177,16 @@ public:
|
|||
break;
|
||||
}
|
||||
|
||||
if (its.isMediumTransition())
|
||||
rRec.medium = its.getTargetMedium(ray);
|
||||
|
||||
const BSDF *bsdf = its.getBSDF(ray);
|
||||
if (!bsdf) {
|
||||
/* Pass right through the surface (there is no BSDF) */
|
||||
ray.setOrigin(its.p);
|
||||
scene->rayIntersect(ray, its);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Possibly include emitted radiance if requested */
|
||||
if (its.isLuminaire() && (rRec.type & RadianceQueryRecord::EEmittedRadiance))
|
||||
|
@ -194,8 +204,8 @@ public:
|
|||
/* ==================================================================== */
|
||||
|
||||
/* Estimate the direct illumination if this is requested */
|
||||
if (rRec.type & RadianceQueryRecord::EDirectRadiance &&
|
||||
scene->sampleLuminaireAttenuated(its, lRec, rRec.nextSample2D())) {
|
||||
if (rRec.type & RadianceQueryRecord::EDirectSurfaceRadiance &&
|
||||
scene->sampleAttenuatedLuminaire(its, lRec, rRec.medium, rRec.nextSample2D())) {
|
||||
/* Allocate a record for querying the BSDF */
|
||||
const BSDFQueryRecord bRec(rRec, its, its.toLocal(-lRec.d));
|
||||
|
||||
|
@ -211,7 +221,7 @@ public:
|
|||
|
||||
/* Weight using the power heuristic */
|
||||
const Float weight = miWeight(lRec.pdf, bsdfPdf);
|
||||
Li += pathThroughput * lRec.Le * bsdfVal * weight;
|
||||
Li += pathThroughput * lRec.value * bsdfVal * weight;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -246,18 +256,21 @@ public:
|
|||
hitLuminaire = true;
|
||||
}
|
||||
}
|
||||
ray.mint = 0; ray.maxt = its.t;
|
||||
Spectrum attenuation = scene->getAttenuation(ray);
|
||||
|
||||
/* If a luminaire was hit, estimate the local illumination and
|
||||
weight using the power heuristic */
|
||||
if (hitLuminaire && rRec.type & RadianceQueryRecord::EDirectRadiance) {
|
||||
lRec.Le = lRec.luminaire->Le(lRec);
|
||||
if (hitLuminaire && rRec.type & RadianceQueryRecord::EDirectSurfaceRadiance) {
|
||||
lRec.value = lRec.luminaire->Le(lRec);
|
||||
/* Prob. of having generated this sample using luminaire sampling */
|
||||
const Float lumPdf = (!(bRec.sampledType & BSDF::EDelta)) ?
|
||||
scene->pdfLuminaire(prevIts, lRec) : 0;
|
||||
const Float weight = miWeight(bsdfPdf, lumPdf);
|
||||
Li += pathThroughput * attenuation * lRec.Le * bsdfVal * weight;
|
||||
Spectrum contrib = pathThroughput * lRec.value * bsdfVal * weight;
|
||||
if (rRec.medium) {
|
||||
ray.mint = 0; ray.maxt = its.t;
|
||||
contrib *= rRec.medium->getAttenuation(ray);
|
||||
}
|
||||
Li += contrib;
|
||||
}
|
||||
|
||||
/* ==================================================================== */
|
||||
|
@ -265,7 +278,7 @@ public:
|
|||
/* ==================================================================== */
|
||||
|
||||
/* Stop if indirect illumination was not requested */
|
||||
if (!(rRec.type & RadianceQueryRecord::EIndirectRadiance))
|
||||
if (!(rRec.type & RadianceQueryRecord::EIndirectSurfaceRadiance))
|
||||
break;
|
||||
rRec.type = RadianceQueryRecord::ERadianceNoEmission;
|
||||
|
||||
|
@ -292,20 +305,18 @@ public:
|
|||
}
|
||||
|
||||
inline Float miWeight(Float pdfA, Float pdfB) const {
|
||||
pdfA = std::pow(pdfA, m_beta);
|
||||
pdfB = std::pow(pdfB, m_beta);
|
||||
return pdfA / (pdfA + pdfB);
|
||||
Float a = pdfA * pdfA;
|
||||
Float b = pdfB * pdfB;
|
||||
return a / (a + b);
|
||||
}
|
||||
|
||||
void serialize(Stream *stream, InstanceManager *manager) const {
|
||||
MonteCarloIntegrator::serialize(stream, manager);
|
||||
stream->writeFloat(m_beta);
|
||||
}
|
||||
|
||||
std::string toString() const {
|
||||
std::ostringstream oss;
|
||||
oss << "VolumetricPathTracer[" << std::endl
|
||||
<< " beta = " << m_beta << "," << std::endl
|
||||
<< " maxDepth = " << m_maxDepth << "," << std::endl
|
||||
<< " rrDepth = " << m_rrDepth << std::endl
|
||||
<< "]";
|
||||
|
|
|
@ -77,9 +77,9 @@ public:
|
|||
/* ==================================================================== */
|
||||
|
||||
/* Estimate the single scattering component if this is requested */
|
||||
if (rRec.type & RadianceQueryRecord::EInscatteredDirectRadiance &&
|
||||
scene->sampleLuminaireAttenuated(mRec.p, lRec, ray.time, rRec.nextSample2D())) {
|
||||
Li += pathThroughput * lRec.Le * phase->f(PhaseFunctionQueryRecord(mRec, -ray.d, -lRec.d));
|
||||
if (rRec.type & RadianceQueryRecord::EDirectMediumRadiance &&
|
||||
scene->sampleAttenuatedLuminaire(mRec.p, lRec, ray.time, rRec.nextSample2D())) {
|
||||
Li += pathThroughput * lRec.value * phase->f(PhaseFunctionQueryRecord(mRec, -ray.d, -lRec.d));
|
||||
}
|
||||
|
||||
/* ==================================================================== */
|
||||
|
@ -100,7 +100,7 @@ public:
|
|||
/* ==================================================================== */
|
||||
|
||||
/* Set the recursive query type */
|
||||
if (!(rRec.type & RadianceQueryRecord::EInscatteredIndirectRadiance))
|
||||
if (!(rRec.type & RadianceQueryRecord::EIndirectMediumRadiance))
|
||||
break; /* Stop if multiple scattering was not requested */
|
||||
|
||||
/* Russian roulette - Possibly stop the recursion */
|
||||
|
@ -144,12 +144,12 @@ public:
|
|||
/* ==================================================================== */
|
||||
|
||||
/* Estimate the direct illumination if this is requested */
|
||||
if (rRec.type & RadianceQueryRecord::EDirectRadiance &&
|
||||
scene->sampleLuminaireAttenuated(its, lRec, rRec.nextSample2D())) {
|
||||
if (rRec.type & RadianceQueryRecord::EDirectSurfaceRadiance &&
|
||||
scene->sampleAttenuatedLuminaire(its, lRec, rRec.nextSample2D())) {
|
||||
/* Allocate a record for querying the BSDF */
|
||||
const BSDFQueryRecord bRec(rRec, its, its.toLocal(-lRec.d));
|
||||
|
||||
Li += pathThroughput * lRec.Le * bsdf->fCos(bRec);
|
||||
Li += pathThroughput * lRec.value * bsdf->fCos(bRec);
|
||||
}
|
||||
|
||||
/* ==================================================================== */
|
||||
|
@ -170,10 +170,10 @@ public:
|
|||
/* Indirect illumination */
|
||||
/* ==================================================================== */
|
||||
bool includeEmitted = (bRec.sampledType & BSDF::EDelta)
|
||||
&& (rRec.type & RadianceQueryRecord::EDirectRadiance);
|
||||
&& (rRec.type & RadianceQueryRecord::EDirectSurfaceRadiance);
|
||||
|
||||
/* Stop if indirect illumination was not requested */
|
||||
if (!(rRec.type & RadianceQueryRecord::EIndirectRadiance)) {
|
||||
if (!(rRec.type & RadianceQueryRecord::EIndirectSurfaceRadiance)) {
|
||||
/* Stop if indirect illumination was not requested (except: sampled a delta BSDF
|
||||
- recursively look for emitted radiance only to get the direct component) */
|
||||
if (includeEmitted) {
|
||||
|
|
|
@ -304,7 +304,7 @@ public:
|
|||
}
|
||||
|
||||
/* Estimate the direct illumination if this is requested */
|
||||
if (rRec.type & RadianceQueryRecord::EDirectRadiance) {
|
||||
if (rRec.type & RadianceQueryRecord::EDirectSurfaceRadiance) {
|
||||
Float weight = 1 / (Float) numDirectSamples;
|
||||
|
||||
for (int i=0; i<numDirectSamples; ++i) {
|
||||
|
@ -315,7 +315,7 @@ public:
|
|||
/* Evaluate BSDF * cos(theta) */
|
||||
const Spectrum bsdfVal = bsdf->fCos(bRec);
|
||||
|
||||
Li += lRec.Le * bsdfVal * weight;
|
||||
Li += lRec.value * bsdfVal * weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -327,7 +327,7 @@ public:
|
|||
|
||||
if (bsdfType == BSDF::EDiffuseReflection) {
|
||||
/* Hit a diffuse material - do a direct photon map visualization. */
|
||||
if (rRec.type & RadianceQueryRecord::EIndirectRadiance)
|
||||
if (rRec.type & RadianceQueryRecord::EIndirectSurfaceRadiance)
|
||||
Li += m_globalPhotonMap->estimateIrradianceFiltered(its.p,
|
||||
its.shFrame.n, m_globalLookupRadius, m_globalLookupSize)
|
||||
* bsdf->getDiffuseReflectance(its) * INV_PI;
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
*/
|
||||
|
||||
#include <mitsuba/render/camera.h>
|
||||
#include <mitsuba/render/medium.h>
|
||||
#include <mitsuba/core/plugin.h>
|
||||
|
||||
MTS_NAMESPACE_BEGIN
|
||||
|
@ -38,6 +39,7 @@ Camera::Camera(Stream *stream, InstanceManager *manager)
|
|||
: ConfigurableObject(stream, manager) {
|
||||
m_film = static_cast<Film *>(manager->getInstance(stream));
|
||||
m_sampler = static_cast<Sampler *>(manager->getInstance(stream));
|
||||
m_medium = static_cast<Medium *>(manager->getInstance(stream));
|
||||
m_worldToCamera = Transform(stream);
|
||||
m_cameraToWorld = Transform(stream);
|
||||
m_shutterOpen = stream->readFloat();
|
||||
|
@ -67,6 +69,7 @@ void Camera::serialize(Stream *stream, InstanceManager *manager) const {
|
|||
ConfigurableObject::serialize(stream, manager);
|
||||
manager->serialize(stream, m_film.get());
|
||||
manager->serialize(stream, m_sampler.get());
|
||||
manager->serialize(stream, m_medium.get());
|
||||
m_worldToCamera.serialize(stream);
|
||||
m_cameraToWorld.serialize(stream);
|
||||
stream->writeFloat(m_shutterOpen);
|
||||
|
@ -91,6 +94,12 @@ void Camera::addChild(const std::string &name, ConfigurableObject *child) {
|
|||
} else if (child->getClass()->derivesFrom(Film::m_theClass)) {
|
||||
Assert(m_film == NULL);
|
||||
m_film = static_cast<Film *>(child);
|
||||
} else if (child->getClass()->derivesFrom(Medium::m_theClass)) {
|
||||
Assert(m_medium == NULL);
|
||||
m_medium = static_cast<Medium *>(child);
|
||||
} else if (child->getClass()->derivesFrom(Medium::m_theClass)) {
|
||||
Assert(m_medium == NULL);
|
||||
m_medium = static_cast<Medium *>(child);
|
||||
} else {
|
||||
Log(EError, "Camera: Invalid child node!");
|
||||
}
|
||||
|
@ -139,7 +148,7 @@ PinholeCamera::PinholeCamera(const Properties &props)
|
|||
*/
|
||||
m_mapSmallerSide = props.getBoolean("mapSmallerSide", true);
|
||||
}
|
||||
|
||||
|
||||
PinholeCamera::PinholeCamera(Stream *stream, InstanceManager *manager)
|
||||
: ProjectiveCamera(stream, manager) {
|
||||
m_fov = stream->readFloat();
|
||||
|
@ -167,6 +176,7 @@ void PinholeCamera::configure() {
|
|||
m_xfov = m_fov;
|
||||
m_yfov = radToDeg(2 * std::atan(std::tan(degToRad(m_fov)/2) / m_aspect));
|
||||
}
|
||||
|
||||
m_imagePlaneSize.x = 2 * std::tan(degToRad(m_xfov)/2);
|
||||
m_imagePlaneSize.y = 2 * std::tan(degToRad(m_yfov)/2);
|
||||
m_imagePlanePixelSize.x = m_imagePlaneSize.x / getFilm()->getSize().x;
|
||||
|
@ -181,22 +191,6 @@ Float PinholeCamera::importance(const Point2 &p) const {
|
|||
return std::pow(1 + x*x+y*y, (Float) (3.0/2.0)) * m_imagePlaneInvArea;
|
||||
}
|
||||
|
||||
Float PinholeCamera::solidAngle(Float xs, Float xe, Float ys, Float ye) const {
|
||||
xs = (xs * m_imagePlanePixelSize.x) - .5f * m_imagePlaneSize.x;
|
||||
ys = (ys * m_imagePlanePixelSize.y) - .5f * m_imagePlaneSize.y;
|
||||
xe = (xe * m_imagePlanePixelSize.x) - .5f * m_imagePlaneSize.x;
|
||||
ye = (ye * m_imagePlanePixelSize.y) - .5f * m_imagePlaneSize.y;
|
||||
|
||||
/* cos(theta) / d^2 with respect to the image plane integrated
|
||||
over the specified footprint */
|
||||
Float xs2 = xs*xs, xe2 = xe*xe, ys2 = ys*ys, ye2 = ye*ye;
|
||||
Float atan1 = std::atan((xe*ye)/std::sqrt(1 + xe2 + ye2));
|
||||
Float atan2 = std::atan((xs*ye)/std::sqrt(1 + xs2 + ye2));
|
||||
Float atan3 = std::atan((xe*ys)/std::sqrt(1 + xe2 + ys2));
|
||||
Float atan4 = std::atan((xs*ys)/std::sqrt(1 + xs2 + ys2));
|
||||
return atan1 - atan2 - atan3 + atan4;
|
||||
}
|
||||
|
||||
Float PinholeCamera::importance(const Vector &v) const {
|
||||
Vector localV;
|
||||
m_worldToCamera(v, localV);
|
||||
|
|
|
@ -51,7 +51,7 @@ void SampleIntegrator::serialize(Stream *stream, InstanceManager *manager) const
|
|||
}
|
||||
|
||||
Spectrum SampleIntegrator::E(const Scene *scene, const Point &p, const Normal &n, Float time,
|
||||
Sampler *sampler, int irrSamples, bool irrIndirect) const {
|
||||
const Medium *medium, Sampler *sampler, int irrSamples, bool irrIndirect) const {
|
||||
Spectrum E(0.0f);
|
||||
LuminaireSamplingRecord lRec;
|
||||
RadianceQueryRecord rRec(scene, sampler);
|
||||
|
@ -59,13 +59,13 @@ Spectrum SampleIntegrator::E(const Scene *scene, const Point &p, const Normal &n
|
|||
|
||||
sampler->generate();
|
||||
for (int i=0; i<irrSamples; i++) {
|
||||
rRec.newQuery(RadianceQueryRecord::ERadianceNoEmission);
|
||||
rRec.newQuery(RadianceQueryRecord::ERadianceNoEmission, medium);
|
||||
|
||||
/* Direct */
|
||||
if (scene->sampleLuminaireAttenuated(p, lRec, time, rRec.nextSample2D())) {
|
||||
if (scene->sampleAttenuatedLuminaire(p, time, medium, lRec, rRec.nextSample2D())) {
|
||||
Float dp = dot(lRec.d, n);
|
||||
if (dp < 0)
|
||||
E -= lRec.Le * dp;
|
||||
E -= lRec.value * dp;
|
||||
}
|
||||
|
||||
/* Indirect */
|
||||
|
@ -148,13 +148,12 @@ void SampleIntegrator::renderBlock(const Scene *scene,
|
|||
/* Use a prescribed traversal order (e.g. using a space-filling curve) */
|
||||
if (!block->collectStatistics()) {
|
||||
for (size_t i=0; i<points->size(); ++i) {
|
||||
Point2i offset = points->operator[](i)
|
||||
+ Vector2i(block->getOffset());
|
||||
Point2i offset = (*points)[i] + Vector2i(block->getOffset());
|
||||
if (stop)
|
||||
break;
|
||||
sampler->generate();
|
||||
for (uint64_t j = 0; j<sampler->getSampleCount(); j++) {
|
||||
rRec.newQuery(RadianceQueryRecord::ECameraRay);
|
||||
rRec.newQuery(RadianceQueryRecord::ECameraRay, camera->getMedium());
|
||||
if (needsLensSample)
|
||||
lensSample = rRec.nextSample2D();
|
||||
if (needsTimeSample)
|
||||
|
@ -172,14 +171,13 @@ void SampleIntegrator::renderBlock(const Scene *scene,
|
|||
} else {
|
||||
Spectrum mean, meanSqr;
|
||||
for (size_t i=0; i<points->size(); ++i) {
|
||||
Point2i offset = points->operator[](i)
|
||||
+ Vector2i(block->getOffset());
|
||||
Point2i offset = (*points)[i] + Vector2i(block->getOffset());
|
||||
if (stop)
|
||||
break;
|
||||
sampler->generate();
|
||||
mean = meanSqr = Spectrum(0.0f);
|
||||
for (uint64_t j = 0; j<sampler->getSampleCount(); j++) {
|
||||
rRec.newQuery(RadianceQueryRecord::ECameraRay);
|
||||
rRec.newQuery(RadianceQueryRecord::ECameraRay, camera->getMedium());
|
||||
if (needsLensSample)
|
||||
lensSample = rRec.nextSample2D();
|
||||
if (needsTimeSample)
|
||||
|
@ -217,7 +215,7 @@ void SampleIntegrator::renderBlock(const Scene *scene,
|
|||
break;
|
||||
sampler->generate();
|
||||
for (uint64_t j = 0; j<sampler->getSampleCount(); j++) {
|
||||
rRec.newQuery(RadianceQueryRecord::ECameraRay);
|
||||
rRec.newQuery(RadianceQueryRecord::ECameraRay, camera->getMedium());
|
||||
if (needsLensSample)
|
||||
lensSample = rRec.nextSample2D();
|
||||
if (needsTimeSample)
|
||||
|
@ -242,7 +240,7 @@ void SampleIntegrator::renderBlock(const Scene *scene,
|
|||
sampler->generate();
|
||||
mean = meanSqr = Spectrum(0.0f);
|
||||
for (uint64_t j = 0; j<sampler->getSampleCount(); j++) {
|
||||
rRec.newQuery(RadianceQueryRecord::ECameraRay);
|
||||
rRec.newQuery(RadianceQueryRecord::ECameraRay, camera->getMedium());
|
||||
if (needsLensSample)
|
||||
lensSample = rRec.nextSample2D();
|
||||
if (needsTimeSample)
|
||||
|
@ -298,11 +296,11 @@ std::string RadianceQueryRecord::toString() const {
|
|||
<< " type = { ";
|
||||
if (type & EEmittedRadiance) oss << "emitted ";
|
||||
if (type & ESubsurfaceRadiance) oss << "subsurface ";
|
||||
if (type & EDirectRadiance) oss << "direct ";
|
||||
if (type & EIndirectRadiance) oss << "indirect ";
|
||||
if (type & EDirectSurfaceRadiance) oss << "direct ";
|
||||
if (type & EIndirectSurfaceRadiance) oss << "indirect ";
|
||||
if (type & ECausticRadiance) oss << "caustic ";
|
||||
if (type & EInscatteredDirectRadiance) oss << "inscatteredDirect ";
|
||||
if (type & EInscatteredIndirectRadiance) oss << "inscatteredIndirect ";
|
||||
if (type & EDirectMediumRadiance) oss << "inscatteredDirect ";
|
||||
if (type & EIndirectMediumRadiance) oss << "inscatteredIndirect ";
|
||||
if (type & EDistance) oss << "distance ";
|
||||
if (type & EOpacity) oss << "opacity ";
|
||||
if (type & EIntersection) oss << "intersection ";
|
||||
|
|
|
@ -74,15 +74,21 @@ bool Luminaire::isBackgroundLuminaire() const {
|
|||
}
|
||||
|
||||
Spectrum Luminaire::Le(const Ray &ray) const {
|
||||
Log(EError, "Luminaire::Le(const Ray &) is not implemented!");
|
||||
return Spectrum(0.0f);
|
||||
}
|
||||
|
||||
Spectrum Luminaire::Le(const Intersection &its, const Vector &d) const {
|
||||
Log(EError, "Luminaire::Le(const Intersection &, const Vector &) is not implemented!");
|
||||
return Spectrum(0.0f);
|
||||
}
|
||||
|
||||
std::string EmissionRecord::toString() const {
|
||||
std::ostringstream oss;
|
||||
oss << "EmissionRecord[" << std::endl
|
||||
<< " sRec = " << indent(sRec.toString()) << "," << std::endl
|
||||
<< " d = " << d.toString() << "," << std::endl
|
||||
<< " P = " << P.toString() << "," << std::endl
|
||||
<< " value = " << value.toString() << "," << std::endl
|
||||
<< " pdfArea = " << pdfArea << "," << std::endl
|
||||
<< " pdfDir = " << pdfDir << "," << std::endl
|
||||
<< " luminaire = " << ((luminaire == NULL ) ? "null" : indent(((Object *) luminaire)->toString()).c_str()) << std::endl
|
||||
|
@ -96,7 +102,7 @@ std::string LuminaireSamplingRecord::toString() const {
|
|||
<< " sRec = " << indent(sRec.toString()) << "," << std::endl
|
||||
<< " d = " << d.toString() << "," << std::endl
|
||||
<< " pdf = " << pdf << "," << std::endl
|
||||
<< " Le = " << Le.toString() << "," << std::endl
|
||||
<< " value = " << value.toString() << "," << std::endl
|
||||
<< " luminaire = " << ((luminaire == NULL ) ? "null" : indent(((Object *) luminaire)->toString()).c_str()) << std::endl
|
||||
<< "]";
|
||||
return oss.str();
|
||||
|
|
|
@ -53,10 +53,6 @@ Medium::Medium(Stream *stream, InstanceManager *manager)
|
|||
m_phaseFunction = static_cast<PhaseFunction *>(manager->getInstance(stream));
|
||||
}
|
||||
|
||||
void Medium::preprocess(const Scene *scene, RenderQueue *queue, const RenderJob *job,
|
||||
int sceneResID, int cameraResID, int samplerResID) {
|
||||
}
|
||||
|
||||
void Medium::addChild(const std::string &name, ConfigurableObject *child) {
|
||||
const Class *cClass = child->getClass();
|
||||
|
||||
|
@ -95,8 +91,7 @@ std::string MediumSamplingRecord::toString() const {
|
|||
<< " pdfFailure = " << pdfFailure << "," << std::endl
|
||||
<< " pdfSuccess = " << pdfSuccess << "," << std::endl
|
||||
<< " pdfSuccessRev = " << pdfSuccessRev << "," << std::endl
|
||||
<< " attenuation = " << attenuation.toString() << "," << std::endl
|
||||
<< " medium = " << indent(((Object *) medium)->toString()) << std::endl
|
||||
<< " attenuation = " << attenuation.toString()
|
||||
<< "]";
|
||||
return oss.str();
|
||||
}
|
||||
|
|
|
@ -70,13 +70,11 @@ ParticleTracer::ParticleTracer(Stream *stream, InstanceManager *manager)
|
|||
: WorkProcessor(stream, manager) {
|
||||
|
||||
m_maxDepth = stream->readInt();
|
||||
m_multipleScattering = stream->readBool();
|
||||
m_rrDepth = stream->readInt();
|
||||
}
|
||||
|
||||
void ParticleTracer::serialize(Stream *stream, InstanceManager *manager) const {
|
||||
stream->writeInt(m_maxDepth);
|
||||
stream->writeBool(m_multipleScattering);
|
||||
stream->writeInt(m_rrDepth);
|
||||
}
|
||||
|
||||
|
@ -112,9 +110,10 @@ void ParticleTracer::process(const WorkUnit *workUnit, WorkResult *workResult,
|
|||
|
||||
/* Sample an emitted particle */
|
||||
m_scene->sampleEmission(eRec, areaSample, dirSample);
|
||||
const Medium *medium = eRec.luminaire->getMedium();
|
||||
|
||||
ray = Ray(eRec.sRec.p, eRec.d, shutterOpen + shutterOpenTime * m_sampler->next1D());
|
||||
weight = eRec.P;
|
||||
weight = eRec.value;
|
||||
depth = 1;
|
||||
caustic = true;
|
||||
|
||||
|
@ -124,7 +123,7 @@ void ParticleTracer::process(const WorkUnit *workUnit, WorkResult *workResult,
|
|||
/* ==================================================================== */
|
||||
/* Radiative Transfer Equation sampling */
|
||||
/* ==================================================================== */
|
||||
if (m_scene->sampleDistance(Ray(ray, 0, its.t), mRec, m_sampler)) {
|
||||
if (medium && medium->sampleDistance(Ray(ray, 0, its.t), mRec, m_sampler)) {
|
||||
/* Sample the integral
|
||||
\int_x^y tau(x, x') [ \sigma_s \int_{S^2} \rho(\omega,\omega') L(x,\omega') d\omega' ] dx'
|
||||
*/
|
||||
|
@ -132,13 +131,10 @@ void ParticleTracer::process(const WorkUnit *workUnit, WorkResult *workResult,
|
|||
weight *= mRec.sigmaS * mRec.attenuation / mRec.pdfSuccess;
|
||||
handleMediumInteraction(depth, caustic, mRec, ray.time, -ray.d, weight);
|
||||
|
||||
if (!m_multipleScattering)
|
||||
break;
|
||||
|
||||
PhaseFunctionQueryRecord pRec(mRec, -ray.d);
|
||||
pRec.quantity = EImportance;
|
||||
|
||||
weight *= mRec.medium->getPhaseFunction()->sample(pRec, m_sampler);
|
||||
weight *= medium->getPhaseFunction()->sample(pRec, m_sampler);
|
||||
caustic = false;
|
||||
|
||||
/* Russian roulette */
|
||||
|
@ -154,16 +150,25 @@ void ParticleTracer::process(const WorkUnit *workUnit, WorkResult *workResult,
|
|||
/* There is no surface in this direction */
|
||||
break;
|
||||
} else {
|
||||
ray.mint = 0; ray.maxt = its.t;
|
||||
|
||||
/* Sample
|
||||
tau(x, y) * (Surface integral). This happens with probability mRec.pdfFailure
|
||||
Divide this out and multiply with the proper per color channel attenuation.
|
||||
*/
|
||||
weight *= m_scene->getAttenuation(ray) / mRec.pdfFailure;
|
||||
handleSurfaceInteraction(depth, caustic, its, weight);
|
||||
if (medium)
|
||||
weight *= mRec.attenuation / mRec.pdfFailure;
|
||||
|
||||
if (its.isMediumTransition())
|
||||
medium = its.getTargetMedium(ray);
|
||||
|
||||
const BSDF *bsdf = its.shape->getBSDF();
|
||||
|
||||
if (!bsdf) {
|
||||
/* Pass right through the surface (there is no BSDF) */
|
||||
ray.setOrigin(its.p);
|
||||
continue;
|
||||
}
|
||||
|
||||
handleSurfaceInteraction(depth, caustic, its, weight);
|
||||
BSDFQueryRecord bRec(its);
|
||||
bRec.quantity = EImportance;
|
||||
bsdfVal = bsdf->sampleCos(bRec, m_sampler->next2D());
|
||||
|
@ -184,7 +189,8 @@ void ParticleTracer::process(const WorkUnit *workUnit, WorkResult *workResult,
|
|||
|
||||
weight *= bsdfVal;
|
||||
Vector wi = -ray.d, wo = its.toWorld(bRec.wo);
|
||||
ray = Ray(its.p, wo, ray.time);
|
||||
ray.setOrigin(its.p);
|
||||
ray.setDirection(wo);
|
||||
|
||||
/* Prevent light leaks due to the use of shading normals -- [Veach, p. 158] */
|
||||
Float wiDotGeoN = dot(its.geoFrame.n, wi),
|
||||
|
@ -199,7 +205,6 @@ void ParticleTracer::process(const WorkUnit *workUnit, WorkResult *workResult,
|
|||
(Frame::cosTheta(bRec.wo) * wiDotGeoN));
|
||||
|
||||
caustic &= (bRec.sampledType & BSDF::EDelta) ? true : false;
|
||||
|
||||
}
|
||||
++depth;
|
||||
}
|
||||
|
|
|
@ -191,17 +191,17 @@ Scene::Scene(Stream *stream, InstanceManager *manager)
|
|||
}
|
||||
|
||||
Scene::~Scene() {
|
||||
for (unsigned int i=0; i<m_shapes.size(); i++)
|
||||
for (size_t i=0; i<m_shapes.size(); i++)
|
||||
m_shapes[i]->decRef();
|
||||
for (unsigned int i=0; i<m_meshes.size(); i++)
|
||||
for (size_t i=0; i<m_meshes.size(); i++)
|
||||
m_meshes[i]->decRef();
|
||||
for (unsigned int i=0; i<m_media.size(); i++)
|
||||
for (size_t i=0; i<m_media.size(); i++)
|
||||
m_media[i]->decRef();
|
||||
for (unsigned int i=0; i<m_objects.size(); i++)
|
||||
for (size_t i=0; i<m_objects.size(); i++)
|
||||
m_objects[i]->decRef();
|
||||
for (unsigned int i=0; i<m_ssIntegrators.size(); i++)
|
||||
for (size_t i=0; i<m_ssIntegrators.size(); i++)
|
||||
m_ssIntegrators[i]->decRef();
|
||||
for (unsigned int i=0; i<m_luminaires.size(); i++)
|
||||
for (size_t i=0; i<m_luminaires.size(); i++)
|
||||
m_luminaires[i]->decRef();
|
||||
}
|
||||
|
||||
|
@ -230,8 +230,6 @@ void Scene::configure() {
|
|||
AABB aabb;
|
||||
for (size_t i=0; i<m_shapes.size(); ++i)
|
||||
aabb.expandBy(m_shapes[i]->getAABB());
|
||||
for (size_t i=0; i<m_media.size(); ++i)
|
||||
aabb.expandBy(m_media[i]->getAABB());
|
||||
if (!aabb.isValid())
|
||||
Log(EError, "Unable to set up a default camera -- does the scene contain anything at all?");
|
||||
Point center = aabb.getCenter();
|
||||
|
@ -247,9 +245,6 @@ void Scene::configure() {
|
|||
m_sampler = m_camera->getSampler();
|
||||
}
|
||||
|
||||
if (m_media.size() > 1)
|
||||
Log(EError, "Scenes are currently restricted to at most one participating medium.");
|
||||
|
||||
m_integrator->configureSampler(m_sampler);
|
||||
|
||||
/**
|
||||
|
@ -275,12 +270,6 @@ void Scene::initialize() {
|
|||
|
||||
m_aabb = m_kdtree->getAABB();
|
||||
m_bsphere = m_kdtree->getBSphere();
|
||||
|
||||
if (m_media.size() > 0) {
|
||||
for (size_t i=0; i<m_media.size(); i++)
|
||||
m_aabb.expandBy(m_media[i]->getAABB());
|
||||
m_bsphere = m_aabb.getBSphere();
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_luminairePDF.isReady()) {
|
||||
|
@ -327,12 +316,6 @@ bool Scene::preprocess(RenderQueue *queue, const RenderJob *job,
|
|||
if (!(*it)->preprocess(this, queue, job,
|
||||
sceneResID, cameraResID, samplerResID))
|
||||
return false;
|
||||
|
||||
/* Pre-process step for all participating media */
|
||||
for (std::vector<Medium *>::iterator it = m_media.begin();
|
||||
it != m_media.end(); ++it)
|
||||
(*it)->preprocess(this, queue, job,
|
||||
sceneResID, cameraResID, samplerResID);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -376,27 +359,12 @@ Float Scene::pdfLuminaire(const Point &p,
|
|||
return luminaire->pdf(p, lRec, delta) * fraction;
|
||||
}
|
||||
|
||||
Float Scene::pdfLuminaire(const Intersection &its,
|
||||
const LuminaireSamplingRecord &lRec, bool delta) const {
|
||||
const Luminaire *luminaire = lRec.luminaire;
|
||||
Float luminance;
|
||||
|
||||
if (m_importanceSampleLuminaires)
|
||||
luminance = luminaire->getSamplingWeight();
|
||||
else
|
||||
luminance = 1.0f;
|
||||
|
||||
/* Calculate the probability of importance sampling this luminaire */
|
||||
const Float fraction = luminance / m_luminairePDF.getOriginalSum();
|
||||
return luminaire->pdf(its, lRec, delta) * fraction;
|
||||
}
|
||||
|
||||
bool Scene::sampleLuminaire(const Point &p,
|
||||
LuminaireSamplingRecord &lRec, Float time, const Point2 &s,
|
||||
bool Scene::sampleLuminaire(const Point &p, Float time,
|
||||
LuminaireSamplingRecord &lRec, const Point2 &s,
|
||||
bool testVisibility) const {
|
||||
Point2 sample(s);
|
||||
Float lumPdf;
|
||||
unsigned int index = m_luminairePDF.sampleReuse(sample.x, lumPdf);
|
||||
size_t index = m_luminairePDF.sampleReuse(sample.x, lumPdf);
|
||||
const Luminaire *luminaire = m_luminaires[index];
|
||||
luminaire->sample(p, lRec, sample);
|
||||
|
||||
|
@ -404,7 +372,7 @@ bool Scene::sampleLuminaire(const Point &p,
|
|||
if (testVisibility && isOccluded(p, lRec.sRec.p, time))
|
||||
return false;
|
||||
lRec.pdf *= lumPdf;
|
||||
lRec.Le /= lRec.pdf;
|
||||
lRec.value /= lRec.pdf;
|
||||
lRec.luminaire = luminaire;
|
||||
return true;
|
||||
} else {
|
||||
|
@ -412,20 +380,30 @@ bool Scene::sampleLuminaire(const Point &p,
|
|||
}
|
||||
}
|
||||
|
||||
bool Scene::sampleLuminaire(const Intersection &its,
|
||||
LuminaireSamplingRecord &lRec, const Point2 &s,
|
||||
bool testVisibility) const {
|
||||
bool Scene::sampleAttenuatedLuminaire(const Point &p, Float time,
|
||||
const Medium *medium, LuminaireSamplingRecord &lRec,
|
||||
const Point2 &s) const {
|
||||
Point2 sample(s);
|
||||
Float lumPdf;
|
||||
unsigned int index = m_luminairePDF.sampleReuse(sample.x, lumPdf);
|
||||
const Luminaire *luminaire = m_luminaires[index];
|
||||
luminaire->sample(its, lRec, sample);
|
||||
const Luminaire *luminaire = m_luminaires[
|
||||
m_luminairePDF.sampleReuse(sample.x, lumPdf)];
|
||||
luminaire->sample(p, lRec, sample);
|
||||
|
||||
if (lRec.pdf != 0) {
|
||||
if (testVisibility && isOccluded(its.p, lRec.sRec.p, its.time))
|
||||
Vector d = normalize(lRec.sRec.p - p);
|
||||
const Shape *shape;
|
||||
Ray ray(p, d, time)
|
||||
Float t;
|
||||
|
||||
while (scene->rayIntersect(ray, t, shape)) {
|
||||
|
||||
}
|
||||
|
||||
Spectrum attenuation = getAttenuation();
|
||||
if (attenuation.isBlack())
|
||||
return false;
|
||||
lRec.pdf *= lumPdf;
|
||||
lRec.Le /= lRec.pdf;
|
||||
lRec.value /= lRec.pdf * attenuation;
|
||||
lRec.luminaire = luminaire;
|
||||
return true;
|
||||
} else {
|
||||
|
@ -435,29 +413,25 @@ bool Scene::sampleLuminaire(const Intersection &its,
|
|||
|
||||
void Scene::sampleEmission(EmissionRecord &eRec, Point2 &sample1, Point2 &sample2) const {
|
||||
Float lumPdf;
|
||||
unsigned int index = m_luminairePDF.sampleReuse(sample1.x, lumPdf);
|
||||
size_t index = m_luminairePDF.sampleReuse(sample1.x, lumPdf);
|
||||
const Luminaire *luminaire = m_luminaires[index];
|
||||
luminaire->sampleEmission(eRec, sample1, sample2);
|
||||
eRec.pdfArea *= lumPdf;
|
||||
eRec.luminaire = luminaire;
|
||||
Float cosTheta = (eRec.luminaire->getType() & Luminaire::EOnSurface)
|
||||
? absDot(eRec.sRec.n, eRec.d) : 1;
|
||||
eRec.P *= cosTheta / (eRec.pdfArea * eRec.pdfDir);
|
||||
eRec.value *= cosTheta / (eRec.pdfArea * eRec.pdfDir);
|
||||
}
|
||||
|
||||
void Scene::sampleEmissionArea(EmissionRecord &eRec, Point2 &sample) const {
|
||||
Float lumPdf;
|
||||
unsigned int index = m_luminairePDF.sampleReuse(sample.x, lumPdf);
|
||||
size_t index = m_luminairePDF.sampleReuse(sample.x, lumPdf);
|
||||
const Luminaire *luminaire = m_luminaires[index];
|
||||
luminaire->sampleEmissionArea(eRec, sample);
|
||||
eRec.pdfArea *= lumPdf;
|
||||
eRec.luminaire = luminaire;
|
||||
}
|
||||
|
||||
Spectrum Scene::sampleEmissionDirection(EmissionRecord &eRec, Point2 &sample) const {
|
||||
return eRec.luminaire->sampleEmissionDirection(eRec, sample);
|
||||
}
|
||||
|
||||
void Scene::pdfEmission(EmissionRecord &eRec, bool delta) const {
|
||||
const Luminaire *luminaire = eRec.luminaire;
|
||||
Float luminance;
|
||||
|
@ -480,41 +454,6 @@ Spectrum Scene::LeBackground(const Ray &ray) const {
|
|||
return Le;
|
||||
}
|
||||
|
||||
Spectrum Scene::getAttenuation(const Ray &_ray) const {
|
||||
Spectrum attenuation(1.0f);
|
||||
if (m_media.size() > 0) {
|
||||
Float dLength = _ray.d.length();
|
||||
Ray ray(_ray.o, _ray.d/dLength,
|
||||
_ray.mint*dLength, _ray.maxt*dLength, _ray.time);
|
||||
for (std::vector<Medium *>::const_iterator it =
|
||||
m_media.begin(); it != m_media.end(); ++it)
|
||||
attenuation *= (*it)->tau(ray);
|
||||
}
|
||||
return attenuation;
|
||||
}
|
||||
|
||||
bool Scene::sampleDistance(const Ray &ray, MediumSamplingRecord &mRec,
|
||||
Sampler *sampler) const {
|
||||
if (m_media.size() > 0) {
|
||||
return m_media[0]->sampleDistance(ray, mRec, sampler);
|
||||
} else {
|
||||
mRec.pdfFailure = 1.0f;
|
||||
mRec.pdfSuccess = mRec.pdfSuccessRev = 0.0f;
|
||||
mRec.attenuation = Spectrum(1.0f);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void Scene::pdfDistance(const Ray &ray, Float t, MediumSamplingRecord &mRec) const {
|
||||
if (m_media.size() > 0) {
|
||||
m_media[0]->pdfDistance(ray, t, mRec);
|
||||
} else {
|
||||
mRec.pdfSuccess = mRec.pdfSuccessRev = 0;
|
||||
mRec.attenuation = Spectrum(1.0f);
|
||||
mRec.pdfFailure = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void Scene::addChild(const std::string &name, ConfigurableObject *child) {
|
||||
const Class *cClass = child->getClass();
|
||||
|
||||
|
@ -554,22 +493,22 @@ void Scene::addChild(const std::string &name, ConfigurableObject *child) {
|
|||
ref<Scene> scene = static_cast<Scene *>(child);
|
||||
/* A scene from somewhere else has been included.
|
||||
Add all of its contents */
|
||||
for (unsigned int i=0; i<scene->getLuminaires().size(); ++i) {
|
||||
for (size_t i=0; i<scene->getLuminaires().size(); ++i) {
|
||||
Luminaire *lum = scene->getLuminaires()[i];
|
||||
lum->setParent(this);
|
||||
addChild("luminaire", lum);
|
||||
}
|
||||
for (unsigned int i=0; i<scene->getShapes().size(); ++i) {
|
||||
for (size_t i=0; i<scene->getShapes().size(); ++i) {
|
||||
Shape *shape = scene->getShapes()[i];
|
||||
shape->setParent(this);
|
||||
addChild("shape", shape);
|
||||
}
|
||||
for (unsigned int i=0; i<scene->getReferencedObjects().size(); ++i) {
|
||||
for (size_t i=0; i<scene->getReferencedObjects().size(); ++i) {
|
||||
ConfigurableObject *obj = scene->getReferencedObjects()[i];
|
||||
obj->setParent(this);
|
||||
addChild("object", obj);
|
||||
}
|
||||
for (unsigned int i=0; i<scene->getMedia().size(); ++i) {
|
||||
for (size_t i=0; i<scene->getMedia().size(); ++i) {
|
||||
Medium *medium = scene->getMedia()[i];
|
||||
medium->setParent(this);
|
||||
addChild("medium", medium);
|
||||
|
@ -646,25 +585,25 @@ void Scene::serialize(Stream *stream, InstanceManager *manager) const {
|
|||
m_aabb.serialize(stream);
|
||||
m_bsphere.serialize(stream);
|
||||
manager->serialize(stream, m_backgroundLuminaire.get());
|
||||
stream->writeUInt((unsigned int) m_shapes.size());
|
||||
stream->writeUInt((uint32_t) m_shapes.size());
|
||||
for (size_t i=0; i<m_shapes.size(); ++i)
|
||||
manager->serialize(stream, m_shapes[i]);
|
||||
stream->writeUInt((unsigned int) m_meshes.size());
|
||||
stream->writeUInt((uint32_t) m_meshes.size());
|
||||
for (size_t i=0; i<m_meshes.size(); ++i)
|
||||
manager->serialize(stream, m_meshes[i]);
|
||||
stream->writeUInt((unsigned int) m_luminaires.size());
|
||||
stream->writeUInt((uint32_t) m_luminaires.size());
|
||||
for (size_t i=0; i<m_luminaires.size(); ++i)
|
||||
manager->serialize(stream, m_luminaires[i]);
|
||||
stream->writeUInt((unsigned int) m_media.size());
|
||||
stream->writeUInt((uint32_t) m_media.size());
|
||||
for (size_t i=0; i<m_media.size(); ++i)
|
||||
manager->serialize(stream, m_media[i]);
|
||||
stream->writeUInt((unsigned int) m_ssIntegrators.size());
|
||||
stream->writeUInt((uint32_t) m_ssIntegrators.size());
|
||||
for (size_t i=0; i<m_ssIntegrators.size(); ++i)
|
||||
manager->serialize(stream, m_ssIntegrators[i]);
|
||||
stream->writeUInt((unsigned int) m_objects.size());
|
||||
stream->writeUInt((uint32_t) m_objects.size());
|
||||
for (size_t i=0; i<m_objects.size(); ++i)
|
||||
manager->serialize(stream, m_objects[i]);
|
||||
stream->writeUInt((unsigned int) m_netObjects.size());
|
||||
stream->writeUInt((uint32_t) m_netObjects.size());
|
||||
for (size_t i=0; i<m_netObjects.size(); ++i)
|
||||
manager->serialize(stream, m_netObjects[i]);
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include <mitsuba/render/bsdf.h>
|
||||
#include <mitsuba/render/subsurface.h>
|
||||
#include <mitsuba/render/luminaire.h>
|
||||
#include <mitsuba/render/medium.h>
|
||||
|
||||
MTS_NAMESPACE_BEGIN
|
||||
|
||||
|
@ -33,19 +34,15 @@ Shape::Shape(Stream *stream, InstanceManager *manager)
|
|||
m_bsdf = static_cast<BSDF *>(manager->getInstance(stream));
|
||||
m_subsurface = static_cast<Subsurface *>(manager->getInstance(stream));
|
||||
m_luminaire = static_cast<Luminaire *>(manager->getInstance(stream));
|
||||
m_interiorMedium = static_cast<Medium *>(manager->getInstance(stream));
|
||||
m_exteriorMedium = static_cast<Medium *>(manager->getInstance(stream));
|
||||
}
|
||||
|
||||
Shape::~Shape() {
|
||||
}
|
||||
|
||||
|
||||
void Shape::configure() {
|
||||
/* Ensure that there is at least some default BSDF */
|
||||
if (m_bsdf == NULL) {
|
||||
m_bsdf = static_cast<BSDF *> (PluginManager::getInstance()->
|
||||
createObject(BSDF::m_theClass, Properties("lambertian")));
|
||||
}
|
||||
}
|
||||
void Shape::configure() { }
|
||||
|
||||
bool Shape::isCompound() const {
|
||||
return false;
|
||||
|
@ -96,6 +93,17 @@ void Shape::addChild(const std::string &name, ConfigurableObject *child) {
|
|||
} else if (cClass->derivesFrom(Subsurface::m_theClass)) {
|
||||
Assert(m_subsurface == NULL);
|
||||
m_subsurface = static_cast<Subsurface *>(child);
|
||||
} else if (cClass->derivesFrom(Medium::m_theClass)) {
|
||||
if (name == "interiorMedium") {
|
||||
Assert(m_interiorMedium != NULL);
|
||||
m_interiorMedium = static_cast<Medium *>(child);
|
||||
} else if (name == "exteriorMedium") {
|
||||
Assert(m_exteriorMedium != NULL);
|
||||
m_exteriorMedium = static_cast<Medium *>(child);
|
||||
} else {
|
||||
Log(EError, "Shape: Invalid medium child (must be named "
|
||||
"'interiorMedium' or 'exteriorMedium')!");
|
||||
}
|
||||
} else {
|
||||
Log(EError, "Shape: Invalid child node!");
|
||||
}
|
||||
|
@ -110,6 +118,8 @@ void Shape::serialize(Stream *stream, InstanceManager *manager) const {
|
|||
manager->serialize(stream, m_bsdf.get());
|
||||
manager->serialize(stream, m_subsurface.get());
|
||||
manager->serialize(stream, m_luminaire.get());
|
||||
manager->serialize(stream, m_interiorMedium.get());
|
||||
manager->serialize(stream, m_exteriorMedium.get());
|
||||
}
|
||||
|
||||
bool Shape::rayIntersect(const Ray &ray, Float mint,
|
||||
|
|
|
@ -131,6 +131,35 @@ bool ShapeKDTree::rayIntersect(const Ray &ray, Intersection &its) const {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool ShapeKDTree::rayIntersect(const Ray &ray, Float &t, ConstShapePtr &shape) const {
|
||||
uint8_t temp[MTS_KD_INTERSECTION_TEMP];
|
||||
Float mint, maxt;
|
||||
|
||||
t = std::numeric_limits<Float>::infinity();
|
||||
|
||||
++shadowRaysTraced;
|
||||
if (m_aabb.rayIntersect(ray, mint, maxt)) {
|
||||
/* Use an adaptive ray epsilon */
|
||||
Float rayMinT = ray.mint;
|
||||
if (rayMinT == Epsilon)
|
||||
rayMinT *= std::max(std::max(std::abs(ray.o.x),
|
||||
std::abs(ray.o.y)), std::abs(ray.o.z));
|
||||
|
||||
if (rayMinT > mint) mint = rayMinT;
|
||||
if (ray.maxt < maxt) maxt = ray.maxt;
|
||||
|
||||
if (EXPECT_TAKEN(maxt > mint)) {
|
||||
if (rayIntersectHavran<false>(ray, mint, maxt, t, temp)) {
|
||||
const IntersectionCache *cache = reinterpret_cast<const IntersectionCache *>(temp);
|
||||
shape = m_shapes[cache->shapeIndex];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool ShapeKDTree::rayIntersect(const Ray &ray) const {
|
||||
Float mint, maxt, t = std::numeric_limits<Float>::infinity();
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ size_t generateVPLs(const Scene *scene, size_t offset, size_t count, int maxDept
|
|||
|
||||
/* Sample an emitted particle */
|
||||
scene->sampleEmissionArea(eRec, areaSample);
|
||||
weight = eRec.P / eRec.pdfArea;
|
||||
weight = eRec.value / eRec.pdfArea;
|
||||
VPL lumVPL(ELuminaireVPL, weight);
|
||||
lumVPL.its.p = eRec.sRec.p;
|
||||
lumVPL.its.shFrame = (eRec.luminaire->getType() & Luminaire::EOnSurface)
|
||||
|
|
|
@ -59,50 +59,41 @@ public:
|
|||
return m_intensity * m_shape->getSurfaceArea() * M_PI;
|
||||
}
|
||||
|
||||
Spectrum Le(const LuminaireSamplingRecord &lRec) const {
|
||||
if (dot(lRec.d, lRec.sRec.n) <= 0)
|
||||
Spectrum Le(const Intersection &its, const Vector &d) const {
|
||||
if (dot(d, its.geoFrame.n) <= 0)
|
||||
return Spectrum(0.0f);
|
||||
return m_intensity;
|
||||
}
|
||||
|
||||
inline void sample(const Point &p, LuminaireSamplingRecord &lRec,
|
||||
void sample(const Point &p, LuminaireSamplingRecord &lRec,
|
||||
const Point2 &sample) const {
|
||||
lRec.pdf = m_shape->sampleSolidAngle(lRec.sRec, p, sample);
|
||||
lRec.d = p - lRec.sRec.p;
|
||||
|
||||
if (EXPECT_TAKEN(lRec.pdf > 0 && dot(lRec.d, lRec.sRec.n) > 0)) {
|
||||
lRec.Le = m_intensity;
|
||||
lRec.value = m_intensity;
|
||||
lRec.d = normalize(lRec.d);
|
||||
} else {
|
||||
lRec.pdf = 0;
|
||||
}
|
||||
}
|
||||
|
||||
inline void sample(const Intersection &its, LuminaireSamplingRecord &lRec,
|
||||
const Point2 &sample) const {
|
||||
AreaLuminaire::sample(its.p, lRec, sample);
|
||||
}
|
||||
|
||||
inline Float pdf(const Point &p, const LuminaireSamplingRecord &lRec, bool delta) const {
|
||||
Float pdf(const Point &p, const LuminaireSamplingRecord &lRec, bool delta) const {
|
||||
return m_shape->pdfSolidAngle(lRec.sRec, p);
|
||||
}
|
||||
|
||||
Float pdf(const Intersection &its, const LuminaireSamplingRecord &lRec, bool delta) const {
|
||||
return AreaLuminaire::pdf(its.p, lRec, delta);
|
||||
}
|
||||
|
||||
void sampleEmission(EmissionRecord &eRec,
|
||||
const Point2 &sample1, const Point2 &sample2) const {
|
||||
eRec.pdfArea = m_shape->sampleArea(eRec.sRec, sample1);
|
||||
Vector wo = squareToHemispherePSA(sample2);
|
||||
eRec.pdfDir = Frame::cosTheta(wo) * INV_PI;
|
||||
eRec.d = Frame(eRec.sRec.n).toWorld(wo);
|
||||
eRec.P = m_intensity;
|
||||
eRec.value = m_intensity;
|
||||
}
|
||||
|
||||
void sampleEmissionArea(EmissionRecord &eRec, const Point2 &sample) const {
|
||||
eRec.pdfArea = m_shape->sampleArea(eRec.sRec, sample);
|
||||
eRec.P = m_intensity * M_PI;
|
||||
eRec.value = m_intensity * M_PI;
|
||||
}
|
||||
|
||||
Spectrum fArea(const EmissionRecord &eRec) const {
|
||||
|
@ -116,7 +107,7 @@ public:
|
|||
return Spectrum(INV_PI);
|
||||
}
|
||||
|
||||
Spectrum f(const EmissionRecord &eRec) const {
|
||||
Spectrum fDirection(const EmissionRecord &eRec) const {
|
||||
Float dp = dot(eRec.sRec.n, eRec.d);
|
||||
if (dp > 0)
|
||||
return Spectrum(INV_PI);
|
||||
|
|
|
@ -54,23 +54,13 @@ public:
|
|||
return m_intensity * m_surfaceArea;
|
||||
}
|
||||
|
||||
Spectrum Le(const LuminaireSamplingRecord &lRec) const {
|
||||
/* Collimated beam is not part of the scene */
|
||||
Log(EWarn, "This function should never be called.");
|
||||
return Spectrum(0.0f);
|
||||
}
|
||||
|
||||
inline Float pdf(const Point &p, const LuminaireSamplingRecord &lRec, bool delta) const {
|
||||
Float pdf(const Point &p, const LuminaireSamplingRecord &lRec, bool delta) const {
|
||||
/* PDF is a delta function - zero probability when a sample point was not
|
||||
generated using sample() */
|
||||
return delta ? 1.0f : 0.0f;
|
||||
}
|
||||
|
||||
Float pdf(const Intersection &its, const LuminaireSamplingRecord &lRec, bool delta) const {
|
||||
return CollimatedBeamLuminaire::pdf(its.p, lRec, delta);
|
||||
}
|
||||
|
||||
inline void sample(const Point &p, LuminaireSamplingRecord &lRec,
|
||||
void sample(const Point &p, LuminaireSamplingRecord &lRec,
|
||||
const Point2 &sample) const {
|
||||
Point local = m_worldToLuminaire(p);
|
||||
Vector2 planeProjection = Vector2(local.x, local.y);
|
||||
|
@ -82,29 +72,24 @@ public:
|
|||
lRec.d = m_direction;
|
||||
lRec.luminaire = this;
|
||||
lRec.pdf = 1.0f;
|
||||
lRec.Le = m_intensity;
|
||||
lRec.value = m_intensity;
|
||||
}
|
||||
}
|
||||
|
||||
void sample(const Intersection &its, LuminaireSamplingRecord &lRec,
|
||||
const Point2 &sample) const {
|
||||
CollimatedBeamLuminaire::sample(its.p, lRec, sample);
|
||||
}
|
||||
|
||||
void sampleEmission(EmissionRecord &eRec, const Point2 &sample1, const Point2 &sample2) const {
|
||||
Point2 posOnDisk = squareToDiskConcentric(sample1) * m_radius;
|
||||
eRec.sRec.p = m_luminaireToWorld(Point(posOnDisk.x, posOnDisk.y, 0));
|
||||
eRec.d = m_direction;
|
||||
eRec.pdfArea = m_invSurfaceArea;
|
||||
eRec.pdfDir = 1;
|
||||
eRec.P = m_intensity;
|
||||
eRec.value = m_intensity;
|
||||
}
|
||||
|
||||
void sampleEmissionArea(EmissionRecord &eRec, const Point2 &sample) const {
|
||||
Point2 posOnDisk = squareToDiskConcentric(sample) * m_radius;
|
||||
eRec.sRec.p = m_luminaireToWorld(Point(posOnDisk.x, posOnDisk.y, 0));
|
||||
eRec.pdfArea = m_invSurfaceArea;
|
||||
eRec.P = m_intensity;
|
||||
eRec.value = m_intensity;
|
||||
}
|
||||
|
||||
Spectrum sampleEmissionDirection(EmissionRecord &eRec, const Point2 &sample) const {
|
||||
|
@ -117,14 +102,14 @@ public:
|
|||
return m_intensity;
|
||||
}
|
||||
|
||||
Spectrum f(const EmissionRecord &eRec) const {
|
||||
Spectrum fDirection(const EmissionRecord &eRec) const {
|
||||
return Spectrum(0.0f);
|
||||
}
|
||||
|
||||
void pdfEmission(EmissionRecord &eRec, bool delta) const {
|
||||
eRec.pdfArea = delta ? 0.0f : m_invSurfaceArea;
|
||||
eRec.pdfDir = delta ? 1.0f : 0.0f;
|
||||
eRec.P = m_intensity;
|
||||
eRec.value = m_intensity;
|
||||
}
|
||||
|
||||
std::string toString() const {
|
||||
|
|
|
@ -71,11 +71,7 @@ public:
|
|||
return m_intensity;
|
||||
}
|
||||
|
||||
Spectrum Le(const LuminaireSamplingRecord &lRec) const {
|
||||
return m_intensity;
|
||||
}
|
||||
|
||||
inline void sample(const Point &p, LuminaireSamplingRecord &lRec,
|
||||
void sample(const Point &p, LuminaireSamplingRecord &lRec,
|
||||
const Point2 &sample) const {
|
||||
Vector d = squareToSphere(sample);
|
||||
|
||||
|
@ -85,25 +81,16 @@ public:
|
|||
lRec.pdf = 1.0f / (4*M_PI);
|
||||
lRec.sRec.n = normalize(m_bsphere.center - lRec.sRec.p);
|
||||
lRec.d = -d;
|
||||
lRec.Le = m_intensity;
|
||||
lRec.value = m_intensity;
|
||||
} else {
|
||||
lRec.pdf = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
inline void sample(const Intersection &its, LuminaireSamplingRecord &lRec,
|
||||
const Point2 &sample) const {
|
||||
ConstantLuminaire::sample(its.p, lRec, sample);
|
||||
}
|
||||
|
||||
inline Float pdf(const Point &p, const LuminaireSamplingRecord &lRec, bool delta) const {
|
||||
Float pdf(const Point &p, const LuminaireSamplingRecord &lRec, bool delta) const {
|
||||
return delta ? 0.0f : 1.0f / (4*M_PI);
|
||||
}
|
||||
|
||||
inline Float pdf(const Intersection &its, const LuminaireSamplingRecord &lRec, bool delta) const {
|
||||
return delta ? 0.0f : 1.0f / (4*M_PI);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the tricky bit - we want to sample a ray that
|
||||
* has uniform density over the set of all rays passing
|
||||
|
@ -126,7 +113,7 @@ public:
|
|||
Float length = eRec.d.length();
|
||||
|
||||
if (length == 0) {
|
||||
eRec.P = Spectrum(0.0f);
|
||||
eRec.value = Spectrum(0.0f);
|
||||
eRec.pdfArea = eRec.pdfDir = 1.0f;
|
||||
return;
|
||||
}
|
||||
|
@ -134,7 +121,7 @@ public:
|
|||
eRec.d /= length;
|
||||
eRec.pdfArea = m_invSurfaceArea;
|
||||
eRec.pdfDir = INV_PI * dot(eRec.sRec.n, eRec.d);
|
||||
eRec.P = m_intensity;
|
||||
eRec.value = m_intensity;
|
||||
}
|
||||
|
||||
void sampleEmissionArea(EmissionRecord &eRec, const Point2 &sample) const {
|
||||
|
@ -147,7 +134,7 @@ public:
|
|||
eRec.sRec.p = m_bsphere.center + d * radius;
|
||||
eRec.sRec.n = Normal(-d);
|
||||
eRec.pdfArea = 1.0f / (4 * M_PI * radius * radius);
|
||||
eRec.P = m_intensity * M_PI;
|
||||
eRec.value = m_intensity * M_PI;
|
||||
}
|
||||
|
||||
Spectrum sampleEmissionDirection(EmissionRecord &eRec, const Point2 &sample) const {
|
||||
|
@ -192,12 +179,12 @@ public:
|
|||
eRec.d = -ray.d;
|
||||
eRec.pdfArea = m_invSurfaceArea;
|
||||
eRec.pdfDir = INV_PI * dot(eRec.sRec.n, eRec.d);
|
||||
eRec.P = m_intensity;
|
||||
eRec.value = m_intensity;
|
||||
eRec.luminaire = this;
|
||||
return true;
|
||||
}
|
||||
|
||||
Spectrum f(const EmissionRecord &eRec) const {
|
||||
Spectrum fDirection(const EmissionRecord &eRec) const {
|
||||
return Spectrum(INV_PI);
|
||||
}
|
||||
|
||||
|
|
|
@ -75,34 +75,19 @@ public:
|
|||
return m_intensity * m_surfaceArea;
|
||||
}
|
||||
|
||||
Spectrum Le(const LuminaireSamplingRecord &lRec) const {
|
||||
/* Directional luminaire is not part of the scene */
|
||||
Log(EError, "This function should never be called.");
|
||||
return Spectrum(0.0f);
|
||||
}
|
||||
|
||||
inline Float pdf(const Point &p, const LuminaireSamplingRecord &lRec, bool delta) const {
|
||||
Float pdf(const Point &p, const LuminaireSamplingRecord &lRec, bool delta) const {
|
||||
/* PDF is a delta function - zero probability when a sample point was not
|
||||
generated using sample() */
|
||||
return delta ? 1.0f : 0.0f;
|
||||
}
|
||||
|
||||
Float pdf(const Intersection &its, const LuminaireSamplingRecord &lRec, bool delta) const {
|
||||
return DirectionalLuminaire::pdf(its.p, lRec, delta);
|
||||
}
|
||||
|
||||
inline void sample(const Point &p, LuminaireSamplingRecord &lRec,
|
||||
void sample(const Point &p, LuminaireSamplingRecord &lRec,
|
||||
const Point2 &sample) const {
|
||||
lRec.sRec.p = p - m_direction * (2 * m_diskRadius);
|
||||
lRec.d = m_direction;
|
||||
lRec.luminaire = this;
|
||||
lRec.pdf = 1.0f;
|
||||
lRec.Le = m_intensity;
|
||||
}
|
||||
|
||||
void sample(const Intersection &its, LuminaireSamplingRecord &lRec,
|
||||
const Point2 &sample) const {
|
||||
DirectionalLuminaire::sample(its.p, lRec, sample);
|
||||
lRec.value = m_intensity;
|
||||
}
|
||||
|
||||
void sampleEmission(EmissionRecord &eRec, const Point2 &sample1, const Point2 &sample2) const {
|
||||
|
@ -111,14 +96,14 @@ public:
|
|||
eRec.d = m_direction;
|
||||
eRec.pdfArea = m_invSurfaceArea;
|
||||
eRec.pdfDir = 1;
|
||||
eRec.P = m_intensity;
|
||||
eRec.value = m_intensity;
|
||||
}
|
||||
|
||||
void sampleEmissionArea(EmissionRecord &eRec, const Point2 &sample) const {
|
||||
Point2 posOnDisk = squareToDiskConcentric(sample) * m_diskRadius;
|
||||
eRec.sRec.p = m_diskOrigin + Frame(m_direction).toWorld(Vector(posOnDisk.x, posOnDisk.y, 0));
|
||||
eRec.pdfArea = m_invSurfaceArea;
|
||||
eRec.P = m_intensity;
|
||||
eRec.value = m_intensity;
|
||||
}
|
||||
|
||||
Spectrum sampleEmissionDirection(EmissionRecord &eRec, const Point2 &sample) const {
|
||||
|
@ -131,7 +116,7 @@ public:
|
|||
return m_intensity;
|
||||
}
|
||||
|
||||
Spectrum f(const EmissionRecord &eRec) const {
|
||||
Spectrum fDirection(const EmissionRecord &eRec) const {
|
||||
/* Directional luminaire beam is not part of the scene */
|
||||
return Spectrum(0.0f);
|
||||
}
|
||||
|
@ -139,7 +124,7 @@ public:
|
|||
void pdfEmission(EmissionRecord &eRec, bool delta) const {
|
||||
eRec.pdfArea = delta ? 0.0f : m_invSurfaceArea;
|
||||
eRec.pdfDir = delta ? 1.0f : 0.0f;
|
||||
eRec.P = m_intensity;
|
||||
eRec.value = m_intensity;
|
||||
}
|
||||
|
||||
std::string toString() const {
|
||||
|
|
|
@ -166,13 +166,9 @@ public:
|
|||
return Le(normalize(ray.d));
|
||||
}
|
||||
|
||||
Spectrum Le(const LuminaireSamplingRecord &lRec) const {
|
||||
return Le(-lRec.d);
|
||||
}
|
||||
|
||||
inline void sample(const Point &p, LuminaireSamplingRecord &lRec,
|
||||
void sample(const Point &p, LuminaireSamplingRecord &lRec,
|
||||
const Point2 &sample) const {
|
||||
Vector d = sampleDirection(sample, lRec.pdf, lRec.Le);
|
||||
Vector d = sampleDirection(sample, lRec.pdf, lRec.value);
|
||||
|
||||
Float nearHit, farHit;
|
||||
if (m_bsphere.contains(p) && m_bsphere.rayIntersect(Ray(p, -d, 0.0f), nearHit, farHit)) {
|
||||
|
@ -184,7 +180,7 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
inline Float pdf(const Point &p, const LuminaireSamplingRecord &lRec, bool delta) const {
|
||||
Float pdf(const Point &p, const LuminaireSamplingRecord &lRec, bool delta) const {
|
||||
#if defined(SAMPLE_UNIFORMLY)
|
||||
return 1.0f / (4*M_PI);
|
||||
#else
|
||||
|
@ -202,16 +198,6 @@ public:
|
|||
#endif
|
||||
}
|
||||
|
||||
/* Sampling routine for surfaces */
|
||||
void sample(const Intersection &its, LuminaireSamplingRecord &lRec,
|
||||
const Point2 &sample) const {
|
||||
EnvMapLuminaire::sample(its.p, lRec, sample);
|
||||
}
|
||||
|
||||
Float pdf(const Intersection &its, const LuminaireSamplingRecord &lRec, bool delta) const {
|
||||
return EnvMapLuminaire::pdf(its.p, lRec, delta);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the tricky bit - we want to sample a ray that
|
||||
* has uniform density over the set of all rays passing
|
||||
|
@ -234,7 +220,7 @@ public:
|
|||
Float length = eRec.d.length();
|
||||
|
||||
if (length == 0) {
|
||||
eRec.P = Spectrum(0.0f);
|
||||
eRec.value = Spectrum(0.0f);
|
||||
eRec.pdfArea = eRec.pdfDir = 1.0f;
|
||||
return;
|
||||
}
|
||||
|
@ -242,7 +228,7 @@ public:
|
|||
eRec.d /= length;
|
||||
eRec.pdfArea = m_invSurfaceArea;
|
||||
eRec.pdfDir = INV_PI * dot(eRec.sRec.n, eRec.d);
|
||||
eRec.P = Le(-eRec.d);
|
||||
eRec.value = Le(-eRec.d);
|
||||
}
|
||||
|
||||
void sampleEmissionArea(EmissionRecord &eRec, const Point2 &sample) const {
|
||||
|
@ -251,7 +237,7 @@ public:
|
|||
eRec.sRec.p = m_bsphere.center + d * m_bsphere.radius;
|
||||
eRec.sRec.n = Normal(-d);
|
||||
eRec.pdfArea = 1.0f / (4 * M_PI * m_bsphere.radius * m_bsphere.radius);
|
||||
eRec.P = Spectrum(M_PI);
|
||||
eRec.value = Spectrum(M_PI);
|
||||
} else {
|
||||
/* Preview mode, which is more suitable for VPL-based rendering: approximate
|
||||
the infinitely far-away source with set of diffuse point sources */
|
||||
|
@ -260,7 +246,7 @@ public:
|
|||
eRec.sRec.p = m_bsphere.center + d * radius;
|
||||
eRec.sRec.n = Normal(-d);
|
||||
eRec.pdfArea = 1.0f / (4 * M_PI * radius * radius);
|
||||
eRec.P = Le(d) * M_PI;
|
||||
eRec.value = Le(d) * M_PI;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -295,7 +281,7 @@ public:
|
|||
eRec.pdfArea = delta ? 0.0f : m_invSurfaceArea;
|
||||
}
|
||||
|
||||
Spectrum f(const EmissionRecord &eRec) const {
|
||||
Spectrum fDirection(const EmissionRecord &eRec) const {
|
||||
if (eRec.type == EmissionRecord::ENormal)
|
||||
return Le(-eRec.d) * INV_PI;
|
||||
else
|
||||
|
@ -321,7 +307,7 @@ public:
|
|||
eRec.pdfArea = m_invSurfaceArea;
|
||||
eRec.pdfDir = INV_PI * dot(eRec.sRec.n, eRec.d);
|
||||
eRec.d = -ray.d;
|
||||
eRec.P = Le(ray.d);
|
||||
eRec.value = Le(ray.d);
|
||||
eRec.luminaire = this;
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -47,33 +47,20 @@ public:
|
|||
return m_intensity * 4 * M_PI;
|
||||
}
|
||||
|
||||
Spectrum Le(const LuminaireSamplingRecord &lRec) const {
|
||||
return Spectrum(0.0f);
|
||||
}
|
||||
|
||||
inline Float pdf(const Point &p, const LuminaireSamplingRecord &lRec, bool delta) const {
|
||||
Float pdf(const Point &p, const LuminaireSamplingRecord &lRec, bool delta) const {
|
||||
/* PDF is a delta function - zero probability when a sample point was not
|
||||
generated using sample() */
|
||||
return delta ? 1.0f : 0.0f;
|
||||
}
|
||||
|
||||
Float pdf(const Intersection &its, const LuminaireSamplingRecord &lRec, bool delta) const {
|
||||
return PointLuminaire::pdf(its.p, lRec, delta);
|
||||
}
|
||||
|
||||
inline void sample(const Point &p, LuminaireSamplingRecord &lRec,
|
||||
void sample(const Point &p, LuminaireSamplingRecord &lRec,
|
||||
const Point2 &sample) const {
|
||||
Vector lumToP = p - m_position;
|
||||
Float invDist = 1.0f / lumToP.length();
|
||||
lRec.sRec.p = m_position;
|
||||
lRec.d = lumToP * invDist;
|
||||
lRec.pdf = 1.0f;
|
||||
lRec.Le = m_intensity * (invDist*invDist);
|
||||
}
|
||||
|
||||
void sample(const Intersection &its, LuminaireSamplingRecord &lRec,
|
||||
const Point2 &sample) const {
|
||||
PointLuminaire::sample(its.p, lRec, sample);
|
||||
lRec.value = m_intensity * (invDist*invDist);
|
||||
}
|
||||
|
||||
void sampleEmission(EmissionRecord &eRec,
|
||||
|
@ -82,13 +69,13 @@ public:
|
|||
eRec.d = squareToSphere(sample2);
|
||||
eRec.pdfDir = 1.0f / (4 * M_PI);
|
||||
eRec.pdfArea = 1;
|
||||
eRec.P = m_intensity;
|
||||
eRec.value = m_intensity;
|
||||
}
|
||||
|
||||
void sampleEmissionArea(EmissionRecord &eRec, const Point2 &sample) const {
|
||||
eRec.sRec.p = m_position;
|
||||
eRec.pdfArea = 1;
|
||||
eRec.P = m_intensity * (4*M_PI);
|
||||
eRec.value = m_intensity * (4*M_PI);
|
||||
}
|
||||
|
||||
Spectrum sampleEmissionDirection(EmissionRecord &eRec, const Point2 &sample) const {
|
||||
|
@ -106,7 +93,7 @@ public:
|
|||
return Spectrum(0.0f);
|
||||
}
|
||||
|
||||
Spectrum f(const EmissionRecord &eRec) const {
|
||||
Spectrum fDirection(const EmissionRecord &eRec) const {
|
||||
return Spectrum(1/(4*M_PI));
|
||||
}
|
||||
|
||||
|
|
|
@ -102,33 +102,20 @@ public:
|
|||
return result * ((m_cutoffAngle - std::acos(cosTheta)) * m_invTransitionWidth);
|
||||
}
|
||||
|
||||
Spectrum Le(const LuminaireSamplingRecord &lRec) const {
|
||||
return Spectrum(0.0f);
|
||||
}
|
||||
|
||||
inline Float pdf(const Point &p, const LuminaireSamplingRecord &lRec, bool delta) const {
|
||||
Float pdf(const Point &p, const LuminaireSamplingRecord &lRec, bool delta) const {
|
||||
/* PDF is a delta function - zero probability when a sample point was not
|
||||
generated using sample() */
|
||||
return delta ? 1.0f : 0.0f;
|
||||
}
|
||||
|
||||
Float pdf(const Intersection &its, const LuminaireSamplingRecord &lRec, bool delta) const {
|
||||
return SpotLuminaire::pdf(its.p, lRec, delta);
|
||||
}
|
||||
|
||||
inline void sample(const Point &p, LuminaireSamplingRecord &lRec,
|
||||
void sample(const Point &p, LuminaireSamplingRecord &lRec,
|
||||
const Point2 &sample) const {
|
||||
Vector lumToP = p - m_position;
|
||||
Float invDist = 1.0f / lumToP.length();
|
||||
lRec.sRec.p = m_position;
|
||||
lRec.d = lumToP * invDist;
|
||||
lRec.pdf = 1.0f;
|
||||
lRec.Le = falloffCurve(lRec.d) * (invDist*invDist);
|
||||
}
|
||||
|
||||
void sample(const Intersection &its, LuminaireSamplingRecord &lRec,
|
||||
const Point2 &sample) const {
|
||||
SpotLuminaire::sample(its.p, lRec, sample);
|
||||
lRec.value = falloffCurve(lRec.d) * (invDist*invDist);
|
||||
}
|
||||
|
||||
void sampleEmission(EmissionRecord &eRec,
|
||||
|
@ -137,13 +124,13 @@ public:
|
|||
m_luminaireToWorld(squareToCone(m_cosCutoffAngle, sample2), eRec.d);
|
||||
eRec.pdfDir = squareToConePdf(m_cosCutoffAngle);
|
||||
eRec.pdfArea = 1;
|
||||
eRec.P = falloffCurve(eRec.d);
|
||||
eRec.value = falloffCurve(eRec.d);
|
||||
}
|
||||
|
||||
void sampleEmissionArea(EmissionRecord &eRec, const Point2 &sample) const {
|
||||
eRec.sRec.p = m_position;
|
||||
eRec.pdfArea = 1;
|
||||
eRec.P = m_intensity;
|
||||
eRec.value = m_intensity;
|
||||
}
|
||||
|
||||
Spectrum sampleEmissionDirection(EmissionRecord &eRec, const Point2 &sample) const {
|
||||
|
@ -160,7 +147,7 @@ public:
|
|||
eRec.pdfArea = delta ? 1.0f : 0.0f;
|
||||
}
|
||||
|
||||
Spectrum f(const EmissionRecord &eRec) const {
|
||||
Spectrum fDirection(const EmissionRecord &eRec) const {
|
||||
return falloffCurve(eRec.d, true);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
Import('env', 'plugins')
|
||||
|
||||
plugins += env.SharedLibrary('#plugins/homogeneous', ['homogeneous.cpp'])
|
||||
plugins += env.SharedLibrary('#plugins/heterogeneous', ['heterogeneous.cpp'])
|
||||
plugins += env.SharedLibrary('#plugins/heterogeneous-flake', ['heterogeneous-flake.cpp'])
|
||||
#plugins += env.SharedLibrary('#plugins/heterogeneous', ['heterogeneous.cpp'])
|
||||
#plugins += env.SharedLibrary('#plugins/heterogeneous-flake', ['heterogeneous-flake.cpp'])
|
||||
|
||||
Export('plugins')
|
||||
|
|
|
@ -22,8 +22,7 @@
|
|||
MTS_NAMESPACE_BEGIN
|
||||
|
||||
/**
|
||||
* Homogeneous participating medium. An arbitrary (manifold) shape
|
||||
* must be specified as a child object.
|
||||
* Homogeneous participating medium with support for various phase functions
|
||||
*/
|
||||
class HomogeneousMedium : public Medium {
|
||||
public:
|
||||
|
@ -34,9 +33,9 @@ public:
|
|||
*/
|
||||
enum ESamplingStrategy {
|
||||
EBalance, /// Exp. distrib.; pick a random channel each time
|
||||
ESingle, /// Exp. distrib.; pick a specified channel
|
||||
EManual, /// Exp. distrib.; manually specify a value
|
||||
EMaximum /// Max-of-exp. distribs
|
||||
ESingle, /// Exp. distrib.; pick a specified channel
|
||||
EManual, /// Exp. distrib.; manually specify a value
|
||||
EMaximum /// Max-of-exp. distribs
|
||||
};
|
||||
|
||||
HomogeneousMedium(const Properties &props)
|
||||
|
@ -111,10 +110,6 @@ public:
|
|||
|
||||
HomogeneousMedium(Stream *stream, InstanceManager *manager)
|
||||
: Medium(stream, manager), m_maxExpDist(NULL) {
|
||||
m_kdTree = new ShapeKDTree();
|
||||
size_t shapeCount = stream->readUInt();
|
||||
for (size_t i=0; i<shapeCount; ++i)
|
||||
addChild("", static_cast<Shape *>(manager->getInstance(stream)));
|
||||
m_strategy = (ESamplingStrategy) stream->readInt();
|
||||
m_samplingDensity= stream->readFloat();
|
||||
m_mediumSamplingWeight = stream->readFloat();
|
||||
|
@ -130,29 +125,16 @@ public:
|
|||
}
|
||||
|
||||
virtual ~HomogeneousMedium() {
|
||||
for (size_t i=0; i<m_shapes.size(); ++i)
|
||||
m_shapes[i]->decRef();
|
||||
if (m_maxExpDist)
|
||||
delete m_maxExpDist;
|
||||
}
|
||||
|
||||
void serialize(Stream *stream, InstanceManager *manager) const {
|
||||
Medium::serialize(stream, manager);
|
||||
stream->writeUInt((uint32_t) m_shapes.size());
|
||||
for (size_t i=0; i<m_shapes.size(); ++i)
|
||||
manager->serialize(stream, m_shapes[i]);
|
||||
stream->writeInt(m_strategy);
|
||||
stream->writeFloat(m_samplingDensity);
|
||||
stream->writeFloat(m_mediumSamplingWeight);
|
||||
}
|
||||
|
||||
void configure() {
|
||||
Medium::configure();
|
||||
if (m_shapes.size() == 0)
|
||||
Log(EError, "This medium requires one or more Shape instance as a child");
|
||||
m_kdTree->build();
|
||||
m_aabb = m_kdTree->getAABB();
|
||||
}
|
||||
|
||||
Float getCoveredLength(const Ray &r) const {
|
||||
Ray ray(r(r.mint), r.d,
|
||||
|
@ -336,27 +318,6 @@ public:
|
|||
Log(EError, "Medium cannot be a parent of a shape");
|
||||
}
|
||||
|
||||
void addChild(const std::string &name, ConfigurableObject *child) {
|
||||
if (child->getClass()->derivesFrom(Shape::m_theClass)) {
|
||||
Shape *shape = static_cast<Shape *>(child);
|
||||
if (shape->isCompound()) {
|
||||
int ctr = 0;
|
||||
while (true) {
|
||||
ref<Shape> childShape = shape->getElement(ctr++);
|
||||
if (!childShape)
|
||||
break;
|
||||
addChild("", childShape);
|
||||
}
|
||||
} else {
|
||||
m_kdTree->addShape(shape);
|
||||
shape->incRef();
|
||||
m_shapes.push_back(shape);
|
||||
}
|
||||
} else {
|
||||
Medium::addChild(name, child);
|
||||
}
|
||||
}
|
||||
|
||||
std::string toString() const {
|
||||
std::ostringstream oss;
|
||||
oss << "HomogeneousMedium[" << endl
|
||||
|
@ -381,8 +342,6 @@ public:
|
|||
|
||||
MTS_DECLARE_CLASS()
|
||||
private:
|
||||
ref<ShapeKDTree> m_kdTree;
|
||||
std::vector<Shape *> m_shapes;
|
||||
Float m_samplingDensity, m_mediumSamplingWeight;
|
||||
ESamplingStrategy m_strategy;
|
||||
MaxExpDist *m_maxExpDist;
|
||||
|
|
Loading…
Reference in New Issue