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