better handling of constant color spectra in a way that makes RGB and spectral builds consistent

metadata
Wenzel Jakob 2014-02-13 18:38:21 +01:00
parent b3dcc7b1dd
commit 6534b07338
20 changed files with 295 additions and 87 deletions

View File

@ -298,6 +298,7 @@
<xsd:attribute name="name" type="xsd:string" use="required"/>
<xsd:attribute name="filename" type="xsd:string" use="optional"/>
<xsd:attribute name="value" type="xsd:string" use="optional"/>
<xsd:attribute name="intent" type="xsd:string" use="optional"/>
</xsd:complexType>
<xsd:complexType name="include">

View File

@ -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{\{}$ <path to mitsuba.exe> $\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] <utility name> [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

View File

@ -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.

View File

@ -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

View File

@ -126,17 +126,53 @@ Passing strings is straightforward:
\begin{xml}
<string name="stringProperty" value="This is a string"/>
\end{xml}
\subsubsection{RGB color values}
In Mitsuba, color data is specified using the \texttt{<rgb>}, and \texttt{<srgb>}, or \texttt{<spectrum>} 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{<srgb>} tag internally causes the specified value
to be linearized by mapping it through an inverse sRGB gamma curve:
\begin{xml}
<rgb name="spectrumProperty" value="0.2, 0.8, 0.4"/>
<srgb name="spectrumProperty" value="0.4, 0.3, 0.2"/>
\end{xml}
The \texttt{<srgb>} tag also accepts HTML-style hex values, e.g.:
\begin{xml}
<srgb name="spectrumProperty" value="#f9aa34"/>
\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}
<rgb name="spectrumProperty" intent="reflectance" value="0.2, 0.8, 0.4"/>
<rgb name="spectrumProperty" intent="illuminant" value="0.2, 0.8, 0.4"/>
\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}
<spectrum name="spectrumProperty" value="0.56"/>
\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{<spectrum>}
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}
<spectrum name="spectrumProperty" intent="reflectance" value="0.56"/>
\end{xml}
On the other hand, the following creates a multiple of the white point (the CIE D65 illuminant):
\begin{xml}
<spectrum name="spectrumProperty" intent="illuminant" value="0.56"/>
\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}
<spectrum name="spectrumProperty" value=".2, .2, .8, .4, .6, .5, .1, .9, .4, .2"/>
\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}
<rgb name="spectrumProperty" value="0.2, 0.8, 0.4"/>
<srgb name="spectrumProperty" value="0.4, 0.3, 0.2"/>
<srgb name="spectrumProperty" value="#f9aa34"/>
\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}
<rgb name="spectrumProperty" intent="reflectance" value="0.2, 0.8, 0.4"/>
<rgb name="spectrumProperty" intent="illuminant" value="0.2, 0.8, 0.4"/>
\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{<rgb>} 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}
<!-- Scale black body radiance by a factor of 1/1000 -->
@ -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}
<scene version=$\MtsVer$>
<texture type="bitmap" id="myImage">

View File

@ -108,8 +108,7 @@
keywordstyle = \bfseries,
captionpos = b,
upquote = true,
literate={*}{{\char42}}1
{\ }{{\copyablespace}}1
literate={\ }{{\copyablespace}}1
}
\lstdefinelanguage{xml} {

View File

@ -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

View File

@ -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}

View File

@ -28,8 +28,8 @@ In the XML scene description language, a sensor declaration looks as follows
\end{xml}
In other words, the \code{<sensor>} declaration is a child element of the \code{<scene>} (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}.

View File

@ -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.

View File

@ -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

View File

@ -134,16 +134,6 @@ protected:
void clear();
private:
struct ParseContext {
inline ParseContext(ParseContext *_parent)
: parent(_parent) { }
ParseContext *parent;
Properties properties;
std::map<std::string, std::string> attributes;
std::vector<std::pair<std::string, ConfigurableObject *> > 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<std::string, std::string> attributes;
std::vector<std::pair<std::string, ConfigurableObject *> > children;
};
typedef std::pair<ETag, const Class *> TagEntry;
typedef boost::unordered_map<std::string, TagEntry> TagMap;

View File

@ -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
}

View File

@ -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 "

View File

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

View File

@ -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'"

View File

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

View File

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

View File

@ -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<SPECTRUM_SAMPLES; i++) {
#if SPECTRUM_SAMPLES == 3
oss << s[i];
#else
oss.precision(1);
oss << std::fixed;
oss << m_wavelengths[i] << "-"
<< m_wavelengths[i+1] << "nm => ";
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
/// ==========================================================================

View File

@ -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; i<xmlAttributes.getLength(); i++) {
std::string attrValue = transcode(xmlAttributes.getValue(i));
@ -216,11 +221,6 @@ void SceneHandler::startElement(const XMLCh* const xmlName,
context.attributes[transcode(xmlAttributes.getName(i))] = attrValue;
}
TagMap::const_iterator it = m_tags.find(name);
if (it == m_tags.end())
XMLLog(EError, "Unhandled tag \"%s\" encountered!", name.c_str());
const TagEntry &tag = it->second;
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, "<spectrum>: please provide one of 'value' or 'filename'");
} else if (hasFilename) {
if (hasIntent)
XMLLog(EError, "<spectrum>: '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<std::string> 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, "<spectrum>: '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; i<SPECTRUM_SAMPLES; i++)
value[i] = parseFloat(name, tokens[i]);
context.parent->properties.setSpectrum(context.attributes["name"],