diff --git a/data/schema/scene.xsd b/data/schema/scene.xsd
index 22e59f11..63e36522 100644
--- a/data/schema/scene.xsd
+++ b/data/schema/scene.xsd
@@ -298,6 +298,7 @@
+
diff --git a/doc/basics.tex b/doc/basics.tex
index e7239bec..317ee923 100644
--- a/doc/basics.tex
+++ b/doc/basics.tex
@@ -7,7 +7,8 @@ provides some basic instructions on how to use them.
To launch the interactive frontend, run \code{Mitsuba.app} on MacOS,
\code{mtsgui.exe} on Windows, and \code{mtsgui} on Linux (after sourcing \code{setpath.sh}).
You can also drag and drop scene files onto the application icon or the running program to open them.
-A quick video tutorial on using the GUI can be found here: \url{http://vimeo.com/13480342}.
+Two video tutorials on using the GUI can be found here: \url{http://vimeo.com/13480342} (somewhat dated)
+and \url{http://vimeo.com/50528092} (describing new features).
\subsection{Command line interface}
\label{sec:mitsuba}
The \texttt{mitsuba} binary is an alternative non-interactive rendering
@@ -74,7 +75,9 @@ mode of operation is to render a single scene, which is provided as a parameter,
\begin{shell}
$\texttt{\$}$ mitsuba path-to/my-scene.xml
\end{shell}
-It is also possible to connect to network render nodes, which essentially lets Mitsuba parallelize
+The next subsections explain various features of the \texttt{mitsuba} command line frontend.
+\subsubsection{Network rendering}
+Mitsuba can connect to network render nodes to parallelize a length rendering task
over additional cores. To do this, pass a semicolon-separated list of machines to
the \code{-c} parameter.
\begin{shell}
@@ -149,8 +152,9 @@ machine2.domain.org
machine3.domain.org:7346
\end{shell}
\subsubsection{Passing parameters}
-Any attribute in the XML-based scene description language can be parameterized from the
-command line.
+Any attribute in the XML-based scene description language (described in detail in \secref{format})
+can be parameterized from the command line.
+
For instance, you can render a scene several times with different reflectance values
on a certain material by changing its description to something like
\begin{xml}
@@ -176,13 +180,13 @@ image, after which it continues rendering. This can sometimes be useful to
check if everything is working correctly.
\subsubsection{Rendering an animation}
-The command line interface is ideally suited for rendering numbers of files in batch
+The command line interface is ideally suited for rendering several files in batch
operation. You can simply pass in the files using a wildcard in the filename.
If you've already rendered a subset of the frames and you only want to complete the remainder,
add the \texttt{-x} flag, and all files with existing output will be skipped. You can also
-let the scheduler work on several scenes at once using the \texttt{-j} parameter --- this is
-especially useful when parallelizing over multiple machines: as some of the participating machines
+let the scheduler work on several scenes at once using the \texttt{-j} parameter---this is
+can accelerate parallelization over many machines: as some of the machines
finish rendering the current frame, they can immediately start working on the next one
instead of having to wait for all other cores to finish. Altogether, you
might start the with parameters such as the following
@@ -196,7 +200,9 @@ the PowerShell supports the following syntax:
dir frame_*.xml | % $\texttt{\{}$ $\texttt{\$\_}$ $\texttt{\}}$
\end{shell}
-\subsection{Direct connection server}
+\subsection{Other programs}
+Mitsuba ships with a few other programs, which are explained in the remainder of this section.
+\subsubsection{Direct connection server}
\label{sec:mtssrv}
A Mitsuba compute node can be created using the \code{mtssrv} executable. By default,
it will listen on port 7554:
@@ -211,7 +217,7 @@ the wrong interface. In this case, please specify an explicit IP address or host
\begin{shell}
$\texttt{\$}$ mtssrv -i maxwell.cs.cornell.edu
\end{shell}
-As advised in Section~\ref{sec:mitsuba}, it is advised to run \code{mtssrv} \emph{only} in trusted networks.
+As advised in \secref{mitsuba}, it is advised to run \code{mtssrv} \emph{only} in trusted networks.
One nice feature of \code{mtssrv} is that it (like the \code{mitsuba} executable)
also supports the \code{-c} and \code{-s} parameters, which create connections
@@ -221,7 +227,7 @@ the root \code{mttsrv} instance of such a hierarchy could share its work with a
number of other machines running \code{mtssrv}, and each of these might also
share their work with further machines, and so on...
-The parallelization over such hierarchies happens transparently---when
+The parallelization over such hierarchies happens transparently: when
connecting a renderering process to the root node, it sees a machine
with hundreds or thousands of cores, to which it can submit work without
needing to worry about how exactly it is going to be spread out in
@@ -238,7 +244,7 @@ Using \code{mtssrv}, you can
instead designate a central scheduling node at your workplace, which accepts connections and delegates
rendering tasks to the other machines. In this case, you will only have to transmit the scene once,
and the remaining distribution happens over the fast local network at your workplace.
-\subsection{Utility launcher}
+\subsubsection{Utility launcher}
\label{sec:mtsutil}
When working on a larger project, one often needs to implement various utility programs that
perform simple tasks, such as applying a filter to an image or processing
@@ -253,9 +259,12 @@ $\texttt{\$}$ mtsutil [options] [arguments]
\end{shell}
For a listing of all supported options and utilities, enter the command without parameters.
+The second part of this manual explains how to develop such extensions yourself, specifically
+\secref{parallelization}.
+
\subsubsection{Tonemapper}
\label{sec:tonemapper}
-One particularly useful utility that shall be mentioned here is the batch tonemapper, which
+One frequently used utility that shall be mentioned here is the batch tonemapper, which
loads EXR/RGBE images and writes tonemapped 8-bit PNG/JPGs. This can save much time when one has to
process many high dynamic-range images such as animation frames using the same basic operations,
e.g. gamma correction, changing the overall brightness, resizing, cropping, etc. The available
diff --git a/doc/compiling.tex b/doc/compiling.tex
index dfa921fe..067db386 100644
--- a/doc/compiling.tex
+++ b/doc/compiling.tex
@@ -10,7 +10,7 @@ which operating system is used and will be explained in the following subsection
\subsection{Common steps}
To get started, you will need to download a recent version of the Mitsuba source code. Before
doing this, ensure that you have read the licensing agreement
-(Section~\ref{sec:license}), and that you abide by its contents. Note that, being a ``viral''
+(\secref{license}), and that you abide by its contents. Note that, being a ``viral''
license, the GPL automatically applies to derivative work. Amongst other things, this
means that Mitsuba's source code is \emph{off-limits} to those who develop rendering
software not distributed under a compatible license.
diff --git a/doc/development.tex b/doc/development.tex
index 523197c9..0bb711e7 100644
--- a/doc/development.tex
+++ b/doc/development.tex
@@ -1,4 +1,5 @@
\part{Development guide}
+\label{sec:development}
This chapter and the subsequent ones will provide an overview
of the the coding conventions and general architecture of Mitsuba.
You should only read them if if you wish to interface with the API
diff --git a/doc/format.tex b/doc/format.tex
index e234b94c..e8cb5dba 100644
--- a/doc/format.tex
+++ b/doc/format.tex
@@ -126,17 +126,53 @@ Passing strings is straightforward:
\begin{xml}
\end{xml}
+\subsubsection{RGB color values}
+In Mitsuba, color data is specified using the \texttt{}, and \texttt{}, or \texttt{} tags.
+We begin with the first two, which are most commonly used. The RGB tags expect
+red, green, and blue color values in floating point format, which are usually between 0 and 1 when
+specifying reflectance values. The \texttt{} tag internally causes the specified value
+to be linearized by mapping it through an inverse sRGB gamma curve:
+\begin{xml}
+
+
+\end{xml}
+The \texttt{} tag also accepts HTML-style hex values, e.g.:
+\begin{xml}
+
+\end{xml}
+When Mitsuba is compiled with the default settings, it internally uses
+linear RGB to represent colors, so these values are directly used.
+However, when configured for spectral rendering
+\footnote{Note that the official
+releases all use linear RGB---to do spectral renderings, you will have
+to compile Mitsuba yourself.}, a color
+spectrum that has a matching RGB value must be found. This is a classic underdetermined problem,
+since there is an infinite number of spectra corresponding to any particular RGB value.
+
+Mitsuba relies on a method by Smits et al. \cite{Smits2005RGB} to find a
+smooth and physically ``plausible'' spectrum. To do so, it chooses
+one of two variants of Smits' approach depending on whether the spectrum contains a
+unitless reflectance value, or a radiance-valued intensity. This choice can be
+enforced via the \texttt{intent} XML attribute, e.g.:
+\begin{xml}
+
+
+\end{xml}
+Usually this attribute is not neccessary:
+Mitsuba detects when an RGB value is specified in the declaration of a light source
+and uses \texttt{intent="illuminant"} in this case and \texttt{intent="reflectance"}
+everywhere else.
+
\subsubsection{Color spectra}
\label{sec:format-spectra}
-Depending on the compilation flags of Mitsuba (see \secref{compiling-flags} for
-details), the renderer internally either represents colors using discretized color spectra
-(when \texttt{SPECTRUM\_SAMPLES} is set to a value other than 3), or it
-uses a basic linear RGB representation\footnote{The official
-releases all use linear RGB---to do spectral renderings, you will have
-to compile Mitsuba yourself.}.
-Irrespective of which internal representation is used, Mitsuba supports
-several different ways of specifying color information, which is then
-converted appropriately.
+Mitsuba can also work with spectral color data. The exact internal representation of
+such spectra depends on how the renderer was compiled (see \secref{compiling-flags} for
+details).
+
+When \texttt{SPECTRUM\_SAMPLES} was set 3 at compile time (the default for the official builds),
+Mistuba uses a basic linear RGB representation and thus always converts color spectra to RGB.
+For other values (e.g. \texttt{SPECTRUM\_SAMPLES}=20), then renderer performs all internal
+computations using a full spectral representation with the specified number of bins.
The preferred way of passing color spectra to the renderer is to explicitly
denote the associated wavelengths of each value:
@@ -146,49 +182,39 @@ denote the associated wavelengths of each value:
This is a mapping from wavelength in nanometers (before the colon)
to a reflectance or intensity value (after the colon).
Values in between are linearly interpolated from the two closest neighbors.
-A useful shortcut to get a completely uniform spectrum, it is to provide
+
+A useful shortcut to get a ``white'' or uniform spectrum, it is to provide
only a single value:
\begin{xml}
\end{xml}
+The exact definition a white spectrum depends on whether
+it specifies a unitless reflectance value or a radiance-valued intensity. As before,
+Mitsuba tries to detect this automatically depending on whether or not the \texttt{}
+tag occurs within a light source declaration, and the \code{intent} attribute can be used
+to override the default behavior. In particular, the next snippet creates a uniform spectrum:
+\begin{xml}
+
+\end{xml}
+On the other hand, the following creates a multiple of the white point (the CIE D65 illuminant):
+\begin{xml}
+
+\end{xml}
+
Another (discouraged) option is to directly provide the spectrum in Mitsuba's
internal representation, avoiding the need for any kind of conversion.
-However, this is problematic, since the associated scene will likely not work
-anymore when Mitsuba is compiled with a different value of
+However, this is problematic, since the associated scene will not work
+when Mitsuba is compiled with a different value of
\texttt{SPECTRUM\_SAMPLES}.
For completeness, the possibility is explained nonetheless. Assuming that
the 360-830$nm$ range is discretized into ten 47$nm$-sized blocks
(i.e. \texttt{SPECTRUM\_SAMPLES} is set to 10), their values can be specified
-as follows:
+as
\begin{xml}
\end{xml}
-Another convenient way of providing color spectra is by specifying linear RGB
-or sRGB values using floating-point triplets or hex values:
-\begin{xml}
-
-
-
-\end{xml}
-When Mitsuba is compiled with the default settings, it internally uses
-linear RGB to represent colors, so these values can directly be used.
-However, when configured for doing spectral rendering, a suitable color
-spectrum with the requested RGB reflectance must be found. This is a tricky
-problem, since there is an infinite number of spectra with this property.
-
-Mitsuba uses a method by Smits et al. \cite{Smits2005RGB} to find a
-``plausible'' spectrum that is as smooth as possible. To do so, it uses
-one of two methods depending on whether the spectrum contains a
-unitless reflectance value, or a radiance-valued intensity.
-\begin{xml}
-
-
-\end{xml}
-The \texttt{reflectance} intent is used by default, so remember to
-set it to \texttt{illuminant} when defining the brightness of a
-light source with the \texttt{} tag.
When spectral power or reflectance distributions are obtained from measurements
(e.g. at 10$nm$ intervals), they are usually quite unwiedy and can clutter
@@ -231,7 +257,7 @@ the light source emission profile.}.
Specifically, the black body spectrum has units of power ($W$) per
unit area ($m^{-2}$) per steradian ($sr^{-1}$) per unit wavelength ($nm^{-1}$).
-If these units are inconsistent with your scene description, you may use the
+If these units are inconsistent with your scene description (e.g. because it is modeled in millimeters or kilometers), you may use the
optional \texttt{scale} attribute to adjust them, e.g.:
\begin{xml}
@@ -286,6 +312,7 @@ The \code{up} parameter is not needed for spot lights.
\end{itemize}
Cordinates that are zero (for \code{translate} and \code{rotate}) or one (for \code{scale})
do not explicitly have to be specified.
+\newpage
\subsection{Animated transformations}
Most shapes, emitters, and sensors in Mitsuba can accept both normal transformations
and \emph{animated transformations} as parameters. The latter is useful to
@@ -316,11 +343,11 @@ interpolation for the rotation component.} these for intermediate
time values.
It is important to specify appropriate shutter open/close times
to the sensor so that the motion is visible.
-\newpage
\subsection{References}
Quite often, you will find yourself using an object (such as a material) in many places. To avoid having
to declare it over and over again, which wastes memory, you can make use of references. Here is an example
of how this works:
+\newpage
\begin{xml}
diff --git a/doc/main.tex b/doc/main.tex
index a314cbbc..6fdf7be6 100644
--- a/doc/main.tex
+++ b/doc/main.tex
@@ -108,8 +108,7 @@
keywordstyle = \bfseries,
captionpos = b,
upquote = true,
- literate={*}{{\char42}}1
- {\ }{{\copyablespace}}1
+ literate={\ }{{\copyablespace}}1
}
\lstdefinelanguage{xml} {
diff --git a/doc/parallelization.tex b/doc/parallelization.tex
index 640d5d86..cb8162d1 100644
--- a/doc/parallelization.tex
+++ b/doc/parallelization.tex
@@ -1,4 +1,5 @@
\section{Parallelization layer}
+\label{sec:parallelization}
Mitsuba is built on top of a flexible parallelization layer, which spreads out
various types of computation over local and remote cores.
The guiding principle is that if an operation can potentially take longer than a
diff --git a/doc/section_integrators.tex b/doc/section_integrators.tex
index 4b1f5089..907d3e3f 100644
--- a/doc/section_integrators.tex
+++ b/doc/section_integrators.tex
@@ -124,7 +124,7 @@ from a rendering so that it can be pasted into a differently-colored document.
Note that only directly visible emitters can be hidden using this feature---a
reflection on a shiny surface will be unaffected. To perform the kind of
compositing shown in Figure~\ref{fig:hideemitters}, it is also necessary to
-enable the alpha channel in the scene's film instance (Section~\ref{sec:films}).
+enable the alpha channel in the scene's film instance (\secref{films}).
\renderings{
\unframedrendering{Daylit smoke rendered with \code{hideEmitters} set to \code{false}
diff --git a/doc/section_sensors.tex b/doc/section_sensors.tex
index f31dabe7..80d16da5 100644
--- a/doc/section_sensors.tex
+++ b/doc/section_sensors.tex
@@ -28,8 +28,8 @@ In the XML scene description language, a sensor declaration looks as follows
\end{xml}
In other words, the \code{} declaration is a child element of the \code{} (the particular
position in the scene file does not play a role). Nested within the sensor declaration is a
-sampler instance (described in Section~\ref{sec:samplers}) and a film instance (described in
-Section~\ref{sec:films}).
+sampler instance (described in \secref{samplers}) and a film instance (described in
+\secref{films}).
\subsubsection*{Handedness convention}
Sensors in Mitsuba are \emph{right-handed}.
diff --git a/doc/section_subsurface.tex b/doc/section_subsurface.tex
index 5c446a91..894cdca6 100644
--- a/doc/section_subsurface.tex
+++ b/doc/section_subsurface.tex
@@ -11,7 +11,7 @@ participating media and subsurface scattering models.
\emph{quickly} and the demands on accuracy are secondary.
At the moment, there is only one subsurface scattering model (the
\pluginref{dipole}), which is described on the next page.
- \item[Participating media:] Described in Section~\ref{sec:media}. When modeling
+ \item[Participating media:] Described in \secref{media}. When modeling
subsurface scattering using a participating medium, Mitsuba performs a \emph{full}
radiative transport simulation, which correctly accounts for all scattering events.
This is more accurate but generally significantly slower.
diff --git a/include/mitsuba/core/spectrum.h b/include/mitsuba/core/spectrum.h
index 4582fdde..fded918c 100644
--- a/include/mitsuba/core/spectrum.h
+++ b/include/mitsuba/core/spectrum.h
@@ -861,6 +861,12 @@ public:
/// Return a string representation
std::string toString() const;
+ /**
+ * \brief Return a spectral color distribution of the
+ * D65 white point (with unit luminance)
+ */
+ inline static const Spectrum &getD65() { return CIE_D65; }
+
/**
* \brief Static initialization (should be called once during the
* application's initialization phase)
@@ -906,6 +912,9 @@ protected:
static Spectrum rgbIllum2SpecBlue;
/// @}
#endif
+
+ /// Pre-integrated D65 illuminant
+ static Spectrum CIE_D65;
};
MTS_NAMESPACE_END
diff --git a/include/mitsuba/render/scenehandler.h b/include/mitsuba/render/scenehandler.h
index 37a3c216..88d8ab1a 100644
--- a/include/mitsuba/render/scenehandler.h
+++ b/include/mitsuba/render/scenehandler.h
@@ -134,16 +134,6 @@ protected:
void clear();
private:
- struct ParseContext {
- inline ParseContext(ParseContext *_parent)
- : parent(_parent) { }
-
- ParseContext *parent;
- Properties properties;
- std::map attributes;
- std::vector > children;
- };
-
/**
* Enumeration of all possible tags that can be encountered in a
* Mitsuba scene file
@@ -161,6 +151,18 @@ private:
EInclude, EAlias, EDefault
};
+ struct ParseContext {
+ inline ParseContext(ParseContext *_parent, ETag tag)
+ : parent(_parent), tag(tag) { }
+
+ ParseContext *parent;
+ ETag tag;
+ Properties properties;
+ std::map attributes;
+ std::vector > children;
+ };
+
+
typedef std::pair TagEntry;
typedef boost::unordered_map TagMap;
diff --git a/src/emitters/area.cpp b/src/emitters/area.cpp
index 2d6462a7..4f5b782a 100644
--- a/src/emitters/area.cpp
+++ b/src/emitters/area.cpp
@@ -74,7 +74,7 @@ public:
"allowed -- the area light inherits this transformation from "
"its parent shape");
- m_radiance = props.getSpectrum("radiance", Spectrum(1.0f));
+ m_radiance = props.getSpectrum("radiance", Spectrum::getD65());
m_power = Spectrum(0.0f); /// Don't know the power yet
}
diff --git a/src/emitters/collimated.cpp b/src/emitters/collimated.cpp
index 3f5f4550..79f5af13 100644
--- a/src/emitters/collimated.cpp
+++ b/src/emitters/collimated.cpp
@@ -58,7 +58,7 @@ public:
CollimatedBeamEmitter(const Properties &props) : Emitter(props) {
m_type |= EDeltaPosition | EDeltaDirection;
- m_power = props.getSpectrum("power", Spectrum(1.0f));
+ m_power = props.getSpectrum("power", Spectrum::getD65());
if (props.getTransform("toWorld", Transform()).hasScale())
Log(EError, "Scale factors in the emitter-to-world "
diff --git a/src/emitters/constant.cpp b/src/emitters/constant.cpp
index f1909c2d..7a7c4c32 100644
--- a/src/emitters/constant.cpp
+++ b/src/emitters/constant.cpp
@@ -46,7 +46,7 @@ class ConstantBackgroundEmitter : public Emitter {
public:
ConstantBackgroundEmitter(const Properties &props) : Emitter(props) {
m_type |= EOnSurface | EEnvironmentEmitter;
- m_radiance = props.getSpectrum("radiance", Spectrum(1.0f));
+ m_radiance = props.getSpectrum("radiance", Spectrum::getD65());
}
ConstantBackgroundEmitter(Stream *stream, InstanceManager *manager)
diff --git a/src/emitters/directional.cpp b/src/emitters/directional.cpp
index 25ea9bbb..b00db66b 100644
--- a/src/emitters/directional.cpp
+++ b/src/emitters/directional.cpp
@@ -55,7 +55,7 @@ public:
DirectionalEmitter(const Properties &props) : Emitter(props) {
m_type |= EDeltaDirection;
- m_normalIrradiance = props.getSpectrum("irradiance", Spectrum(1.0f));
+ m_normalIrradiance = props.getSpectrum("irradiance", Spectrum::getD65());
if (props.hasProperty("direction")) {
if (props.hasProperty("toWorld"))
Log(EError, "Only one of the parameters 'direction' and 'toWorld'"
diff --git a/src/emitters/point.cpp b/src/emitters/point.cpp
index 5b286e3d..e73cc3d7 100644
--- a/src/emitters/point.cpp
+++ b/src/emitters/point.cpp
@@ -65,7 +65,7 @@ public:
Transform::translate(Vector(props.getPoint("position"))));
}
- m_intensity = props.getSpectrum("intensity", Spectrum(1.0f));
+ m_intensity = props.getSpectrum("intensity", Spectrum::getD65());
}
PointEmitter(Stream *stream, InstanceManager *manager)
diff --git a/src/emitters/spot.cpp b/src/emitters/spot.cpp
index 4acb24c6..8e1808b4 100644
--- a/src/emitters/spot.cpp
+++ b/src/emitters/spot.cpp
@@ -74,7 +74,7 @@ public:
Assert(m_cutoffAngle >= m_beamWidth);
m_type = EDeltaPosition;
m_texture = new ConstantSpectrumTexture(
- props.getSpectrum("texture", Spectrum(1.0f)));
+ props.getSpectrum("texture", Spectrum::getD65()));
}
SpotEmitter(Stream *stream, InstanceManager *manager)
diff --git a/src/libcore/spectrum.cpp b/src/libcore/spectrum.cpp
index 9f14399e..4078cd0c 100644
--- a/src/libcore/spectrum.cpp
+++ b/src/libcore/spectrum.cpp
@@ -31,6 +31,7 @@ extern const Float CIE_wavelengths[CIE_samples];
extern const Float CIE_X_entries[CIE_samples];
extern const Float CIE_Y_entries[CIE_samples];
extern const Float CIE_Z_entries[CIE_samples];
+extern const Float CIE_D65_entries[CIE_samples];
/// @}
/**
@@ -59,6 +60,7 @@ extern const Float RGBIllum2SpecBlue_entries[RGB2Spec_samples];
static InterpolatedSpectrum CIE_X_interp(CIE_wavelengths, CIE_X_entries, CIE_samples);
static InterpolatedSpectrum CIE_Y_interp(CIE_wavelengths, CIE_Y_entries, CIE_samples);
static InterpolatedSpectrum CIE_Z_interp(CIE_wavelengths, CIE_Z_entries, CIE_samples);
+static InterpolatedSpectrum CIE_D65_interp(CIE_wavelengths, CIE_D65_entries, CIE_samples);
/// @}
@@ -67,6 +69,7 @@ static InterpolatedSpectrum CIE_Z_interp(CIE_wavelengths, CIE_Z_entries, CIE_sam
Spectrum Spectrum::CIE_X;
Spectrum Spectrum::CIE_Y;
Spectrum Spectrum::CIE_Z;
+Spectrum Spectrum::CIE_D65;
Float Spectrum::CIE_normalization;
/// @}
@@ -119,6 +122,9 @@ void Spectrum::staticInitialization() {
CIE_normalization += CIE_Y[i];
CIE_normalization = 1.0f / CIE_normalization;
+ CIE_D65.fromContinuousSpectrum(CIE_D65_interp);
+ CIE_D65 /= CIE_D65.getLuminance();
+
/* Pre-integrate the Smits-style RGB to Spectrum conversion data */
rgbRefl2SpecWhite.fromContinuousSpectrum(InterpolatedSpectrum(
RGB2Spec_wavelengths, RGBRefl2SpecWhite_entries, RGB2Spec_samples));
@@ -152,6 +158,8 @@ void Spectrum::staticInitialization() {
SLog(EDebug, "Configured for spectral rendering using "
"the wavelength discretization %s", oss.str().c_str());
+#else
+ CIE_D65 = Spectrum(1.0f);
#endif
}
@@ -445,16 +453,20 @@ void Spectrum::fromRGBE(const uint8_t rgbe[4], EConversionIntent intent) {
std::string Spectrum::toString() const {
std::ostringstream oss;
oss << "[";
- if (std::abs(getLuminance()) > 0.1f)
- oss << std::fixed;
+ bool dark = std::abs(getLuminance()) < 0.1f;
for (int i=0; i ";
oss.precision(3);
+ if (dark)
+ oss << std::scientific;
+ else
+ oss << std::fixed;
oss << s[i];
#endif
if (i < SPECTRUM_SAMPLES - 1)
@@ -1123,6 +1135,127 @@ const Float CIE_Z_entries[CIE_samples] = {
0.0, 0.0, 0.0
};
+const Float CIE_D65_entries[CIE_samples] = {
+ 46.6383, 47.1834, 47.7285, 48.2735,
+ 48.8186, 49.3637, 49.9088, 50.4539,
+ 50.9989, 51.544, 52.0891, 51.8777,
+ 51.6664, 51.455, 51.2437, 51.0323,
+ 50.8209, 50.6096, 50.3982, 50.1869,
+ 49.9755, 50.4428, 50.91, 51.3773,
+ 51.8446, 52.3118, 52.7791, 53.2464,
+ 53.7137, 54.1809, 54.6482, 57.4589,
+ 60.2695, 63.0802, 65.8909, 68.7015,
+ 71.5122, 74.3229, 77.1336, 79.9442,
+ 82.7549, 83.628, 84.5011, 85.3742,
+ 86.2473, 87.1204, 87.9936, 88.8667,
+ 89.7398, 90.6129, 91.486, 91.6806,
+ 91.8752, 92.0697, 92.2643, 92.4589,
+ 92.6535, 92.8481, 93.0426, 93.2372,
+ 93.4318, 92.7568, 92.0819, 91.4069,
+ 90.732, 90.057, 89.3821, 88.7071,
+ 88.0322, 87.3572, 86.6823, 88.5006,
+ 90.3188, 92.1371, 93.9554, 95.7736,
+ 97.5919, 99.4102, 101.228, 103.047,
+ 104.865, 106.079, 107.294, 108.508,
+ 109.722, 110.936, 112.151, 113.365,
+ 114.579, 115.794, 117.008, 117.088,
+ 117.169, 117.249, 117.33, 117.41,
+ 117.49, 117.571, 117.651, 117.732,
+ 117.812, 117.517, 117.222, 116.927,
+ 116.632, 116.336, 116.041, 115.746,
+ 115.451, 115.156, 114.861, 114.967,
+ 115.073, 115.18, 115.286, 115.392,
+ 115.498, 115.604, 115.711, 115.817,
+ 115.923, 115.212, 114.501, 113.789,
+ 113.078, 112.367, 111.656, 110.945,
+ 110.233, 109.522, 108.811, 108.865,
+ 108.92, 108.974, 109.028, 109.082,
+ 109.137, 109.191, 109.245, 109.3,
+ 109.354, 109.199, 109.044, 108.888,
+ 108.733, 108.578, 108.423, 108.268,
+ 108.112, 107.957, 107.802, 107.501,
+ 107.2, 106.898, 106.597, 106.296,
+ 105.995, 105.694, 105.392, 105.091,
+ 104.79, 105.08, 105.37, 105.66,
+ 105.95, 106.239, 106.529, 106.819,
+ 107.109, 107.399, 107.689, 107.361,
+ 107.032, 106.704, 106.375, 106.047,
+ 105.719, 105.39, 105.062, 104.733,
+ 104.405, 104.369, 104.333, 104.297,
+ 104.261, 104.225, 104.19, 104.154,
+ 104.118, 104.082, 104.046, 103.641,
+ 103.237, 102.832, 102.428, 102.023,
+ 101.618, 101.214, 100.809, 100.405,
+ 100, 99.6334, 99.2668, 98.9003,
+ 98.5337, 98.1671, 97.8005, 97.4339,
+ 97.0674, 96.7008, 96.3342, 96.2796,
+ 96.225, 96.1703, 96.1157, 96.0611,
+ 96.0065, 95.9519, 95.8972, 95.8426,
+ 95.788, 95.0778, 94.3675, 93.6573,
+ 92.947, 92.2368, 91.5266, 90.8163,
+ 90.1061, 89.3958, 88.6856, 88.8177,
+ 88.9497, 89.0818, 89.2138, 89.3459,
+ 89.478, 89.61, 89.7421, 89.8741,
+ 90.0062, 89.9655, 89.9248, 89.8841,
+ 89.8434, 89.8026, 89.7619, 89.7212,
+ 89.6805, 89.6398, 89.5991, 89.4091,
+ 89.219, 89.029, 88.8389, 88.6489,
+ 88.4589, 88.2688, 88.0788, 87.8887,
+ 87.6987, 87.2577, 86.8167, 86.3757,
+ 85.9347, 85.4936, 85.0526, 84.6116,
+ 84.1706, 83.7296, 83.2886, 83.3297,
+ 83.3707, 83.4118, 83.4528, 83.4939,
+ 83.535, 83.576, 83.6171, 83.6581,
+ 83.6992, 83.332, 82.9647, 82.5975,
+ 82.2302, 81.863, 81.4958, 81.1285,
+ 80.7613, 80.394, 80.0268, 80.0456,
+ 80.0644, 80.0831, 80.1019, 80.1207,
+ 80.1395, 80.1583, 80.177, 80.1958,
+ 80.2146, 80.4209, 80.6272, 80.8336,
+ 81.0399, 81.2462, 81.4525, 81.6588,
+ 81.8652, 82.0715, 82.2778, 81.8784,
+ 81.4791, 81.0797, 80.6804, 80.281,
+ 79.8816, 79.4823, 79.0829, 78.6836,
+ 78.2842, 77.4279, 76.5716, 75.7153,
+ 74.859, 74.0027, 73.1465, 72.2902,
+ 71.4339, 70.5776, 69.7213, 69.9101,
+ 70.0989, 70.2876, 70.4764, 70.6652,
+ 70.854, 71.0428, 71.2315, 71.4203,
+ 71.6091, 71.8831, 72.1571, 72.4311,
+ 72.7051, 72.979, 73.253, 73.527,
+ 73.801, 74.075, 74.349, 73.0745,
+ 71.8, 70.5255, 69.251, 67.9765,
+ 66.702, 65.4275, 64.153, 62.8785,
+ 61.604, 62.4322, 63.2603, 64.0885,
+ 64.9166, 65.7448, 66.573, 67.4011,
+ 68.2293, 69.0574, 69.8856, 70.4057,
+ 70.9259, 71.446, 71.9662, 72.4863,
+ 73.0064, 73.5266, 74.0467, 74.5669,
+ 75.087, 73.9376, 72.7881, 71.6387,
+ 70.4893, 69.3398, 68.1904, 67.041,
+ 65.8916, 64.7421, 63.5927, 61.8752,
+ 60.1578, 58.4403, 56.7229, 55.0054,
+ 53.288, 51.5705, 49.8531, 48.1356,
+ 46.4182, 48.4569, 50.4956, 52.5344,
+ 54.5731, 56.6118, 58.6505, 60.6892,
+ 62.728, 64.7667, 66.8054, 66.4631,
+ 66.1209, 65.7786, 65.4364, 65.0941,
+ 64.7518, 64.4096, 64.0673, 63.7251,
+ 63.3828, 63.4749, 63.567, 63.6592,
+ 63.7513, 63.8434, 63.9355, 64.0276,
+ 64.1198, 64.2119, 64.304, 63.8188,
+ 63.3336, 62.8484, 62.3632, 61.8779,
+ 61.3927, 60.9075, 60.4223, 59.9371,
+ 59.4519, 58.7026, 57.9533, 57.204,
+ 56.4547, 55.7054, 54.9562, 54.2069,
+ 53.4576, 52.7083, 51.959, 52.5072,
+ 53.0553, 53.6035, 54.1516, 54.6998,
+ 55.248, 55.7961, 56.3443, 56.8924,
+ 57.4406, 57.7278, 58.015, 58.3022,
+ 58.5894, 58.8765, 59.1637, 59.4509,
+ 59.7381, 60.0253, 60.3125
+};
+
/// ==========================================================================
// Smits-style RGB to Spectrum conversion data generated by Karl vom Berge
/// ==========================================================================
diff --git a/src/librender/scenehandler.cpp b/src/librender/scenehandler.cpp
index 44884579..4075e950 100644
--- a/src/librender/scenehandler.cpp
+++ b/src/librender/scenehandler.cpp
@@ -195,8 +195,13 @@ Float SceneHandler::parseFloat(const std::string &name,
void SceneHandler::startElement(const XMLCh* const xmlName,
AttributeList &xmlAttributes) {
std::string name = transcode(xmlName);
+ TagMap::const_iterator it = m_tags.find(name);
- ParseContext context((name == "scene") ? NULL : &m_context.top());
+ if (it == m_tags.end())
+ XMLLog(EError, "Unhandled tag \"%s\" encountered!", name.c_str());
+
+ const TagEntry &tag = it->second;
+ ParseContext context((name == "scene") ? NULL : &m_context.top(), tag.first);
for (size_t i=0; isecond;
switch (tag.first) {
case EScene: {
std::string versionString = context.attributes["version"];
@@ -458,6 +458,9 @@ void SceneHandler::endElement(const XMLCh* const xmlName) {
case ERGB: {
Spectrum::EConversionIntent intent = Spectrum::EReflectance;
+ if (context.parent->tag == EEmitter)
+ intent = Spectrum::EIlluminant;
+
if (context.attributes.find("intent") != context.attributes.end()) {
std::string intentString = boost::to_lower_copy(context.attributes["intent"]);
if (intentString == "reflectance")
@@ -545,10 +548,13 @@ void SceneHandler::endElement(const XMLCh* const xmlName) {
case ESpectrum: {
bool hasValue = context.attributes.find("value") != context.attributes.end();
bool hasFilename = context.attributes.find("filename") != context.attributes.end();
+ bool hasIntent = context.attributes.find("intent") != context.attributes.end();
if (hasValue == hasFilename) {
- SLog(EError, "Spectrum: please provide one of 'value' or 'filename'");
+ XMLLog(EError, ": please provide one of 'value' or 'filename'");
} else if (hasFilename) {
+ if (hasIntent)
+ XMLLog(EError, ": 'intent' and 'filename' cannot be specified at the same time!");
FileResolver *resolver = Thread::getThread()->getFileResolver();
fs::path path = resolver->resolve(context.attributes["filename"]);
InterpolatedSpectrum interp(path);
@@ -561,11 +567,31 @@ void SceneHandler::endElement(const XMLCh* const xmlName) {
std::vector tokens = tokenize(
context.attributes["value"], ", ");
Float value[SPECTRUM_SAMPLES];
- if (tokens.size() == 1) {
+ if (tokens.size() == 1 && tokens[0].find(':') == std::string::npos) {
+ Spectrum::EConversionIntent intent = Spectrum::EReflectance;
+ if (context.parent->tag == EEmitter)
+ intent = Spectrum::EIlluminant;
+
+ if (hasIntent) {
+ std::string intentString = boost::to_lower_copy(context.attributes["intent"]);
+ if (intentString == "reflectance")
+ intent = Spectrum::EReflectance;
+ else if (intentString == "illuminant")
+ intent = Spectrum::EIlluminant;
+ else
+ XMLLog(EError, "Invalid intent \"%s\", must be "
+ "\"reflectance\" or \"illuminant\"", intentString.c_str());
+ }
value[0] = parseFloat(name, tokens[0]);
- context.parent->properties.setSpectrum(context.attributes["name"],
- Spectrum(value[0]));
+ Spectrum spec;
+ if (intent == Spectrum::EReflectance)
+ spec = Spectrum(value[0]);
+ else
+ spec = Spectrum::getD65() * value[0];
+ context.parent->properties.setSpectrum(context.attributes["name"], spec);
} else {
+ if (hasIntent)
+ XMLLog(EError, ": 'intent' can only be specified when given a single-valued argument.");
if (tokens[0].find(':') != std::string::npos) {
InterpolatedSpectrum interp(tokens.size());
/* Wavelength -> Value mapping */
@@ -585,7 +611,7 @@ void SceneHandler::endElement(const XMLCh* const xmlName) {
discrete);
} else {
if (tokens.size() != SPECTRUM_SAMPLES)
- XMLLog(EError, "Invalid spectrum value specified (incorrect length)");
+ XMLLog(EError, "Invalid spectrum value specified (length does not match the current spectral discretization!)");
for (int i=0; iproperties.setSpectrum(context.attributes["name"],