initial commit with accumulated changes of the participating medium rewrite

metadata
Wenzel Jakob 2011-03-08 20:23:17 +01:00
parent b791e0b453
commit 22a1a37cf0
38 changed files with 1031 additions and 946 deletions

View File

@ -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;
}

View File

@ -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);

View File

@ -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

View File

@ -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
*/

View File

@ -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;
/**

View File

@ -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;

View File

@ -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
*

View File

@ -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;
};

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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;
};

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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
<< "]";

View File

@ -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) {

View File

@ -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;

View File

@ -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);

View File

@ -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 ";

View File

@ -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();

View File

@ -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();
}

View File

@ -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;
}

View File

@ -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]);
}

View File

@ -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,

View File

@ -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();

View File

@ -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)

View File

@ -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);

View File

@ -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 {

View File

@ -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);
}

View File

@ -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 {

View File

@ -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;
}

View File

@ -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));
}

View File

@ -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);
}

View File

@ -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')

View File

@ -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;