better handling of constant color spectra in a way that makes RGB and spectral builds consistent
parent
b3dcc7b1dd
commit
6534b07338
|
@ -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">
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
105
doc/format.tex
105
doc/format.tex
|
@ -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">
|
||||
|
|
|
@ -108,8 +108,7 @@
|
|||
keywordstyle = \bfseries,
|
||||
captionpos = b,
|
||||
upquote = true,
|
||||
literate={*}{{\char42}}1
|
||||
{\ }{{\copyablespace}}1
|
||||
literate={\ }{{\copyablespace}}1
|
||||
}
|
||||
|
||||
\lstdefinelanguage{xml} {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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}.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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 "
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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'"
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
/// ==========================================================================
|
||||
|
|
|
@ -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"],
|
||||
|
|
Loading…
Reference in New Issue