diff --git a/doc/images/preetham_06.jpg b/doc/images/preetham_06.jpg new file mode 100644 index 00000000..603fc7da Binary files /dev/null and b/doc/images/preetham_06.jpg differ diff --git a/doc/images/preetham_08.jpg b/doc/images/preetham_08.jpg new file mode 100644 index 00000000..f7a4fdb8 Binary files /dev/null and b/doc/images/preetham_08.jpg differ diff --git a/doc/images/preetham_10.jpg b/doc/images/preetham_10.jpg new file mode 100644 index 00000000..9937a577 Binary files /dev/null and b/doc/images/preetham_10.jpg differ diff --git a/doc/images/preetham_12.jpg b/doc/images/preetham_12.jpg new file mode 100644 index 00000000..b36e77ca Binary files /dev/null and b/doc/images/preetham_12.jpg differ diff --git a/doc/images/preetham_14.jpg b/doc/images/preetham_14.jpg new file mode 100644 index 00000000..058125a8 Binary files /dev/null and b/doc/images/preetham_14.jpg differ diff --git a/doc/images/preetham_16.jpg b/doc/images/preetham_16.jpg new file mode 100644 index 00000000..12811697 Binary files /dev/null and b/doc/images/preetham_16.jpg differ diff --git a/doc/images/preetham_18.jpg b/doc/images/preetham_18.jpg new file mode 100644 index 00000000..1cf3fddf Binary files /dev/null and b/doc/images/preetham_18.jpg differ diff --git a/doc/images/preetham_20.jpg b/doc/images/preetham_20.jpg new file mode 100644 index 00000000..9c9ef530 Binary files /dev/null and b/doc/images/preetham_20.jpg differ diff --git a/doc/images/preetham_turb_2.jpg b/doc/images/preetham_turb_2.jpg new file mode 100644 index 00000000..3fe9a53e Binary files /dev/null and b/doc/images/preetham_turb_2.jpg differ diff --git a/doc/images/preetham_turb_3.jpg b/doc/images/preetham_turb_3.jpg new file mode 100644 index 00000000..868197eb Binary files /dev/null and b/doc/images/preetham_turb_3.jpg differ diff --git a/doc/images/preetham_turb_4.jpg b/doc/images/preetham_turb_4.jpg new file mode 100644 index 00000000..a7ba6b22 Binary files /dev/null and b/doc/images/preetham_turb_4.jpg differ diff --git a/doc/images/preetham_turb_5.jpg b/doc/images/preetham_turb_5.jpg new file mode 100644 index 00000000..0888a18c Binary files /dev/null and b/doc/images/preetham_turb_5.jpg differ diff --git a/doc/images/preetham_turb_6.jpg b/doc/images/preetham_turb_6.jpg new file mode 100644 index 00000000..f784bf4f Binary files /dev/null and b/doc/images/preetham_turb_6.jpg differ diff --git a/doc/images/preetham_turb_7.jpg b/doc/images/preetham_turb_7.jpg new file mode 100644 index 00000000..a087d7d5 Binary files /dev/null and b/doc/images/preetham_turb_7.jpg differ diff --git a/doc/images/preetham_turb_8.jpg b/doc/images/preetham_turb_8.jpg new file mode 100644 index 00000000..757f7318 Binary files /dev/null and b/doc/images/preetham_turb_8.jpg differ diff --git a/doc/images/preetham_turb_9.jpg b/doc/images/preetham_turb_9.jpg new file mode 100644 index 00000000..e00da95a Binary files /dev/null and b/doc/images/preetham_turb_9.jpg differ diff --git a/doc/macros.sty b/doc/macros.sty index 892b9081..50e8e495 100644 --- a/doc/macros.sty +++ b/doc/macros.sty @@ -67,6 +67,7 @@ \newcommand{\medrendering}[2]{ \subfloat[#1]{\fbox{\includegraphics[width=0.3\textwidth]{images/#2}}}\hfill} \newcommand{\unframedmedrendering}[2]{\subfloat[#1]{\includegraphics[width=0.3\textwidth]{images/#2}}\hfill} \newcommand{\smallrendering}[2]{ \subfloat[#1]{\fbox{\includegraphics[width=0.2\textwidth]{images/#2}}}\hfill} +\newcommand{\tinyrendering}[2]{ \subfloat[#1]{\includegraphics[width=0.125\textwidth]{images/#2}}} \newcommand{\parameter}[3]{ \otoprule diff --git a/src/librender/scene.cpp b/src/librender/scene.cpp index dbaeed8b..af577b72 100644 --- a/src/librender/scene.cpp +++ b/src/librender/scene.cpp @@ -282,11 +282,11 @@ void Scene::initialize() { if (!m_luminairePDF.isReady()) { if (m_luminaires.size() == 0) { - Log(EWarn, "No luminaires found -- adding a constant environment source"); - Properties constantProps("constant"); - constantProps.setSpectrum("intensity", Spectrum(0.9f)); + Log(EWarn, "No luminaires found -- adding a sky luminaire"); + Properties skyPropsProps("sky"); + skyPropsProps.setFloat("scale", 0.03f); ref luminaire = static_cast( - PluginManager::getInstance()->createObject(MTS_CLASS(Luminaire), constantProps)); + PluginManager::getInstance()->createObject(MTS_CLASS(Luminaire), skyPropsProps)); addChild("", luminaire); luminaire->configure(); } diff --git a/src/luminaires/sky.cpp b/src/luminaires/sky.cpp index 1c2b7073..ec4fd48a 100644 --- a/src/luminaires/sky.cpp +++ b/src/luminaires/sky.cpp @@ -32,10 +32,10 @@ MTS_NAMESPACE_BEGIN * `haze') in the atmosphere. Smaller values ($\sim 2$) produce a * clear blue sky, larger values ($\sim 8$) lead to an overcast sky, * and a very high values ($\sim 20$) cause a color shift towards - * orange and red. \default{2} + * orange and red. \default{3} * } * \parameter{day}{\Integer}{Solar day used to compute the sun's position. - * Must be in the range 1 to 365. \default{180}} + * Must be in the range between 1 and 365. \default{180}} * \parameter{time}{\Float}{Fractional time used to compute the sun's * position. A time of 4:15 PM corresponds to 16.25. \default{15.00}} * \parameter{latitude, longitude}{\Float}{ @@ -48,10 +48,10 @@ MTS_NAMESPACE_BEGIN * the sun's position \default{135 --- Japan standard time} * } * \parameter{sunDirection}{\Vector}{Allows to manually - * override the sun direction. In that case, all parameters - * pertaining to the computation of this direction (\code{day, - * time, latitude, longitude,} and \code{standardMeridian}) - * become unnecessary. + * override the sun direction in world space. When this value + * is provided, parameters pertaining to the computation + * of the sun direction (\code{day, time, latitude, longitude,} + * and \code{standardMeridian}) are unnecessary. \default{none} * } * \parameter{extend}{\Boolean}{ * Extend luminaire below the horizon? \default{\code{false}} @@ -69,10 +69,17 @@ MTS_NAMESPACE_BEGIN * } * * \renderings{ - * \rendering{Homogeneous reflectance, see \lstref{diffuse-uniform}} - * {bsdf_diffuse_plain} - * \rendering{Textured reflectance, see \lstref{diffuse-textured}} - * {bsdf_diffuse_textured} + * \tinyrendering{6AM}{preetham_06} + * \tinyrendering{8AM}{preetham_08} + * \tinyrendering{10AM}{preetham_10} + * \tinyrendering{12PM}{preetham_12} + * \tinyrendering{2PM}{preetham_14} + * \tinyrendering{4PM}{preetham_16} + * \tinyrendering{6PM}{preetham_18} + * \tinyrendering{8PM}{preetham_20}\hfill + * \vspace{-3mm} + * \caption{Time series with the default settings (visualized by + * projecting the sky onto a disk)} * } * * This plugin implements the physically-based skylight model proposed by @@ -82,26 +89,45 @@ MTS_NAMESPACE_BEGIN * of the earth. * * Numerous parameters allow changing the both the position on Earth, as - * well as the time of observation. These are used to compute the sun's - * position which, together with \code{turbidity}, constitutes the main + * well as the time of observation. These are used to compute the sun + * direction which, together with \code{turbidity}, constitutes the main * parameter of the model. If desired, the sun direction can also be - * specified explicitly. + * specified manually. + * + * \renderings{ + * \tinyrendering{2}{preetham_turb_2} + * \tinyrendering{3}{preetham_turb_3} + * \tinyrendering{4}{preetham_turb_4} + * \tinyrendering{5}{preetham_turb_5} + * \tinyrendering{6}{preetham_turb_6} + * \tinyrendering{7}{preetham_turb_7} + * \tinyrendering{8}{preetham_turb_8} + * \tinyrendering{9}{preetham_turb_9} + * \vspace{-3mm} + * \caption{Sky light for different turbidity values (fixed time \& location)} + * } * * \emph{Turbidity}, the other important parameter, specifies the amount of * atmospheric extinction due to larger particles ($t_l$), as opposed to * molecules ($t_m$). Lower values correspond to a clear sky, and higher values * produce illumination resembling that of a hazy, overcast sky. Formally, - * the turbidity is defined as the ratio $T=\frac{t_m+t_l}{t_m}$. - * Values between 1 and 30 are possible, though the model will be most - * accurate for values between 2 and 6, to which it was fit using numerical - * optimization. + * the turbidity is defined as the ratio between the combined extinction + * cross-section and the cross-section only due to molecules, i.e. + * $T=\frac{t_m+t_l}{t_m}$. Values between 1 and 30 are possible, though + * the model will be most accurate for values between 2 and 6, to which + * it was fit using numerical optimization. + + * The default coordinate system of the luminaire associates the up + * direction with the $+Y$ axis. The east direction is associated with $+X$ + * and the north direction is equal to $+Z$. To change this coordinate + * system, rotations can be applied using the \code{toWorld} parameter. * * By default, the luminaire will not emit any light below the * horizon, which means that these regions will be black when they - * are observed directly. By setting the \code{extend=true}, - * the emitted radiance at the horizon will be extended to the entire - * bottom hemisphere. Note that this will significantly increase - * the amount of illumination present in the scene. + * are observed directly. By setting the \code{extend} parameter to + * \code{true}, the emitted radiance at the horizon will be extended to + * the entire bottom hemisphere. Note that this will significantly + * increase the amount of illumination present in the scene. * * For performance reasons, the implementation precomputes an environment * map of the entire sky that is then forwarded to the \pluginref{envmap} @@ -114,7 +140,7 @@ MTS_NAMESPACE_BEGIN * it does not extend to the night sky, where illumination from stars, galaxies, * 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 and modified by Tom Kazimiers. + * ported by Tom Kazimiers. */ class SkyLuminaire : public Luminaire { public: @@ -127,12 +153,12 @@ public: : Luminaire(props) { /* Transformation from the luminaire's local coordinates to * world coordiantes */ - m_luminaireToWorld = Transform::rotate(Vector(1, 0, 0),-90) - * props.getTransform("toWorld", Transform()); + 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(2.0)); + m_turbidity = props.getFloat("turbidity", Float(3.0)); if (m_turbidity < 1 || m_turbidity > 30) Log(EError, "The turbidity parameter must be in the range [1,30]!"); @@ -148,8 +174,8 @@ public: Log(EError, "Both the 'sunDirection' parameter and time/location " "information were provided -- only one of them can be specified at a time!"); - configureSunPosition(m_worldToLuminaire( - props.getVector("sunDirection"))); + configureSunPosition( + props.getVector("sunDirection")); } else { Float lat = props.getFloat("latitude", 35.6894f); Float lon = props.getFloat("longitude", 139.6917f); @@ -239,14 +265,13 @@ public: m_perezY[4] = -0.01092f * m_turbidity + 0.05291f; int thetaBins = m_resolution, phiBins = m_resolution*2; + +#if 0 ref bitmap = new Bitmap(phiBins, thetaBins, 128); bitmap->clear(); - Point2 factor(M_PI / thetaBins, (2*M_PI) / phiBins); for (int i=0; igetFloatData()[(j+i*phiBins)*4 + 3] = 1; } } +#else + ref bitmap = new Bitmap(512, 512, 128); + bitmap->clear(); + for (int x=0; x<512; ++x) { + for (int y=0; y<512; ++y) { + Vector d; + d.x = -1.0f + (x + 0.5f) * 2.0f/512.0f; + d.z = -1.0f + (y + 0.5f) * 2.0f/512.0f; + Float tmp = 1-d.x*d.x - d.z*d.z; + Spectrum s(0.0f); + if (tmp > 0) { + d.y = std::sqrt(tmp); + s = Le(d); + } + Float r, g, b; + s.toLinearRGB(r, g, b); + bitmap->getFloatData()[(x+y*512)*4 + 0] = r; + bitmap->getFloatData()[(x+y*512)*4 + 1] = g; + bitmap->getFloatData()[(x+y*512)*4 + 2] = b; + bitmap->getFloatData()[(x+y*512)*4 + 3] = 1; + } + } +#endif ref fs = new FileStream("out.exr", FileStream::ETruncReadWrite); bitmap->save(Bitmap::EEXR, fs); } + 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), + cosPhi = std::cos(phi), sinPhi = std::sin(phi); + return m_luminaireToWorld(Vector( + sinTheta * sinPhi, cosTheta, -sinTheta*cosPhi)); + } + + Point2 fromSphere(const Vector &d) const { + Float theta = std::acos(std::max((Float) -1.0f, + std::min((Float) 1.0f, d.y))); + Float phi = std::atan2(d.x,-d.z); + if (phi < 0) + phi += 2*M_PI; + return Point2(theta, phi); + } + /** * Configures the position of the sun. This calculation is based on * your position on the world and time of day. @@ -294,13 +360,8 @@ public: m_thetaS = M_PI / 2.0f - solarAltitude; } - /** - * Configures the position of the sun by using a vector that points - * to the sun. It is expected, that +x = south, +y = east, +z = up. - */ void configureSunPosition(const Vector& sunDir) { - Vector wh = normalize(sunDir); - Point2 sunPos = toSphericalCoordinates(wh); + Point2 sunPos = fromSphere(normalize(m_luminaireToWorld(sunDir))); m_thetaS = sunPos.x; m_phiS = sunPos.y; } @@ -309,8 +370,6 @@ public: /* Get the scene's bounding sphere and slightly enlarge it */ m_bsphere = scene->getBSphere(); m_bsphere.radius *= 1.01f; - m_surfaceArea = m_bsphere.radius * m_bsphere.radius * M_PI; - m_invSurfaceArea = 1/m_surfaceArea; } Spectrum getPower() const { @@ -322,15 +381,7 @@ public: inline Spectrum Le(const Vector &direction) const { /* Compute sky light radiance for direction */ Vector d = normalize(m_worldToLuminaire(direction)); - - if (!m_extend && Frame::cosTheta(d) <= 0) { - /* If the viewing direction is below the - horizon, return black */ - return Spectrum(0.0f); - } - - const Point2 sphCoords = toSphericalCoordinates(d); - + const Point2 sphCoords = fromSphere(d); return getSkySpectralRadiance(sphCoords.x, sphCoords.y) * m_scale; } @@ -456,13 +507,6 @@ public: } void pdfEmission(EmissionRecord &eRec, bool delta) const { - Assert(eRec.type == EmissionRecord::ENormal); - Float dp = dot(eRec.sRec.n, eRec.d); - if (dp > 0) - eRec.pdfDir = delta ? 0.0f : INV_PI * dp; - else - eRec.pdfDir = 0; - eRec.pdfArea = delta ? 0.0f : m_invSurfaceArea; } std::string toString() const { @@ -535,6 +579,8 @@ private: * Calculates the spectral radiance of the sky in the specified directiono. */ Spectrum getSkySpectralRadiance(Float theta, Float phi) const { + if (!m_extend && std::cos(theta) <= 0) + return Spectrum(0.0f); /* Clip directions that are extremely close to grazing (for numerical * stability) or entirely below the horizon. This effectively extends * the horizon luminance value to the bottom hemisphere */ @@ -557,7 +603,7 @@ private: /* Create spectrum from XYZ values */ Spectrum dstSpect; - dstSpect.fromXYZ(X, Y, Z); + dstSpect.fromXYZ(X, Y, Z, Spectrum::EIlluminant); /* The produced spectrum might contain out-of-gamut colors. * The common solution is to clamp resulting values to zero. */ dstSpect.clampNegative(); @@ -567,8 +613,6 @@ private: MTS_DECLARE_CLASS() protected: Spectrum m_average; - Float m_surfaceArea; - Float m_invSurfaceArea; BSphere m_bsphere; int m_resolution; Float m_scale; diff --git a/src/qtgui/upgrade.cpp b/src/qtgui/upgrade.cpp index 9e7f92de..d8ac4bde 100644 --- a/src/qtgui/upgrade.cpp +++ b/src/qtgui/upgrade.cpp @@ -66,7 +66,7 @@ UpgradeManager::UpgradeManager(const FileResolver *resolver) : m_resolver(resolv fs::path file = *it; std::string extension = file.extension().string(); if (boost::to_lower_copy(extension) != ".xsl" || - !boost::starts_with(extension, "upgrade_")) + !boost::starts_with(file.filename().string(), "upgrade_")) continue; std::string filename = file.filename().string(); Version version(filename.substr(8, filename.length()-12)); diff --git a/src/utils/tonemap.cpp b/src/utils/tonemap.cpp index 5350c5a2..14858628 100644 --- a/src/utils/tonemap.cpp +++ b/src/utils/tonemap.cpp @@ -31,13 +31,14 @@ class Tonemap : public Utility { public: void help() { cout << endl; - cout << "Synopsis: Loads in one or more linear EXR images and writes out tonemapped 8-bit PNGs"; + cout << "Synopsis: Loads one or more linear EXR images and writes tonemapped 8-bit PNG/JPGs"; cout << endl; cout << "Usage: mtsutil tonemap [options] " << endl; cout << "Options/Arguments:" << endl; cout << " -h Display this help text" << endl << endl; cout << " -g gamma Specify the gamma value (The default is -1 => sRGB)" << endl << endl; cout << " -m multiplier Multiply the pixel values by 'multiplier' (Default = 1)" << endl << endl; + cout << " -f fmt Specifies the output format (png/jpg, default:png)" << endl << endl; } inline float toSRGB(float value) { @@ -52,9 +53,10 @@ public: char optchar, *end_ptr = NULL; optind = 1; Float gamma = -1, multiplier = 1; + Bitmap::EFileFormat format = Bitmap::EPNG; /* Parse command-line arguments */ - while ((optchar = getopt(argc, argv, "hg:m:")) != -1) { + while ((optchar = getopt(argc, argv, "hg:m:f:")) != -1) { switch (optchar) { case 'h': { help(); @@ -66,6 +68,16 @@ public: if (*end_ptr != '\0') SLog(EError, "Could not parse the gamma value!"); break; + case 'f': { + std::string fmt = optarg; + if (fmt == "png") + format = Bitmap::EPNG; + else if (fmt == "jpg" || fmt == "jpeg") + format = Bitmap::EJPEG; + else + SLog(EError, "Unknown format! (must be png/jpg)"); + } + break; case 'm': multiplier = (Float) strtod(optarg, &end_ptr); if (*end_ptr != '\0') @@ -83,8 +95,6 @@ public: for (int i=optind; iresolve(argv[i]); - fs::path outputFile = inputFile; - outputFile.replace_extension(".png"); Log(EInfo, "Loading EXR image \"%s\" ..", inputFile.string().c_str()); ref is = new FileStream(inputFile, FileStream::EReadOnly); ref input = new Bitmap(Bitmap::EEXR, is); @@ -104,9 +114,20 @@ public: } } - Log(EInfo, "Writing tonemapped PNG image \"%s\" ..", outputFile.string().c_str()); + fs::path outputFile = inputFile; + if (format == Bitmap::EPNG) { + outputFile.replace_extension(".png"); + Log(EInfo, "Writing tonemapped PNG image \"%s\" ..", outputFile.string().c_str()); + ref os = new FileStream(outputFile, FileStream::ETruncReadWrite); + } else if (format == Bitmap::EJPEG) { + outputFile.replace_extension(".jpg"); + Log(EInfo, "Writing tonemapped JPEG image \"%s\" ..", outputFile.string().c_str()); + } else { + Log(EError, "Unknown format!"); + } + ref os = new FileStream(outputFile, FileStream::ETruncReadWrite); - output->save(Bitmap::EPNG, os); + output->save(format, os); } return 0; }