diff --git a/doc/format.tex b/doc/format.tex index d4c65f1e..6a990ec9 100644 --- a/doc/format.tex +++ b/doc/format.tex @@ -10,12 +10,16 @@ An simple scene with a single mesh and the default lighting and camera setup mig something like this: \begin{xml} - + \end{xml} +The scene version attribute denotes the release of Mitsuba that was used to +create the scene. This information allows Mitsuba to always correctly process the +file irregardless of any potential future changes in the scene description language. + This example already contains the most important things to know about format: you can have \emph{objects} (such as the objects instantiated by the \code{scene} or \code{shape} tags), which are allowed to be nested within each other. Each object optionally accepts \emph{properties} (such as the \code{string} tag), @@ -29,7 +33,7 @@ the certainly case for the plugin named \code{obj} (it contains a WaveFront OBJ Similarly, you could write \begin{xml} - + @@ -43,7 +47,7 @@ The most common scene setup is to declare an integrator, some geometry, a camera and one or more luminaires. Here is a more complex example: \begin{xml} - + @@ -202,8 +206,8 @@ Quite often, you will find yourself using an object (such as a material) in many to declare it over and over again, which wastes memory, you can make use of references. Here is an example of how this works: \begin{xml} - - + + diff --git a/doc/images/bsdf_roughdielectric_beckmann_0_1.jpg b/doc/images/bsdf_roughdielectric_beckmann_0_1.jpg new file mode 100644 index 00000000..647b50f2 Binary files /dev/null and b/doc/images/bsdf_roughdielectric_beckmann_0_1.jpg differ diff --git a/doc/images/bsdf_roughdielectric_ggx_0_304.jpg b/doc/images/bsdf_roughdielectric_ggx_0_304.jpg new file mode 100644 index 00000000..9e06825d Binary files /dev/null and b/doc/images/bsdf_roughdielectric_ggx_0_304.jpg differ diff --git a/doc/macros.sty b/doc/macros.sty index 5ade0651..50e3c962 100644 --- a/doc/macros.sty +++ b/doc/macros.sty @@ -44,13 +44,14 @@ \newcommand{\renderings}[1]{ \begin{figure}[h!] + \setcounter{subfigure}{0} \centering \hfill #1 \end{figure} } -\newcommand{\rendering}[2]{\subfigure[#1]{\fbox{\includegraphics[width=0.4\textwidth]{images/#2}}}\hfill} +\newcommand{\rendering}[2]{\subfigure[#1]{\fbox{\includegraphics[width=0.47\textwidth]{images/#2}}}\hfill} \newcommand{\medrendering}[2]{ \subfigure[#1]{\fbox{\includegraphics[width=0.3\textwidth]{images/#2}}}\hfill} \newcommand{\smallrendering}[2]{ \subfigure[#1]{\fbox{\includegraphics[width=0.2\textwidth]{images/#2}}}\hfill} diff --git a/doc/section_bsdf.tex b/doc/section_bsdf.tex index 2754ae95..dccf24b2 100644 --- a/doc/section_bsdf.tex +++ b/doc/section_bsdf.tex @@ -73,7 +73,7 @@ either be performed by nesting BSDFs within shapes, or they can be named and then later referenced by their name. The following fragment shows an example of both kinds of usages: \begin{xml} - + diff --git a/src/bsdfs/difftrans.cpp b/src/bsdfs/difftrans.cpp index bed47f07..b74b827f 100644 --- a/src/bsdfs/difftrans.cpp +++ b/src/bsdfs/difftrans.cpp @@ -53,8 +53,8 @@ MTS_NAMESPACE_BEGIN * \end{xml} * \begin{xml}[caption=Lambertian material with a texture map, label=lst:lambertian-textured] * - * - * + * + * * * * \end{xml} diff --git a/src/bsdfs/lambertian.cpp b/src/bsdfs/lambertian.cpp index 07691e01..996a4e56 100644 --- a/src/bsdfs/lambertian.cpp +++ b/src/bsdfs/lambertian.cpp @@ -57,8 +57,8 @@ MTS_NAMESPACE_BEGIN * * \begin{xml}[caption=Lambertian material with a texture map, label=lst:lambertian-textured] * - * - * + * + * * * * \end{xml} diff --git a/src/bsdfs/roughdielectric.cpp b/src/bsdfs/roughdielectric.cpp index f59aaf0c..ab41a136 100644 --- a/src/bsdfs/roughdielectric.cpp +++ b/src/bsdfs/roughdielectric.cpp @@ -30,7 +30,7 @@ MTS_NAMESPACE_BEGIN * used to model the surface roughness. * \begin{enumerate}[(i)] * \item \code{beckmann}: Physically-based distribution derived from - * Gaussian random surfaces. This is the default choice. + * Gaussian random surfaces. This is the default. * \item \code{phong}: Classical $\cos^p\theta$ distribution. * The Phong exponent $p$ is obtained using a transformation that * produces roughness similar to a Beckmann distribution of the same @@ -55,35 +55,62 @@ MTS_NAMESPACE_BEGIN * \parameter{extIOR}{\Float}{Exterior index of refraction \default{1.0}} * \parameter{specular\showbreak Reflectance}{\Spectrum\Or\Texture}{Optional * factor used to modulate the reflectance component\default{1.0}} - * \parameter{specular\showbreak Transmittance}{\Spectrum\Or\Texture}{Optional + * \lastparameter{specular\showbreak Transmittance}{\Spectrum\Or\Texture}{Optional * factor used to modulate the transmittance component\default{1.0}} * } * - * \renderings{ - * \medrendering{Beckmann, $\alpha$=0.2}{bsdf_dielectric_glass} - * \medrendering{Beckmann, $\alpha$=0.3}{bsdf_dielectric_glass} - * \medrendering{Beckmann, $\alpha$=0.4}{bsdf_dielectric_glass} - * } * * This plugin implements a realistic microfacet scattering model for rendering - * rough interfaces between dielectric materials, such as a transition from air to ground glass. - * Microfacet theory describes surfaces as an arrangement of unresolved and ideally specular - * facets, whose normals are given by a specially chosen \emph{microfacet - * distribution}. By accounting for shadowing and masking effects between - * these facets, it is possible to reproduce the off-specular reflections - * peaks observed in real-world measurements of such materials. + * rough interfaces between dielectric materials, such as a transition from air to + * ground glass. Microfacet theory describes rough surfaces as an arrangement of + * unresolved and ideally specular facets, whose normal directions are given by + * a specially chosen \emph{microfacet distribution}. By accounting for shadowing + * and masking effects between these facets, it is possible to reproduce the + * off-specular reflections peaks observed in real-world measurements of such + * materials. + * \renderings{ + * \rendering{Rough glass (Beckmann, $\alpha$=0.1)}{bsdf_roughdielectric_beckmann_0_1.jpg} + * \rendering{Ground glass (GGX, $\alpha$=0.304, \lstref{roughdielectric-roughglass})}{bsdf_roughdielectric_ggx_0_304.jpg} + * } * * This plugin is essentially the ``roughened'' equivalent of the plugin - * \pluginref{dielectric}. Its implementation is based on the paper - * ``Microfacet Models for Refraction through Rough Surfaces'' - * \cite{Walter07Microfacet}. The model supports several types of microfacet - * distributions and a texturable roughness. The default settings are set + * \pluginref{dielectric}. As the roughness value is decreased, it increasingly + * approximates that model. Its implementation is based on the paper + * ``Microfacet Models for Refraction through Rough Surfaces'' by Walter et + * al. \cite{Walter07Microfacet}. The model supports several types of microfacet + * distributions and has a texturable roughness parameter. + * The default settings are set * to a borosilicate glass BK7/air interface with a light amount of rougness * modeled using a Beckmann distribution. * * When using this plugin, it is crucial that the scene contains - * meaningful and mutally compatible index of refraction change -- see - * \figref{glass-explanation} for an example. + * meaningful and mutally compatible index of refraction change---see + * \figref{glass-explanation} for an example. Also, please note that + * the importance sampling implementation of this model is close, but + * not perfect a perfect match to the underlying scattering distribution, + * particularly for high roughness values and when the \texttt{GGX} + * model is used. Hence, such renderings may converge slowly. + * + * \begin{xml}[caption=Ground glass, label=lst:roughdielectric-roughglass] + * + * + * + * + * + * + * \end{xml} + * + * \begin{xml}[caption=Textured rougness, label=lst:roughdielectric-textured] + * + * + * + * + * + * + * + * + * + * \end{xml} */ class RoughDielectric : public BSDF { public: @@ -384,7 +411,8 @@ public: } /* Evaluate the roughness */ - const Float alpha = m_alpha->getValue(bRec.its).average(); + const Float alpha = + std::max(m_alpha->getValue(bRec.its).average(), (Float) 1e-4f); /* Microsurface normal distribution */ const Float D = evalD(H, alpha); @@ -467,7 +495,8 @@ public: } /* Evaluate the roughness */ - Float alpha = m_alpha->getValue(bRec.its).average(); + Float alpha = + std::max(m_alpha->getValue(bRec.its).average(), (Float) 1e-4f); /* Suggestion by Bruce Walter: sample using a slightly different value of alpha. This in practice limits the weights to @@ -549,7 +578,8 @@ public: } /* Evaluate the roughness */ - Float alpha = m_alpha->getValue(bRec.its).average(); + Float alpha = + std::max(m_alpha->getValue(bRec.its).average(), (Float) 1e-4f); /* Suggestion by Bruce Walter: sample using a slightly different value of alpha. This in practice limits the weights to @@ -671,7 +701,8 @@ public: } /* Evaluate the roughness */ - Float alpha = m_alpha->getValue(bRec.its).average(); + Float alpha = + std::max(m_alpha->getValue(bRec.its).average(), (Float) 1e-4f); /* Suggestion by Bruce Walter: sample using a slightly different value of alpha. This in practice limits the weights to diff --git a/src/textures/SConscript b/src/textures/SConscript index 62c34064..7ea1f828 100644 --- a/src/textures/SConscript +++ b/src/textures/SConscript @@ -1,7 +1,6 @@ Import('env', 'plugins') -plugins += env.SharedLibrary('exrtexture', ['exrtexture.cpp']) -plugins += env.SharedLibrary('ldrtexture', ['ldrtexture.cpp']) +plugins += env.SharedLibrary('bitmap', ['bitmap.cpp']) plugins += env.SharedLibrary('gridtexture', ['gridtexture.cpp']) plugins += env.SharedLibrary('checkerboard', ['checkerboard.cpp']) plugins += env.SharedLibrary('vertexcolors', ['vertexcolors.cpp']) diff --git a/src/textures/ldrtexture.cpp b/src/textures/bitmap.cpp similarity index 70% rename from src/textures/ldrtexture.cpp rename to src/textures/bitmap.cpp index 1906023a..88843042 100644 --- a/src/textures/ldrtexture.cpp +++ b/src/textures/bitmap.cpp @@ -31,14 +31,17 @@ MTS_NAMESPACE_BEGIN /** - * Gamma-corrected bitmap texture using the JPG, PNG, TGA or BMP + * Gamma-corrected bitmap texture using the EXR, JPG, PNG, TGA or BMP + * file formats. */ -class LDRTexture : public Texture2D { +class BitmapTexture : public Texture2D { public: - LDRTexture(const Properties &props) : Texture2D(props) { + BitmapTexture(const Properties &props) : Texture2D(props) { m_filename = Thread::getThread()->getFileResolver()->resolve( props.getString("filename")); - m_gamma = props.getFloat("gamma", -1); /* -1 means sRGB */ + + /* -1 means sRGB. Gamma is ignored when loading EXR files */ + m_gamma = props.getFloat("gamma", -1); Log(EInfo, "Loading texture \"%s\"", m_filename.leaf().c_str()); ref fs = new FileStream(m_filename, FileStream::EReadOnly); @@ -71,7 +74,9 @@ public: m_maxAnisotropy = props.getFloat("maxAnisotropy", 8); - if (extension == ".jpg" || extension == ".jpeg") + if (extension == ".exr") + m_format = Bitmap::EEXR; + else if (extension == ".jpg" || extension == ".jpeg") m_format = Bitmap::EJPEG; else if (extension == ".png") m_format = Bitmap::EPNG; @@ -86,7 +91,7 @@ public: initializeFrom(bitmap); } - LDRTexture(Stream *stream, InstanceManager *manager) + BitmapTexture(Stream *stream, InstanceManager *manager) : Texture2D(stream, manager) { m_filename = stream->readString(); Log(EInfo, "Unserializing texture \"%s\"", m_filename.leaf().c_str()); @@ -121,84 +126,91 @@ public: } void initializeFrom(Bitmap *bitmap) { - ref corrected = new Bitmap(bitmap->getWidth(), bitmap->getHeight(), 128); - - float tbl[256]; - if (m_gamma == -1) { - for (int i=0; i<256; ++i) - tbl[i] = fromSRGBComponent((Float) i / (Float) 255); + ref corrected; + m_bpp = bitmap->getBitsPerPixel(); + if (bitmap->getBitsPerPixel() == 128) { + /* Nothing needs to be done */ + corrected = bitmap; } else { - for (int i=0; i<256; ++i) - tbl[i] = std::pow((Float) i / (Float) 255, m_gamma); - } + corrected = new Bitmap(bitmap->getWidth(), bitmap->getHeight(), 128); - uint8_t *data = bitmap->getData(); - float *flData = corrected->getFloatData(); - if (bitmap->getBitsPerPixel() == 32) { - for (int y=0; ygetHeight(); ++y) { - for (int x=0; xgetWidth(); ++x) { - float - r = tbl[*data++], - g = tbl[*data++], - b = tbl[*data++], - a = *data++ / 255.0f; - *flData++ = r; - *flData++ = g; - *flData++ = b; - *flData++ = a; - } + float tbl[256]; + if (m_gamma == -1) { + for (int i=0; i<256; ++i) + tbl[i] = fromSRGBComponent((Float) i / (Float) 255); + } else { + for (int i=0; i<256; ++i) + tbl[i] = std::pow((Float) i / (Float) 255, m_gamma); } - } else if (bitmap->getBitsPerPixel() == 24) { - for (int y=0; ygetHeight(); ++y) { - for (int x=0; xgetWidth(); ++x) { - float - r = tbl[*data++], - g = tbl[*data++], - b = tbl[*data++]; - *flData++ = r; - *flData++ = g; - *flData++ = b; - *flData++ = 1.0f; + + uint8_t *data = bitmap->getData(); + float *flData = corrected->getFloatData(); + if (bitmap->getBitsPerPixel() == 32) { + for (int y=0; ygetHeight(); ++y) { + for (int x=0; xgetWidth(); ++x) { + float + r = tbl[*data++], + g = tbl[*data++], + b = tbl[*data++], + a = *data++ / 255.0f; + *flData++ = r; + *flData++ = g; + *flData++ = b; + *flData++ = a; + } } - } - } else if (bitmap->getBitsPerPixel() == 16) { - for (int y=0; ygetHeight(); ++y) { - for (int x=0; xgetWidth(); ++x) { - float col = tbl[*data++], - a = *data++ / 255.0f; - *flData++ = col; - *flData++ = col; - *flData++ = col; - *flData++ = a; + } else if (bitmap->getBitsPerPixel() == 24) { + for (int y=0; ygetHeight(); ++y) { + for (int x=0; xgetWidth(); ++x) { + float + r = tbl[*data++], + g = tbl[*data++], + b = tbl[*data++]; + *flData++ = r; + *flData++ = g; + *flData++ = b; + *flData++ = 1.0f; + } } - } - } else if (bitmap->getBitsPerPixel() == 8) { - for (int y=0; ygetHeight(); ++y) { - for (int x=0; xgetWidth(); ++x) { - float col = tbl[*data++]; - *flData++ = col; - *flData++ = col; - *flData++ = col; - *flData++ = 1.0f; + } else if (bitmap->getBitsPerPixel() == 16) { + for (int y=0; ygetHeight(); ++y) { + for (int x=0; xgetWidth(); ++x) { + float col = tbl[*data++], + a = *data++ / 255.0f; + *flData++ = col; + *flData++ = col; + *flData++ = col; + *flData++ = a; + } } - } - } else if (bitmap->getBitsPerPixel() == 1) { - int pos = 0; - for (int y=0; ygetHeight(); ++y) { - for (int x=0; xgetWidth(); ++x) { - int entry = pos / 8; - int bit = pos % 8; - int value = (data[entry] & (1 << bit)) ? 255 : 0; - float col = tbl[value]; - *flData++ = col; - *flData++ = col; - *flData++ = col; - *flData++ = 1.0f; - pos++; + } else if (bitmap->getBitsPerPixel() == 8) { + for (int y=0; ygetHeight(); ++y) { + for (int x=0; xgetWidth(); ++x) { + float col = tbl[*data++]; + *flData++ = col; + *flData++ = col; + *flData++ = col; + *flData++ = 1.0f; + } } + } else if (bitmap->getBitsPerPixel() == 1) { + int pos = 0; + for (int y=0; ygetHeight(); ++y) { + for (int x=0; xgetWidth(); ++x) { + int entry = pos / 8; + int bit = pos % 8; + int value = (data[entry] & (1 << bit)) ? 255 : 0; + float col = tbl[value]; + *flData++ = col; + *flData++ = col; + *flData++ = col; + *flData++ = 1.0f; + pos++; + } + } + } else { + Log(EError, "%i bpp images are currently not supported!", bitmap->getBitsPerPixel()); } - } else { - Log(EError, "%i bpp images are currently not supported!", bitmap->getBitsPerPixel()); } m_mipmap = MIPMap::fromBitmap(corrected, m_filterType, @@ -258,10 +270,16 @@ public: std::string toString() const { std::ostringstream oss; - oss << "LDRTexture[" << endl + oss << "BitmapTexture[" << endl << " filename = \"" << m_filename << "\"," << endl - << " gamma = " << m_gamma << endl - << "]"; + << " bpp = " << m_bpp; + if (m_bpp < 128) { + oss << "," << endl + << " gamma = " << m_gamma << endl; + } else { + oss << endl; + } + oss << "]"; return oss.str(); } @@ -278,13 +296,14 @@ protected: Float m_gamma; MIPMap::EWrapMode m_wrapMode; Float m_maxAnisotropy; + int m_bpp; }; // ================ Hardware shader implementation ================ -class LDRTextureShader : public Shader { +class BitmapTextureShader : public Shader { public: - LDRTextureShader(Renderer *renderer, std::string filename, ref bitmap, + BitmapTextureShader(Renderer *renderer, std::string filename, ref bitmap, const Point2 &uvOffset, const Vector2 &uvScale, MIPMap::EWrapMode wrapMode, Float maxAnisotropy) : Shader(renderer, ETextureShader), m_uvOffset(uvOffset), m_uvScale(uvScale) { @@ -342,14 +361,14 @@ private: Vector2 m_uvScale; }; -Shader *LDRTexture::createShader(Renderer *renderer) const { - return new LDRTextureShader(renderer, m_filename.leaf(), +Shader *BitmapTexture::createShader(Renderer *renderer) const { + return new BitmapTextureShader(renderer, m_filename.leaf(), m_mipmap->getLDRBitmap(), m_uvOffset, m_uvScale, m_wrapMode, (m_filterType == MIPMap::EEWA) ? m_maxAnisotropy : 1.0f); } -MTS_IMPLEMENT_CLASS_S(LDRTexture, false, Texture2D) -MTS_IMPLEMENT_CLASS(LDRTextureShader, false, Shader) -MTS_EXPORT_PLUGIN(LDRTexture, "LDR texture (JPG/PNG/TGA/BMP)"); +MTS_IMPLEMENT_CLASS_S(BitmapTexture, false, Texture2D) +MTS_IMPLEMENT_CLASS(BitmapTextureShader, false, Shader) +MTS_EXPORT_PLUGIN(BitmapTexture, "Bitmap texture (EXR/JPG/PNG/TGA/BMP)"); MTS_NAMESPACE_END diff --git a/src/textures/exrtexture.cpp b/src/textures/exrtexture.cpp deleted file mode 100644 index 2b551370..00000000 --- a/src/textures/exrtexture.cpp +++ /dev/null @@ -1,113 +0,0 @@ -/* - 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 -#include -#include -#include -#include -#include - -MTS_NAMESPACE_BEGIN - -/** - * Simple linear (i.e. not gamma corrected) bitmap texture - * using the EXR file format - */ -class EXRTexture : public Texture2D { -public: - EXRTexture(const Properties &props) : Texture2D(props) { - m_filename = Thread::getThread()->getFileResolver()->resolve( - props.getString("filename")); - Log(EInfo, "Loading texture \"%s\"", m_filename.leaf().c_str()); - - ref fs = new FileStream(m_filename, FileStream::EReadOnly); - ref bitmap = new Bitmap(Bitmap::EEXR, fs); - m_mipmap = MIPMap::fromBitmap(bitmap); - m_average = m_mipmap->triangle(m_mipmap->getLevels()-1, 0, 0); - m_maximum = m_mipmap->getMaximum(); - } - - EXRTexture(Stream *stream, InstanceManager *manager) - : Texture2D(stream, manager) { - m_filename = stream->readString(); - Log(EInfo, "Unserializing texture \"%s\"", m_filename.leaf().c_str()); - size_t size = stream->readSize(); - ref mStream = new MemoryStream(size); - stream->copyTo(mStream, size); - mStream->setPos(0); - ref bitmap = new Bitmap(Bitmap::EEXR, mStream); - m_mipmap = MIPMap::fromBitmap(bitmap); - m_average = m_mipmap->triangle(m_mipmap->getLevels()-1, 0, 0); - m_maximum = m_mipmap->getMaximum(); - } - - void serialize(Stream *stream, InstanceManager *manager) const { - Texture2D::serialize(stream, manager); - stream->writeString(m_filename.file_string()); - ref is = new FileStream(m_filename, FileStream::EReadOnly); - stream->writeSize(is->getSize()); - is->copyTo(stream); - } - - Spectrum getValue(const Point2 &uv) const { - return m_mipmap->triangle(0, uv.x, uv.y); - } - - Spectrum getValue(const Point2 &uv, Float dudx, - Float dudy, Float dvdx, Float dvdy) const { - return m_mipmap->getValue(uv.x, uv.y, dudx, dudy, dvdx, dvdy); - } - - Spectrum getMaximum() const { - return m_maximum; - } - - Spectrum getAverage() const { - return m_average; - } - - bool usesRayDifferentials() const { - return true; - } - - Vector3i getResolution() const { - return Vector3i( - m_mipmap->getWidth(), - m_mipmap->getHeight(), - 1 - ); - } - - std::string toString() const { - std::ostringstream oss; - oss << "EXRTexture[filename=\"" << m_filename.file_string() << "\"]"; - return oss.str(); - } - - MTS_DECLARE_CLASS() -protected: - ref m_mipmap; - fs::path m_filename; - Spectrum m_average, m_maximum; -}; - -MTS_IMPLEMENT_CLASS_S(EXRTexture, false, Texture2D) -MTS_EXPORT_PLUGIN(EXRTexture, "HDR texture (EXR)"); -MTS_NAMESPACE_END