diff --git a/data/tests/test_luminaire.xml b/data/tests/test_luminaire.xml
index f93b1195..ca233426 100644
--- a/data/tests/test_luminaire.xml
+++ b/data/tests/test_luminaire.xml
@@ -9,6 +9,9 @@
+
+
+
diff --git a/doc/images/bsdf_conductor_copper.jpg b/doc/images/bsdf_conductor_copper.jpg
index 7aef725d..92314d32 100644
Binary files a/doc/images/bsdf_conductor_copper.jpg and b/doc/images/bsdf_conductor_copper.jpg differ
diff --git a/doc/images/bsdf_conductor_gold.jpg b/doc/images/bsdf_conductor_gold.jpg
index 824aa4c5..9c27dd0b 100644
Binary files a/doc/images/bsdf_conductor_gold.jpg and b/doc/images/bsdf_conductor_gold.jpg differ
diff --git a/include/mitsuba/render/camera.h b/include/mitsuba/render/camera.h
index aeb01eff..895c4f9a 100644
--- a/include/mitsuba/render/camera.h
+++ b/include/mitsuba/render/camera.h
@@ -55,17 +55,17 @@ public:
* Returns false if the computed position is not visible through
* the film's crop window
*/
- virtual bool positionToSample(const Point &p, Point2 &sample) const = 0;
+ virtual bool positionToSample(const Point &p, Point2 &sample) const;
/// Does generateRay() expect a proper lens sample?
- virtual bool needsLensSample() const = 0;
+ virtual bool needsLensSample() const;
/// Does generateRay() expect a proper time sample?
inline bool needsTimeSample() const { return m_shutterOpenTime > 0; }
/// Return the time value of the shutter opening event
inline Float getShutterOpen() const { return m_shutterOpen; }
-
+
/// Return the length, for which the shutter remains open
inline Float getShutterOpenTime() const { return m_shutterOpenTime; }
@@ -114,7 +114,7 @@ public:
* 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;
+ virtual Float areaDensity(const Point2 &p) const;
//! @}
// =============================================================
@@ -175,6 +175,10 @@ public:
/// Return the properties of this camera
inline const Properties &getProperties() const { return m_properties; }
+ /** \brief Configure the object (called _once_ after construction
+ and addition of all child ConfigurableObjects. */
+ virtual void configure();
+
//! @}
// =============================================================
diff --git a/include/mitsuba/render/luminaire.h b/include/mitsuba/render/luminaire.h
index eb96afc6..c59a9027 100644
--- a/include/mitsuba/render/luminaire.h
+++ b/include/mitsuba/render/luminaire.h
@@ -157,7 +157,7 @@ public:
* \brief Return an estimate of the total amount of power emitted
* by this luminaire.
*/
- virtual Spectrum getPower() const = 0;
+ virtual Spectrum getPower() const;
/// Is this luminaire intersectable (e.g. can it be encountered by a tracing a ray)?
inline bool isIntersectable() const { return m_intersectable; }
@@ -194,7 +194,7 @@ public:
* 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;
+ LuminaireSamplingRecord &lRec, const Point2 &sample) const;
/**
* \brief Calculate the solid angle density for generating this sample
@@ -204,7 +204,7 @@ public:
* are considered in the query. Otherwise, they are left out.
*/
virtual Float pdf(const Point &p,
- const LuminaireSamplingRecord &lRec, bool delta) const = 0;
+ const LuminaireSamplingRecord &lRec, bool delta) const;
//! @}
// =============================================================
@@ -225,7 +225,7 @@ public:
* of the spatial and directional sampling densities.
*/
virtual void sampleEmission(EmissionRecord &eRec,
- const Point2& areaSample, const Point2 &dirSample) const = 0;
+ const Point2& areaSample, const Point2 &dirSample) const;
/**
* \brief Sample only the spatial part of the emission sampling strategy
@@ -239,7 +239,7 @@ public:
* spatially dependent emittance component will be stored in \c eRec.
*/
virtual void sampleEmissionArea(EmissionRecord &lRec,
- const Point2 &sample) const = 0;
+ const Point2 &sample) const;
/**
* \brief Sample only the directional part of the emission sampling strategy
@@ -252,7 +252,7 @@ public:
* component of the radiant emittance obtained in \ref sampleEmissionArea.
*/
virtual Spectrum sampleEmissionDirection(EmissionRecord &lRec,
- const Point2 &sample) const = 0;
+ const Point2 &sample) const;
/**
* \brief Given an emitted particle, populate the emission record with the
@@ -261,13 +261,13 @@ public:
* 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;
+ virtual void pdfEmission(EmissionRecord &eRec, bool delta) const;
/**
* \brief Evaluate the spatial component of the radiant emittance at a
* point on the luminaire (ignoring any directional variations).
*/
- virtual Spectrum evalArea(const EmissionRecord &eRec) const = 0;
+ virtual Spectrum evalArea(const EmissionRecord &eRec) const;
/**
* \brief Evaluate the directional emission distribution of this light source
@@ -275,7 +275,7 @@ public:
*
* This function is normalized so that it integrates to one.
*/
- virtual Spectrum evalDirection(const EmissionRecord &eRec) const = 0;
+ virtual Spectrum evalDirection(const EmissionRecord &eRec) const;
//! @}
// =============================================================
diff --git a/src/cameras/SConscript b/src/cameras/SConscript
index 07aa31ac..209caa6b 100644
--- a/src/cameras/SConscript
+++ b/src/cameras/SConscript
@@ -2,5 +2,6 @@ Import('env', 'plugins')
plugins += env.SharedLibrary('perspective', ['perspective.cpp'])
plugins += env.SharedLibrary('orthographic', ['orthographic.cpp'])
+plugins += env.SharedLibrary('environment', ['environment.cpp'])
Export('plugins')
diff --git a/src/cameras/environment.cpp b/src/cameras/environment.cpp
new file mode 100644
index 00000000..9d235c6b
--- /dev/null
+++ b/src/cameras/environment.cpp
@@ -0,0 +1,104 @@
+/*
+ This file is part of Mitsuba, a physically based rendering system.
+
+ Copyright (c) 2007-2011 by Wenzel Jakob and others.
+
+ Mitsuba is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License Version 3
+ as published by the Free Software Foundation.
+
+ Mitsuba is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+#include
+#include
+
+MTS_NAMESPACE_BEGIN
+
+static StatsCounter cameraRays("General", "Camera ray generations");
+
+/**
+ * Simple environment camera model
+ * - based on the version in PBRT
+ */
+class EnvironmentCamera : public Camera {
+public:
+ EnvironmentCamera(const Properties &props)
+ : Camera(props) { }
+
+ EnvironmentCamera(Stream *stream, InstanceManager *manager)
+ : Camera(stream, manager) {
+ configure();
+ }
+
+ void serialize(Stream *stream, InstanceManager *manager) const {
+ Camera::serialize(stream, manager);
+ }
+
+ void configure() {
+ Camera::configure();
+ Vector2i filmSize = m_film->getSize();
+ m_invResolution = Point2(
+ 1.0f / filmSize.x,
+ 1.0f / filmSize.y
+ );
+ }
+
+ /**
+ * Cartesian-to-spherical coordinate mapping with
+ * v=0 => Y=1
+ */
+ Vector squareToSphereY(Float u, Float v) const {
+ Float cosTheta = std::cos(v * M_PI),
+ sinTheta = std::sin(v * M_PI),//std::sqrt(1-cosTheta*cosTheta),
+ phi = u * 2 * M_PI,
+ cosPhi = std::cos(phi), sinPhi = std::sin(phi);
+ return Vector(
+ sinTheta * sinPhi, cosTheta, -sinTheta*cosPhi);
+ }
+
+ /* Corresponding reverse mapping */
+ Point2 sphereToSquareY(const Vector &d) const {
+ Float u = std::atan2(d.x,-d.z) * (0.5f * INV_PI),
+ v = std::acos(std::max((Float) -1.0f,
+ std::min((Float) 1.0f, d.y))) / M_PI;
+ if (u < 0)
+ u += 1;
+ return Point2(u, v);
+ }
+
+ void generateRay(const Point2 &dirSample, const Point2 &lensSample,
+ Float timeSample, Ray &ray) const {
+ ++cameraRays;
+
+ Float u = dirSample.x * m_invResolution.x,
+ v = dirSample.y * m_invResolution.y;
+
+ Vector direction = squareToSphereY(u, v);
+ Point2 uvPrime = sphereToSquareY(direction);
+
+ if ((std::abs(uvPrime.x-u) > Epsilon || std::abs(uvPrime.y-v)>Epsilon) && u < 1 && v < 1 && u > 0 && v > 0)
+ cout << uvPrime.toString() << " vs " << u << ", " << v << endl;
+
+ /* Construct ray in camera space */
+ Ray localRay(Point(0.0f), direction,
+ m_shutterOpen + m_shutterOpenTime * timeSample);
+
+ /* Transform into world space */
+ m_cameraToWorld(localRay, ray);
+ }
+
+ MTS_DECLARE_CLASS()
+private:
+ Point2 m_invResolution;
+};
+
+MTS_IMPLEMENT_CLASS_S(EnvironmentCamera, false, Camera)
+MTS_EXPORT_PLUGIN(EnvironmentCamera, "Environment camera");
+MTS_NAMESPACE_END
diff --git a/src/librender/camera.cpp b/src/librender/camera.cpp
index 5ff958a8..c70e406a 100644
--- a/src/librender/camera.cpp
+++ b/src/librender/camera.cpp
@@ -56,6 +56,23 @@ void Camera::setParent(ConfigurableObject *parent) {
// the camera subtree needs to be serialized by itself.
}
+void Camera::configure() {
+ if (m_film == NULL) {
+ /* Instantiate an EXR film by default */
+ m_film = static_cast (PluginManager::getInstance()->
+ createObject(MTS_CLASS(Film), Properties("exrfilm")));
+ m_film->configure();
+ }
+ if (m_sampler == NULL) {
+ /* No sampler has been selected - load an independent filter with 4 samples/pixel by default */
+ Properties props("independent");
+ props.setInteger("sampleCount", 4);
+ m_sampler = static_cast (PluginManager::getInstance()->
+ createObject(MTS_CLASS(Sampler), props));
+ m_sampler->configure();
+ }
+}
+
Point Camera::getPosition(const Point2 &sample) const {
return m_position; // default impl.
}
@@ -107,6 +124,22 @@ void Camera::addChild(const std::string &name, ConfigurableObject *child) {
}
}
+bool Camera::positionToSample(const Point &p, Point2 &sample) const {
+ Log(EError, "%s::positionToSample(): not implemented!",
+ getClass()->getName().c_str());
+ return false;
+}
+
+Float Camera::areaDensity(const Point2 &p) const {
+ Log(EError, "%s::areaDensity(): not implemented!",
+ getClass()->getName().c_str());
+ return 0.0f;
+}
+
+bool Camera::needsLensSample() const {
+ return false;
+}
+
ProjectiveCamera::ProjectiveCamera(Stream *stream, InstanceManager *manager)
: Camera(stream, manager) {
m_cameraToScreen = Transform(stream);
@@ -124,21 +157,7 @@ ProjectiveCamera::ProjectiveCamera(const Properties &props) : Camera(props) {
}
void ProjectiveCamera::configure() {
- if (m_film == NULL) {
- /* Instantiate an EXR film by default */
- m_film = static_cast (PluginManager::getInstance()->
- createObject(MTS_CLASS(Film), Properties("exrfilm")));
- m_film->configure();
- }
-
- if (m_sampler == NULL) {
- /* No sampler has been selected - load an independent filter with 4 samples/pixel by default */
- Properties props("independent");
- props.setInteger("sampleCount", 4);
- m_sampler = static_cast (PluginManager::getInstance()->
- createObject(MTS_CLASS(Sampler), props));
- m_sampler->configure();
- }
+ Camera::configure();
m_aspect = (Float) m_film->getSize().x / (Float) m_film->getSize().y;
}
diff --git a/src/librender/luminaire.cpp b/src/librender/luminaire.cpp
index 34bb0a99..9d9326e1 100644
--- a/src/librender/luminaire.cpp
+++ b/src/librender/luminaire.cpp
@@ -103,6 +103,61 @@ Luminaire *Luminaire::getElement(int i) {
return NULL;
}
+void Luminaire::sampleEmission(EmissionRecord &eRec,
+ const Point2& areaSample, const Point2 &dirSample) const {
+ Log(EError, "%s::areaDensity(): not implemented!",
+ getClass()->getName().c_str());
+}
+
+void Luminaire::sampleEmissionArea(EmissionRecord &lRec,
+ const Point2 &sample) const {
+ Log(EError, "%s::sampleEmissionArea(): not implemented!",
+ getClass()->getName().c_str());
+}
+
+Spectrum Luminaire::sampleEmissionDirection(EmissionRecord &lRec,
+ const Point2 &sample) const {
+ Log(EError, "%s::sampleEmissionDirection(): not implemented!",
+ getClass()->getName().c_str());
+ return Spectrum(0.0f);
+}
+
+void Luminaire::pdfEmission(EmissionRecord &eRec, bool delta) const {
+ Log(EError, "%s::pdfEmission(): not implemented!",
+ getClass()->getName().c_str());
+}
+
+Spectrum Luminaire::evalArea(const EmissionRecord &eRec) const {
+ Log(EError, "%s::evalArea(): not implemented!",
+ getClass()->getName().c_str());
+ return Spectrum(0.0f);
+}
+
+Spectrum Luminaire::evalDirection(const EmissionRecord &eRec) const {
+ Log(EError, "%s::evalDirection(): not implemented!",
+ getClass()->getName().c_str());
+ return Spectrum(0.0f);
+}
+
+void Luminaire::sample(const Point &p,
+ LuminaireSamplingRecord &lRec, const Point2 &sample) const {
+ Log(EError, "%s::sample(): not implemented!",
+ getClass()->getName().c_str());
+}
+
+Float Luminaire::pdf(const Point &p,
+ const LuminaireSamplingRecord &lRec, bool delta) const {
+ Log(EError, "%s::pdf(): not implemented!",
+ getClass()->getName().c_str());
+ return 0.0f;
+}
+
+Spectrum Luminaire::getPower() const {
+ Log(EError, "%s::getPower(): not implemented!",
+ getClass()->getName().c_str());
+ return Spectrum(0.0f);
+}
+
std::string EmissionRecord::toString() const {
std::ostringstream oss;
oss << "EmissionRecord[" << std::endl
diff --git a/src/luminaires/envmap.cpp b/src/luminaires/envmap.cpp
index aafc5a79..90f8884f 100644
--- a/src/luminaires/envmap.cpp
+++ b/src/luminaires/envmap.cpp
@@ -25,8 +25,6 @@
#include
#include
-//#define SAMPLE_UNIFORMLY 1
-
MTS_NAMESPACE_BEGIN
/**
@@ -39,10 +37,17 @@ class EnvMapLuminaire : public Luminaire {
public:
EnvMapLuminaire(const Properties &props) : Luminaire(props) {
m_intensityScale = props.getFloat("intensityScale", 1);
- m_path = Thread::getThread()->getFileResolver()->resolve(props.getString("filename"));
- Log(EInfo, "Loading environment map \"%s\"", m_path.leaf().c_str());
- ref is = new FileStream(m_path, FileStream::EReadOnly);
- ref bitmap = new Bitmap(Bitmap::EEXR, is);
+ ref bitmap;
+
+ if (props.hasProperty("bitmap")) {
+ bitmap = reinterpret_cast(props.getData("bitmap").ptr);
+ m_path = "";
+ } else {
+ m_path = Thread::getThread()->getFileResolver()->resolve(props.getString("filename"));
+ Log(EInfo, "Loading environment map \"%s\"", m_path.leaf().c_str());
+ ref is = new FileStream(m_path, FileStream::EReadOnly);
+ bitmap = new Bitmap(Bitmap::EEXR, is);
+ }
m_mipmap = MIPMap::fromBitmap(bitmap, MIPMap::ETrilinear,
MIPMap::ERepeat, 0.0f, Spectrum::EIlluminant);
@@ -97,9 +102,11 @@ public:
void configure() {
int mipMapLevel = std::min(3, m_mipmap->getLevels()-1);
m_pdfResolution = m_mipmap->getLevelResolution(mipMapLevel);
- m_pdfInvResolution = Vector2(1.0f / m_pdfResolution.x, 1.0f / m_pdfResolution.y);
+ m_pdfInvResolution = Vector2(1.0f / m_pdfResolution.x,
+ 1.0f / m_pdfResolution.y);
- Log(EDebug, "Creating a %ix%i sampling density", m_pdfResolution.x, m_pdfResolution.y);
+ Log(EDebug, "Creating a %ix%i sampling density",
+ m_pdfResolution.x, m_pdfResolution.y);
const Spectrum *coarseImage = m_mipmap->getImageData(mipMapLevel);
int index = 0;
m_pdf = DiscretePDF(m_pdfResolution.x * m_pdfResolution.y);
@@ -133,35 +140,45 @@ public:
return m_average * m_surfaceArea * M_PI;
}
+ /// Sample an emission direction
Vector sampleDirection(Point2 sample, Float &pdf, Spectrum &value) const {
-#if defined(SAMPLE_UNIFORMLY)
- pdf = 1.0f / (4*M_PI);
- Vector d = squareToSphere(sample);
- value = Le(-d);
- return d;
-#else
int idx = m_pdf.sampleReuse(sample.x, pdf);
int row = idx / m_pdfResolution.x;
int col = idx - m_pdfResolution.x * row;
Float x = col + sample.x, y = row + sample.y;
- value = m_mipmap->triangle(0, x * m_pdfInvResolution.x, y * m_pdfInvResolution.y)
- * m_intensityScale;
- Float theta = m_pdfPixelSize.y * y, phi = m_pdfPixelSize.x * x - M_PI;
- Float sinTheta = std::sin(theta), cosTheta = std::cos(theta);
- Float sinPhi = std::sin(phi), cosPhi = std::cos(phi);
+ value = m_mipmap->triangle(0, x * m_pdfInvResolution.x,
+ y * m_pdfInvResolution.y) * m_intensityScale;
+ Float theta = m_pdfPixelSize.y * y,
+ phi = m_pdfPixelSize.x * x;
+
+ /* Spherical-to-cartesian coordinate mapping with
+ theta=0 => Y=1 */
+ Float cosTheta = std::cos(theta),
+ sinTheta = std::sqrt(1-cosTheta*cosTheta),
+ cosPhi = std::cos(phi),
+ sinPhi = std::sin(phi);
+
+ Vector sampledDirection(sinTheta * sinPhi,
+ cosTheta, -sinTheta*cosPhi);
pdf = pdf / (m_pdfPixelSize.x * m_pdfPixelSize.y * sinTheta);
- return m_luminaireToWorld(Vector(
- -sinTheta * sinPhi, -cosTheta, sinTheta*cosPhi));
-#endif
+ return m_luminaireToWorld(-sampledDirection);
+ }
+
+ Point2 fromSphere(const Vector &d) const {
+ Float u = std::atan2(d.x,-d.z) * (0.5f * INV_PI),
+ v = std::acos(std::max((Float) -1.0f,
+ std::min((Float) 1.0f, d.y))) * INV_PI;
+ if (u < 0)
+ u += 1;
+ return Point2(u, v);
}
inline Spectrum Le(const Vector &direction) const {
- const Vector d = m_worldToLuminaire(direction);
- const Float u = .5f * (1 + std::atan2(d.x,-d.z) / M_PI);
- const Float v = std::acos(std::max((Float) -1.0f,
- std::min((Float) 1.0f, d.y))) / M_PI;
- return m_mipmap->triangle(0, u, v) * m_intensityScale;
+ Point2 uv = fromSphere(m_worldToLuminaire(direction));
+
+ return m_mipmap->triangle(0, uv.x, uv.y)
+ * m_intensityScale;
}
inline Spectrum Le(const Ray &ray) const {
@@ -183,21 +200,17 @@ public:
}
Float pdf(const Point &p, const LuminaireSamplingRecord &lRec, bool delta) const {
-#if defined(SAMPLE_UNIFORMLY)
- return 1.0f / (4*M_PI);
-#else
- const Vector d = m_worldToLuminaire(-lRec.d);
- const Float x = .5f * (1 + std::atan2(d.x,-d.z) / M_PI) * m_pdfResolution.x;
- const Float y = std::acos(std::max((Float) -1.0f, std::min((Float) 1.0f, d.y)))
- / M_PI * m_pdfResolution.y;
- int xPos = std::min(std::max((int) std::floor(x), 0), m_pdfResolution.x-1);
- int yPos = std::min(std::max((int) std::floor(y), 0), m_pdfResolution.y-1);
+ const Vector d = m_worldToLuminaire(-lRec.d);
+ Point2 xy = fromSphere(d);
+ xy.x *= m_pdfResolution.x;
+ xy.y *= m_pdfResolution.y;
+ int xPos = std::min(std::max((int) std::floor(xy.x), 0), m_pdfResolution.x-1);
+ int yPos = std::min(std::max((int) std::floor(xy.y), 0), m_pdfResolution.y-1);
Float pdf = m_pdf[xPos + yPos * m_pdfResolution.x];
Float sinTheta = std::sqrt(std::max((Float) Epsilon, 1-d.y*d.y));
return pdf / (m_pdfPixelSize.x * m_pdfPixelSize.y * sinTheta);
-#endif
}
/**
@@ -386,7 +399,9 @@ public:
<< endl
<< "vec3 " << evalName << "_background(vec3 wo) {" << endl
<< " vec3 d = normalize((" << evalName << "_worldToLuminaire * vec4(wo, 0.0)).xyz);" << endl
- << " float u = 0.5 * (1.0 + atan(d.x, -d.z) * 0.318309);" << endl
+ << " float u = atan(d.x, -d.z) * 0.15915;" << endl
+ << " if (u < 0.0)" << endl
+ << " u += 1.0;" << endl
<< " float v = acos(max(-1.0, min(1.0, d.y))) * 0.318309;" << endl
// The following is not very elegant, but necessary to trick GLSL
// into doing correct texture filtering across the u=0 to u=1 seam.
diff --git a/src/luminaires/sky.cpp b/src/luminaires/sky.cpp
index 5b801b6f..04c7bec2 100644
--- a/src/luminaires/sky.cpp
+++ b/src/luminaires/sky.cpp
@@ -17,9 +17,8 @@
*/
#include
-#include
#include
-#include
+#include
#define SAMPLE_UNIFORMLY 1
@@ -141,6 +140,15 @@ MTS_NAMESPACE_BEGIN
* and the moon dominate. The model also currently does not handle cloudy skies.
* The implementation in Mitsuba is based on code by Preetham et al. It was
* ported by Tom Kazimiers.
+ *
+ * \begin{xml}[caption={Rotating the sky luminaire for scenes that use $Z$ as
+ * the ``up'' direction}, label=lst:sky-up]
+ *
+ *
+ *
+ *
+ *
+ * \end{xml}
*/
class SkyLuminaire : public Luminaire {
public:
@@ -151,12 +159,6 @@ public:
*/
SkyLuminaire(const Properties &props)
: Luminaire(props) {
- /* Transformation from the luminaire's local coordinates to
- * world coordiantes */
- m_luminaireToWorld =
- props.getTransform("toWorld", Transform());
- m_worldToLuminaire = m_luminaireToWorld.inverse();
-
m_scale = props.getFloat("scale", Float(1.0));
m_turbidity = props.getFloat("turbidity", Float(3.0));
if (m_turbidity < 1 || m_turbidity > 30)
@@ -242,7 +244,6 @@ public:
m_zenithL = (4.0453f * m_turbidity - 4.9710f) * std::tan(chi)
- 0.2155f * m_turbidity + 2.4192f;
- cout << toString() << endl;
/* Evaluate quadratic polynomials to find the Perez sky
* model coefficients for the x, y and luminance components */
@@ -263,12 +264,20 @@ public:
m_perezY[2] = -0.00792f * m_turbidity + 0.21023f;
m_perezY[3] = -0.04405f * m_turbidity - 1.65369f;
m_perezY[4] = -0.01092f * m_turbidity + 0.05291f;
+ }
+
+ bool isCompound() const {
+ return true;
+ }
+ Luminaire *getElement(int i) {
+ if (i != 0)
+ return NULL;
int thetaBins = m_resolution, phiBins = m_resolution*2;
ref bitmap = new Bitmap(phiBins, thetaBins, 128);
- bitmap->clear();
Point2 factor(M_PI / thetaBins, (2*M_PI) / phiBins);
+ float *target = bitmap->getFloatData();
for (int i=0; igetFloatData()[(j+i*phiBins)*4 + 0] = r;
- bitmap->getFloatData()[(j+i*phiBins)*4 + 1] = g;
- bitmap->getFloatData()[(j+i*phiBins)*4 + 2] = b;
- bitmap->getFloatData()[(j+i*phiBins)*4 + 3] = 1;
+ *target++ = r; *target++ = g;
+ *target++ = b; *target++ = 1;
}
}
+
+ /* Instantiate a nested envmap plugin */
+ Properties props("envmap");
+ Properties::Data bitmapData;
+ bitmapData.ptr = (uint8_t *) bitmap.get();
+ bitmapData.size = sizeof(Bitmap);
+ props.setData("bitmap", bitmapData);
+ props.setTransform("toWorld", m_luminaireToWorld);
+ props.setFloat("samplingWeight", m_samplingWeight);
+ Luminaire *luminaire = static_cast(
+ PluginManager::getInstance()->createObject(
+ MTS_CLASS(Luminaire), props));
+ luminaire->configure();
+ return luminaire;
}
Vector toSphere(Float theta, Float phi) const {
/* Spherical-to-cartesian coordinate mapping with
theta=0 => Y=1 */
- Float cosTheta = std::cos(theta), sinTheta = std::sin(theta),
+ Float cosTheta = std::cos(theta),
+ sinTheta = std::sqrt(1-cosTheta*cosTheta),
cosPhi = std::cos(phi), sinPhi = std::sin(phi);
return m_luminaireToWorld(Vector(
sinTheta * sinPhi, cosTheta, -sinTheta*cosPhi));
@@ -340,149 +362,6 @@ public:
m_phiS = sunPos.y;
}
- void preprocess(const Scene *scene) {
- /* Get the scene's bounding sphere and slightly enlarge it */
- m_bsphere = scene->getBSphere();
- m_bsphere.radius *= 1.01f;
- }
-
- Spectrum getPower() const {
- /* TODO */
- return m_average * (M_PI * 4 * M_PI
- * m_bsphere.radius * m_bsphere.radius);
- }
-
- inline Spectrum Le(const Vector &direction) const {
- /* Compute sky light radiance for direction */
- Vector d = normalize(m_worldToLuminaire(direction));
- const Point2 sphCoords = fromSphere(d);
- return getSkySpectralRadiance(sphCoords.x, sphCoords.y) * m_scale;
- }
-
- inline Spectrum Le(const Ray &ray) const {
- return Le(normalize(ray.d));
- }
-
- Spectrum Le(const LuminaireSamplingRecord &lRec) const {
- return Le(-lRec.d);
- }
-
- inline void sample(const Point &p, LuminaireSamplingRecord &lRec,
- const Point2 &sample) const {
- lRec.d = sampleDirection(sample, lRec.pdf, lRec.value);
- lRec.sRec.p = p - lRec.d * (2 * m_bsphere.radius);
- }
-
- void sample(const Intersection &its, LuminaireSamplingRecord &lRec,
- const Point2 &sample) const {
- SkyLuminaire::sample(its.p, lRec, sample);
- }
-
- inline Float pdf(const Point &p, const LuminaireSamplingRecord &lRec, bool delta) const {
-#if defined(SAMPLE_UNIFORMLY)
- return 1.0f / (4 * M_PI);
-#endif
- }
-
- Float pdf(const Intersection &its, const LuminaireSamplingRecord &lRec, bool delta) const {
- return SkyLuminaire::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
- * through the scene.
- * For more detail, see "Using low-discrepancy sequences and
- * the Crofton formula to compute surface areas of geometric models"
- * by Li, X. and Wang, W. and Martin, R.R. and Bowyer, A.
- * (Computer-Aided Design vol 35, #9, pp. 771--782)
- */
- void sampleEmission(EmissionRecord &eRec,
- const Point2 &sample1, const Point2 &sample2) const {
- Assert(eRec.type == EmissionRecord::ENormal);
- /* Chord model - generate the ray passing through two uniformly
- distributed points on a sphere containing the scene */
- Vector d = squareToSphere(sample1);
- eRec.sRec.p = m_bsphere.center + d * m_bsphere.radius;
- eRec.sRec.n = Normal(-d);
- Point p2 = m_bsphere.center + squareToSphere(sample2) * m_bsphere.radius;
- eRec.d = p2 - eRec.sRec.p;
- Float length = eRec.d.length();
-
- if (length == 0) {
- eRec.value = Spectrum(0.0f);
- eRec.pdfArea = eRec.pdfDir = 1.0f;
- return;
- }
-
- eRec.d /= length;
- eRec.pdfArea = 1.0f / (4 * M_PI * m_bsphere.radius * m_bsphere.radius);
- eRec.pdfDir = INV_PI * dot(eRec.sRec.n, eRec.d);
- eRec.value = Le(-eRec.d);
- }
-
- void sampleEmissionArea(EmissionRecord &eRec, const Point2 &sample) const {
- if (eRec.type == EmissionRecord::ENormal) {
- Vector d = squareToSphere(sample);
- 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.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 */
- const Float radius = m_bsphere.radius * 1.5f;
- Vector d = squareToSphere(sample);
- eRec.sRec.p = m_bsphere.center + d * radius;
- eRec.sRec.n = Normal(-d);
- eRec.pdfArea = 1.0f / (4 * M_PI * radius * radius);
- eRec.value = Le(d) * M_PI;
- }
- }
-
- Spectrum sampleEmissionDirection(EmissionRecord &eRec, const Point2 &sample) const {
- Float radius = m_bsphere.radius;
- if (eRec.type == EmissionRecord::EPreview)
- radius *= 1.5f;
- Point p2 = m_bsphere.center + squareToSphere(sample) * radius;
- eRec.d = p2 - eRec.sRec.p;
- Float length = eRec.d.length();
-
- if (length == 0.0f) {
- eRec.pdfDir = 1.0f;
- return Spectrum(0.0f);
- }
-
- eRec.d /= length;
- eRec.pdfDir = INV_PI * dot(eRec.sRec.n, eRec.d);
- if (eRec.type == EmissionRecord::ENormal)
- return Le(-eRec.d) * INV_PI;
- else
- return Spectrum(INV_PI);
- }
-
- Spectrum evalDirection(const EmissionRecord &eRec) const {
- if (eRec.type == EmissionRecord::ENormal)
- return Le(-eRec.d) * INV_PI;
- else
- return Spectrum(INV_PI);
- }
-
- Spectrum evalArea(const EmissionRecord &eRec) const {
- Assert(eRec.type == EmissionRecord::ENormal);
- return Spectrum(M_PI);
- }
-
- Spectrum f(const EmissionRecord &eRec) const {
- if (eRec.type == EmissionRecord::ENormal)
- return Le(-eRec.d) * INV_PI;
- else
- return Spectrum(INV_PI);
- }
-
- void pdfEmission(EmissionRecord &eRec, bool delta) const {
- }
-
std::string toString() const {
std::ostringstream oss;
oss << "SkyLuminaire[" << endl
@@ -493,20 +372,6 @@ public:
<< "]";
return oss.str();
}
-
- bool isBackgroundLuminaire() const {
- return true;
- }
-
- Vector sampleDirection(Point2 sample, Float &pdf, Spectrum &value) const {
-#if defined(SAMPLE_UNIFORMLY)
- pdf = 1.0f / (4*M_PI);
- Vector d = squareToSphere(sample);
- value = Le(-d);
- return d;
-#endif
- }
-
private:
/**
* Calculates the angle between two spherical cooridnates. All
@@ -586,9 +451,9 @@ private:
MTS_DECLARE_CLASS()
protected:
- Spectrum m_average;
- BSphere m_bsphere;
+ /* Environment map resolution */
int m_resolution;
+ /* Constant scale factor applied to the model */
Float m_scale;
/* The turbidity of the sky ranges normally from 1 to 30.
For clear skies values in range [2,6] are useful. */
@@ -606,6 +471,6 @@ protected:
};
MTS_IMPLEMENT_CLASS_S(SkyLuminaire, false, Luminaire)
-MTS_EXPORT_PLUGIN(SkyLuminaire, "Sky luminaire");
+MTS_EXPORT_PLUGIN(SkyLuminaire, "Preetham sky luminaire");
MTS_NAMESPACE_END
diff --git a/src/tests/test_chisquare.cpp b/src/tests/test_chisquare.cpp
index 02666295..8709571d 100644
--- a/src/tests/test_chisquare.cpp
+++ b/src/tests/test_chisquare.cpp
@@ -554,13 +554,13 @@ public:
LuminaireAdapter adapter(luminaire, sampler);
ref chiSqr = new ChiSquare(thetaBins, 2*thetaBins, 1);
chiSqr->setLogLevel(EDebug);
- chiSqr->dumpTables("test.m");
// Initialize the tables used by the chi-square test
chiSqr->fill(
boost::bind(&LuminaireAdapter::generateSample, &adapter),
boost::bind(&LuminaireAdapter::pdf, &adapter, _1, _2)
);
+ chiSqr->dumpTables("test.m");
// (the following assumes that the distribution has 1 parameter, e.g. exponent value)
ChiSquare::ETestResult result = chiSqr->runTest(SIGNIFICANCE_LEVEL);