documentation updates, the chi-square test now also handles delta components, addes support for loading interpolated color spectra from disk
parent
54fb516737
commit
4a6d69df32
|
@ -2,7 +2,7 @@
|
||||||
Mitsuba uses a very simple and general XML-based format to represent scenes.
|
Mitsuba uses a very simple and general XML-based format to represent scenes.
|
||||||
Since the framework's philosophy is to represent discrete blocks of functionality as plugins,
|
Since the framework's philosophy is to represent discrete blocks of functionality as plugins,
|
||||||
a scene file can essentially be interpreted as description that determines which
|
a scene file can essentially be interpreted as description that determines which
|
||||||
plugins should be instantiated and how they should be interface with each other.
|
plugins should be instantiated and how they should interface with each other.
|
||||||
In the following, we'll look at a few examples to get a feeling for the scope of the
|
In the following, we'll look at a few examples to get a feeling for the scope of the
|
||||||
format.
|
format.
|
||||||
|
|
||||||
|
@ -21,15 +21,15 @@ create the scene. This information allows Mitsuba to always correctly process th
|
||||||
file irregardless of any potential future changes in the scene description language.
|
file irregardless of any potential future changes in the scene description language.
|
||||||
|
|
||||||
This example already contains the most important things to know about format: you can have
|
This example already contains the most important things to know about format: you can have
|
||||||
\emph{objects} (such as the objects instantiated by the \code{scene} or \code{shape} tags), which are allowed to be nested within
|
\emph{objects} (such as the objects instantiated by the \code{scene} or \code{shape} tags),
|
||||||
each other. Each object optionally accepts \emph{properties} (such as the \code{string} tag),
|
which are allowed to be nested within each other. Each object optionally accepts \emph{properties}
|
||||||
which further characterize its behavior. All objects except for the root object (the \code{scene})
|
(such as the \code{string} tag), which further characterize its behavior. All objects except
|
||||||
cause the renderer to load and instantiate a plugin, hence you must provide the plugin name using
|
for the root object (the \code{scene}) cause the renderer to search and load a plugin from disk,
|
||||||
\code{type=".."} parameter.
|
hence you must provide the plugin name using \code{type=".."} parameter.
|
||||||
|
|
||||||
The object tags also let the renderer know \emph{what kind} of object is to be instantiated: for instance,
|
The object tags also let the renderer know \emph{what kind} of object is to be instantiated: for instance,
|
||||||
any plugin loaded using the \code{shape} tag must conform to the \emph{Shape} interface, which
|
any plugin loaded using the \code{shape} tag must conform to the \emph{Shape} interface, which is
|
||||||
the certainly case for the plugin named \code{obj} (it contains a WaveFront OBJ loader).
|
certainly the case for the plugin named \code{obj} (it contains a WaveFront OBJ loader).
|
||||||
Similarly, you could write
|
Similarly, you could write
|
||||||
\begin{xml}
|
\begin{xml}
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
@ -41,16 +41,20 @@ Similarly, you could write
|
||||||
\end{xml}
|
\end{xml}
|
||||||
This loads a different plugin (\code{sphere}) which is still a \emph{Shape}, but instead represents
|
This loads a different plugin (\code{sphere}) which is still a \emph{Shape}, but instead represents
|
||||||
a sphere configured with a radius of 10 world-space units. Mitsuba ships with
|
a sphere configured with a radius of 10 world-space units. Mitsuba ships with
|
||||||
a large number of plugins; please refer to the next chapter for a reference.
|
a large number of plugins; please refer to the next chapter for a detailed
|
||||||
|
overview of them.
|
||||||
|
|
||||||
The most common scene setup is to declare an integrator, some geometry, a camera, a film, a sampler
|
The most common scene setup is to declare an integrator, some geometry, a camera, a film, a sampler
|
||||||
and one or more luminaires. Here is a more complex example:
|
and one or more luminaires. Here is a more complex example:
|
||||||
\begin{xml}
|
\begin{xml}
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
<scene version=$\MtsVer$>
|
<scene version=$\MtsVer$>
|
||||||
<integrator type="path"> <!-- Path trace an 8-bounce GI solution -->
|
<integrator type="path">
|
||||||
|
<!-- Path trace with a max. path length of 8 -->
|
||||||
<integer name="maxDepth" value="8"/>
|
<integer name="maxDepth" value="8"/>
|
||||||
</integrator>
|
</integrator>
|
||||||
|
|
||||||
<!-- Instantiate a perspective camera with 45 degrees field of view -->
|
<!-- Instantiate a perspective camera with 45 degrees field of view -->
|
||||||
<camera type="perspective">
|
<camera type="perspective">
|
||||||
<!-- Rotate the camera around the Y axis by 180 degrees -->
|
<!-- Rotate the camera around the Y axis by 180 degrees -->
|
||||||
|
@ -72,17 +76,18 @@ and one or more luminaires. Here is a more complex example:
|
||||||
</film>
|
</film>
|
||||||
</camera>
|
</camera>
|
||||||
|
|
||||||
<!-- Add a dragon mesh made of rough glass (stored as OBJ) -->
|
<!-- Add a dragon mesh made of rough glass (stored as OBJ file) -->
|
||||||
<shape type="obj">
|
<shape type="obj">
|
||||||
<string name="filename" value="dragon.obj"/>
|
<string name="filename" value="dragon.obj"/>
|
||||||
|
|
||||||
<bsdf type="roughglass">
|
<bsdf type="roughdielectric">
|
||||||
<!-- Tweak the roughness parameter of the material -->
|
<!-- Tweak the roughness parameter of the material -->
|
||||||
<float name="alphaB" value="0.01"/>
|
<float name="alpha" value="0.01"/>
|
||||||
</bsdf>
|
</bsdf>
|
||||||
</shape>
|
</shape>
|
||||||
|
|
||||||
<!-- Add a mesh stored using a more compact representation -->
|
<!-- Add another mesh -- this time, stored using Mitsuba's own
|
||||||
|
(compact) binary representation -->
|
||||||
<shape type="serialized">
|
<shape type="serialized">
|
||||||
<string name="filename" value="lightsource.serialized"/>
|
<string name="filename" value="lightsource.serialized"/>
|
||||||
<transform name="toWorld">
|
<transform name="toWorld">
|
||||||
|
@ -134,18 +139,41 @@ these values are directly used. The renderer can also be configured to sample th
|
||||||
number of samples, in which case the RGB values are first converted into spectra using a simple heuristic.
|
number of samples, in which case the RGB values are first converted into spectra using a simple heuristic.
|
||||||
|
|
||||||
You can also directly supply the spectral color samples that Mitsuba internally uses if spectral rendering is
|
You can also directly supply the spectral color samples that Mitsuba internally uses if spectral rendering is
|
||||||
active. This unfortunately closely couples the interpretation of a scene to how Mitsuba is compiled, which can be a disadvantage.
|
active. This unfortunately closely couples the interpretation of a scene to how Mitsuba is compiled, which
|
||||||
For instance, the below example assumes that 6 spectral samples are being used:
|
can be a disadvantage.
|
||||||
|
For instance, the below example assumes that 6 spectral samples in the 400-700nm range are being used:
|
||||||
\begin{xml}
|
\begin{xml}
|
||||||
<spectrum name="spectrumProperty" value="0.2, 0.8, 0.4, 0.6, 0.1, 0.9"/>
|
<spectrum name="spectrumProperty" value="0.2, 0.8, 0.4, 0.6, 0.1, 0.9"/>
|
||||||
\end{xml}
|
\end{xml}
|
||||||
A safer way is to specify a linearly interpolated spectral power distribution, which is converted into
|
A safer way is to specify a linearly interpolated spectral power distribution, which is converted into
|
||||||
the internal spectral representation as the scene is loaded.
|
the internal spectral representation when the scene is loaded.
|
||||||
\begin{xml}
|
\begin{xml}
|
||||||
<spectrum name="spectrumProperty" value="400:0.56, 500:0.18, 600:0.58, 700:0.24"/>
|
<spectrum name="spectrumProperty" value="400:0.56, 500:0.18, 600:0.58, 700:0.24"/>
|
||||||
\end{xml}
|
\end{xml}
|
||||||
This is essentially a mapping from wavelength in nanometers (before the colon) to a reflectance or intensity value (after the colon). Missing values are linearly interpolated from the two closest neighbors.
|
This is essentially a mapping from wavelength in nanometers (before the colon) to a reflectance or
|
||||||
To specify a constant spectrum, simply provide just one value
|
intensity value (after the colon). Missing values are linearly interpolated from the two closest neighbors.
|
||||||
|
|
||||||
|
When spectral power distributions are obtained from measurements (e.g. at 10nm intervals), they are
|
||||||
|
usually quite unwiedy and can clutter the scene description. For this reason,
|
||||||
|
there is yet another way to pass a spectrum by loading it from an external
|
||||||
|
file:
|
||||||
|
\begin{xml}
|
||||||
|
<spectrum name="spectrumProperty" filename="measuredSpectrum.spd"/>
|
||||||
|
\end{xml}
|
||||||
|
The file should contain a single measurement per line, with the corresponding
|
||||||
|
wavelength in nanometers and the measured value separated by a space. Here is
|
||||||
|
an example:
|
||||||
|
\begin{xml}
|
||||||
|
406.13 0.703313
|
||||||
|
413.88 0.744563
|
||||||
|
422.03 0.791625
|
||||||
|
430.62 0.822125
|
||||||
|
435.09 0.834000
|
||||||
|
...
|
||||||
|
\end{xml}
|
||||||
|
|
||||||
|
It is also possible to specify a completely flat spectral power distribution by specifying
|
||||||
|
just one value:
|
||||||
\begin{xml}
|
\begin{xml}
|
||||||
<spectrum name="spectrumProperty" value="0.56"/>
|
<spectrum name="spectrumProperty" value="0.56"/>
|
||||||
\end{xml}
|
\end{xml}
|
||||||
|
@ -163,8 +191,8 @@ Points and vectors can be specified as follows:
|
||||||
It is important that whatever you choose as world-space units (meters, inches, etc.) is
|
It is important that whatever you choose as world-space units (meters, inches, etc.) is
|
||||||
used consistently in all places.
|
used consistently in all places.
|
||||||
\subsubsection{Transformations}
|
\subsubsection{Transformations}
|
||||||
Transformations are the only kind of property, which require more than a single tag. The idea is that, starting
|
Transformations are the only kind of property that require more than a single tag. The idea is that, starting
|
||||||
with the identity, you build up a transformation using nested commands. For instance, a transformation that
|
with the identity, one can build up a transformation using a sequence of commands. For instance, a transformation that
|
||||||
does a translation followed by a rotation might be written like this:
|
does a translation followed by a rotation might be written like this:
|
||||||
\begin{xml}
|
\begin{xml}
|
||||||
<transform name="trafoProperty">
|
<transform name="trafoProperty">
|
||||||
|
@ -211,9 +239,9 @@ of how this works:
|
||||||
<string name="filename" value="textures/myImage.jpg"/>
|
<string name="filename" value="textures/myImage.jpg"/>
|
||||||
</texture>
|
</texture>
|
||||||
|
|
||||||
<bsdf type="lambertian" id="myMaterial">
|
<bsdf type="diffuse" id="myMaterial">
|
||||||
<!-- Reference the texture named myImage and pass it
|
<!-- Reference the texture named myImage and pass it
|
||||||
to the BRDF as the reflectance channel -->
|
to the BRDF as the reflectance parameter -->
|
||||||
<ref name="reflectance" id="myImage"/>
|
<ref name="reflectance" id="myImage"/>
|
||||||
</bsdf>
|
</bsdf>
|
||||||
|
|
||||||
|
@ -225,14 +253,28 @@ of how this works:
|
||||||
</shape>
|
</shape>
|
||||||
</scene>
|
</scene>
|
||||||
\end{xml}
|
\end{xml}
|
||||||
Note that this feature cannot yet be used to do geometry instancing.
|
By providing a unique \texttt{id} attribute in the
|
||||||
|
object declaration, the object is bound to that identifier
|
||||||
|
upon instantiation.
|
||||||
|
Referencing this identifier at a later point (using the \texttt{<ref id="..."/>} tag)
|
||||||
|
will add the instance to the parent object, with no further memory
|
||||||
|
allocation taking place. Note that some plugins expect their child objects
|
||||||
|
to be named\footnote{For instance, material plugins such as \pluginref{diffuse} require that
|
||||||
|
nested texture instances explicitly specify the parameter to which they want to bind (e.g. ``\texttt{reflectance}'').}.
|
||||||
|
For this reason, a name can also be associated with the reference.
|
||||||
|
|
||||||
|
Note that while this feature is meant to efficiently handle materials,
|
||||||
|
textures, and participating media that are referenced from multiple places,
|
||||||
|
it cannot be used to instantiate geometry---if this functionality is needed,
|
||||||
|
take a look at the \pluginref{instance} plugin.
|
||||||
|
|
||||||
\subsection{Including external files}
|
\subsection{Including external files}
|
||||||
A scene can be split into multiple pieces for better readability.
|
A scene can be split into multiple pieces for better readability.
|
||||||
to include an external file, please use the following command:
|
to include an external file, please use the following command:
|
||||||
\begin{xml}
|
\begin{xml}
|
||||||
<include filename="nested-scene.xml"/>
|
<include filename="nested-scene.xml"/>
|
||||||
\end{xml}
|
\end{xml}
|
||||||
In this case, the file \code{nested-scene.xml} must still be a proper scene file with a \code{<scene>} tag at the root.
|
In this case, the file \code{nested-scene.xml} must be a proper scene file with a \code{<scene>} tag at the root.
|
||||||
This feature is sometimes very convenient in conjunction with the \code{-D key=value} flag of the \code{mitsuba} command line renderer (see the previous section for details).
|
This feature is sometimes very convenient in conjunction with the \code{-D key=value} flag of the \code{mitsuba} command line renderer (see the previous section for details).
|
||||||
This lets you include different parts of a scene configuration by changing the command line parameters (and without having to touch the XML file):
|
This lets you include different parts of a scene configuration by changing the command line parameters (and without having to touch the XML file):
|
||||||
\begin{xml}
|
\begin{xml}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
\section{Introduction}
|
\section{Introduction}
|
||||||
\textbf{Disclaimer:} This is manual is currently a work in progress --
|
\textbf{Disclaimer:} This is manual is currently a work in progress---please
|
||||||
please bear with me as things are coming together.
|
bear with me while things are coming together.
|
||||||
|
|
||||||
\subsection{About Mitsuba}
|
\subsection{About Mitsuba}
|
||||||
Mitsuba is an extensible rendering framework written in portable C++. It implements unbiased
|
Mitsuba is an extensible rendering framework written in portable C++. It implements unbiased
|
||||||
|
@ -11,5 +11,40 @@ In comparison to other open source renderers, Mitsuba places a strong emphasis o
|
||||||
rendering techniques, such as path-based formulations of Metropolis Light Transport and volumetric
|
rendering techniques, such as path-based formulations of Metropolis Light Transport and volumetric
|
||||||
modeling approaches.
|
modeling approaches.
|
||||||
|
|
||||||
|
\paragraph{Performance:}
|
||||||
|
One important goal of Mitsuba is to provide optimized implementations of the most commonly
|
||||||
|
used rendering algorithms. By virtue of running on a shared foundation, comparisons between them can
|
||||||
|
better highlight the merits and limitations of different approaches. This is in contrast to, say,
|
||||||
|
comparing two completely different rendering products, where technical information on the underlying
|
||||||
|
implementation is often intentionally not provided.
|
||||||
|
|
||||||
|
\paragraph{Robustness:}
|
||||||
|
In many cases, physically-based rendering packages force the user to model scenes with the underlying
|
||||||
|
algorithm (specifically: its convergence behavior) in mind. For instance, glass windows are routinely
|
||||||
|
replaced with light portals, photons must be manually guided to the relevant parts of a scene, and
|
||||||
|
interactions with complex materials are taboo, since they cannot be importance sampled exactly.
|
||||||
|
One focus of Mitsuba will be to develop path-space light transport algorithms, which handle such
|
||||||
|
cases more gracefully.
|
||||||
|
|
||||||
|
\paragraph{Scalability:} Mitsuba instances can be merged into large clusters, which transparently distribute and
|
||||||
|
jointly execute tasks assigned to them using only node-to-node communcation. It has successfully
|
||||||
|
scaled to large-scale renderings that involved 1024 cores working on a single image.
|
||||||
|
Most algorithms in Mitsuba are written using a generic parallelization layer, which can tap
|
||||||
|
into this cluster-wide parallelism. The principle is that if any component of the renderer produces
|
||||||
|
work that takes longer than a second or so, it at least ought to use all of the processing power
|
||||||
|
it can get.
|
||||||
|
|
||||||
|
The renderer also tries to be very conservative in its use of memory, which lets it handle
|
||||||
|
large scenes (>30 million triangles) and multi-gigabyte heterogeneous volumes on consumer hardware.
|
||||||
|
|
||||||
|
\paragraph{Usability:}
|
||||||
|
Mitsuba comes with a graphical user interface to interactively explore scenes. Once a suitable
|
||||||
|
viewpoint has been found, it is straightforward to perform renderings using any of the
|
||||||
|
implemented rendering techniques, while tweaking their parameters to find the most suitable
|
||||||
|
settings. Experimental integration into Blender 2.5 is also available.
|
||||||
|
|
||||||
\subsection{License}
|
\subsection{License}
|
||||||
Mitsuba is free software and can be redistributed and modified under the terms of the GNU General Public License (Version 3) as provided by the Free Software Foundation.
|
Mitsuba is free software and can be redistributed and modified under the terms of the GNU General
|
||||||
|
Public License (Version 3) as provided by the Free Software Foundation.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -101,7 +101,9 @@
|
||||||
keywords= [1] {
|
keywords= [1] {
|
||||||
shape,bsdf,scene,texture,phase,integer,float,
|
shape,bsdf,scene,texture,phase,integer,float,
|
||||||
string,transform,ref,rgb,srgb,spectrum,blackbody,
|
string,transform,ref,rgb,srgb,spectrum,blackbody,
|
||||||
medium,camera,film,sampler,integrator
|
medium,camera,film,sampler,integrator,luminaire,
|
||||||
|
translate,rotate,scale,lookAt,point,vector,matrix,
|
||||||
|
include
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,7 +142,7 @@
|
||||||
\include{introduction}
|
\include{introduction}
|
||||||
%\include{compiling}
|
%\include{compiling}
|
||||||
%\include{basics}
|
%\include{basics}
|
||||||
%\include{format}
|
\include{format}
|
||||||
\IfFileExists{plugins_generated.tex}{\include{plugins_generated}}{}
|
\IfFileExists{plugins_generated.tex}{\include{plugins_generated}}{}
|
||||||
%\include{import}
|
%\include{import}
|
||||||
%\include{development}
|
%\include{development}
|
||||||
|
|
|
@ -20,6 +20,8 @@
|
||||||
#define __CHI_SQUARE_TEST_H
|
#define __CHI_SQUARE_TEST_H
|
||||||
|
|
||||||
#include <mitsuba/core/fresolver.h>
|
#include <mitsuba/core/fresolver.h>
|
||||||
|
#include <mitsuba/render/common.h>
|
||||||
|
#include <boost/tuple/tuple.hpp>
|
||||||
#include <boost/function.hpp>
|
#include <boost/function.hpp>
|
||||||
|
|
||||||
MTS_NAMESPACE_BEGIN
|
MTS_NAMESPACE_BEGIN
|
||||||
|
@ -33,7 +35,8 @@ MTS_NAMESPACE_BEGIN
|
||||||
* This class performs a chi-square goodness-of-fit test of the null hypothesis
|
* This class performs a chi-square goodness-of-fit test of the null hypothesis
|
||||||
* that a specified sampling procedure produces samples that are distributed
|
* that a specified sampling procedure produces samples that are distributed
|
||||||
* according to a supplied density function. This is very useful to verify BRDF
|
* according to a supplied density function. This is very useful to verify BRDF
|
||||||
* and phase function sampling codes for their correctness.
|
* and phase function sampling codes for their correctness. Currently, it
|
||||||
|
* supports both 2D and discrete sampling methods and mixtures thereof.
|
||||||
*
|
*
|
||||||
* This implementation works by generating a large batch of samples, which are
|
* This implementation works by generating a large batch of samples, which are
|
||||||
* then accumulated into rectangular bins in spherical coordinates. To obtain
|
* then accumulated into rectangular bins in spherical coordinates. To obtain
|
||||||
|
@ -48,10 +51,10 @@ MTS_NAMESPACE_BEGIN
|
||||||
* // Sample a (optionally weighted) direction. A non-unity weight
|
* // Sample a (optionally weighted) direction. A non-unity weight
|
||||||
* // in the return value is needed when the sampling distribution
|
* // in the return value is needed when the sampling distribution
|
||||||
* // doesn't exactly match the implementation in pdf()
|
* // doesn't exactly match the implementation in pdf()
|
||||||
* std::pair<Vector, Float> generateSample() const;
|
* boost::tuple<Vector, Float, EMeasure> generateSample() const;
|
||||||
*
|
*
|
||||||
* /// Compute the probability density for the specified direction
|
* /// Compute the probability density for the specified direction and measure
|
||||||
* Float pdf(const Vector &direction) const;
|
* Float pdf(const Vector &direction, EMeasure) const;
|
||||||
* };
|
* };
|
||||||
* </code>
|
* </code>
|
||||||
*
|
*
|
||||||
|
@ -64,14 +67,13 @@ MTS_NAMESPACE_BEGIN
|
||||||
* // Initialize the tables used by the chi-square test
|
* // Initialize the tables used by the chi-square test
|
||||||
* chiSqr.fill(
|
* chiSqr.fill(
|
||||||
* boost::bind(&MyDistribution::generateSample, myDistrInstance),
|
* boost::bind(&MyDistribution::generateSample, myDistrInstance),
|
||||||
* boost::bind(&MyDistribution::pdf, myDistrInstance, _1)
|
* boost::bind(&MyDistribution::pdf, myDistrInstance, _1, _2)
|
||||||
* );
|
* );
|
||||||
*
|
*
|
||||||
* // Optional: dump the tables to a MATLAB file for external analysis
|
* // Optional: dump the tables to a MATLAB file for external analysis
|
||||||
* chiSqr.dumpTables("debug.m");
|
* chiSqr.dumpTables("debug.m");
|
||||||
*
|
*
|
||||||
* // (the following assumes that the distribution has 1 parameter, e.g. exponent value)
|
* if (!chiSqr.runTest())
|
||||||
* if (!chiSqr.runTest(1))
|
|
||||||
* Log(EError, "Uh oh -- test failed, the implementation is probably incorrect!");
|
* Log(EError, "Uh oh -- test failed, the implementation is probably incorrect!");
|
||||||
* </code>
|
* </code>
|
||||||
*/
|
*/
|
||||||
|
@ -139,8 +141,8 @@ public:
|
||||||
* on how to invoke this function
|
* on how to invoke this function
|
||||||
*/
|
*/
|
||||||
void fill(
|
void fill(
|
||||||
const boost::function<std::pair<Vector, Float>()> &sampleFn,
|
const boost::function<boost::tuple<Vector, Float, EMeasure>()> &sampleFn,
|
||||||
const boost::function<Float (const Vector &)> &pdfFn);
|
const boost::function<Float (const Vector &, EMeasure)> &pdfFn);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Dump the bin counts to a file using MATLAB format
|
* \brief Dump the bin counts to a file using MATLAB format
|
||||||
|
@ -150,11 +152,6 @@ public:
|
||||||
/**
|
/**
|
||||||
* \brief Perform the actual chi-square test
|
* \brief Perform the actual chi-square test
|
||||||
*
|
*
|
||||||
* \param distParams
|
|
||||||
* Number of parameters of the distribution in question.
|
|
||||||
* Anything such as lobe width, indices of refraction, etc.
|
|
||||||
* should be counted.
|
|
||||||
*
|
|
||||||
* \param pvalThresh
|
* \param pvalThresh
|
||||||
* The implementation will reject the null hypothesis
|
* The implementation will reject the null hypothesis
|
||||||
* when the computed p-value lies below this parameter
|
* when the computed p-value lies below this parameter
|
||||||
|
@ -162,7 +159,7 @@ public:
|
||||||
*
|
*
|
||||||
* \return A status value of type \ref ETestResult
|
* \return A status value of type \ref ETestResult
|
||||||
*/
|
*/
|
||||||
ETestResult runTest(int distParams, Float pvalThresh = 0.01f);
|
ETestResult runTest(Float pvalThresh = 0.01f);
|
||||||
|
|
||||||
MTS_DECLARE_CLASS()
|
MTS_DECLARE_CLASS()
|
||||||
protected:
|
protected:
|
||||||
|
@ -171,11 +168,12 @@ protected:
|
||||||
|
|
||||||
/// Functor to evaluate the pdf values in parallel using OpenMP
|
/// Functor to evaluate the pdf values in parallel using OpenMP
|
||||||
static void integrand(
|
static void integrand(
|
||||||
const boost::function<Float (const Vector &)> &pdfFn,
|
const boost::function<Float (const Vector &, EMeasure)> &pdfFn,
|
||||||
size_t nPts, const Float *in, Float *out) {
|
size_t nPts, const Float *in, Float *out) {
|
||||||
#pragma omp parallel for
|
#pragma omp parallel for
|
||||||
for (int i=0; i<(int) nPts; ++i)
|
for (int i=0; i<(int) nPts; ++i)
|
||||||
out[i] = pdfFn(sphericalDirection(in[2*i], in[2*i+1])) * std::sin(in[2*i]);
|
out[i] = pdfFn(sphericalDirection(in[2*i], in[2*i+1]), ESolidAngle)
|
||||||
|
* std::sin(in[2*i]);
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
ELogLevel m_logLevel;
|
ELogLevel m_logLevel;
|
||||||
|
|
|
@ -20,6 +20,9 @@
|
||||||
#define __SPECTRUM_H
|
#define __SPECTRUM_H
|
||||||
|
|
||||||
#include <mitsuba/mitsuba.h>
|
#include <mitsuba/mitsuba.h>
|
||||||
|
#include <boost/filesystem.hpp>
|
||||||
|
|
||||||
|
namespace fs = boost::filesystem;
|
||||||
|
|
||||||
#define SPECTRUM_MIN_WAVELENGTH 400
|
#define SPECTRUM_MIN_WAVELENGTH 400
|
||||||
#define SPECTRUM_MAX_WAVELENGTH 700
|
#define SPECTRUM_MAX_WAVELENGTH 700
|
||||||
|
@ -76,6 +79,10 @@ private:
|
||||||
/**
|
/**
|
||||||
* \brief Linearly interpolated spectral power distribution
|
* \brief Linearly interpolated spectral power distribution
|
||||||
*
|
*
|
||||||
|
* This class implements a linearly interpolated spectral
|
||||||
|
* power distribution that is defined over a discrete set of
|
||||||
|
* measurements at different wavelengths.
|
||||||
|
*
|
||||||
* \ingroup libcore
|
* \ingroup libcore
|
||||||
*/
|
*/
|
||||||
class MTS_EXPORT_CORE InterpolatedSpectrum : public SmoothSpectrum {
|
class MTS_EXPORT_CORE InterpolatedSpectrum : public SmoothSpectrum {
|
||||||
|
@ -89,6 +96,12 @@ public:
|
||||||
m_value.reserve(size);
|
m_value.reserve(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read an interpolated spectrum from a simple
|
||||||
|
* ASCII format
|
||||||
|
*/
|
||||||
|
InterpolatedSpectrum(const fs::path &path);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Append an entry to the spectral power distribution.
|
* \brief Append an entry to the spectral power distribution.
|
||||||
*
|
*
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
<xsd:element name="boolean" type="boolean"/>
|
<xsd:element name="boolean" type="boolean"/>
|
||||||
<xsd:element name="transform" type="transform"/>
|
<xsd:element name="transform" type="transform"/>
|
||||||
<xsd:element name="string" type="string"/>
|
<xsd:element name="string" type="string"/>
|
||||||
<xsd:element name="spectrum" type="string"/>
|
<xsd:element name="spectrum" type="spectrum"/>
|
||||||
<xsd:element name="rgb" type="string"/>
|
<xsd:element name="rgb" type="string"/>
|
||||||
<xsd:element name="srgb" type="string"/>
|
<xsd:element name="srgb" type="string"/>
|
||||||
<xsd:element name="blackbody" type="blackbody"/>
|
<xsd:element name="blackbody" type="blackbody"/>
|
||||||
|
@ -41,7 +41,7 @@
|
||||||
<xsd:element name="boolean" type="boolean"/>
|
<xsd:element name="boolean" type="boolean"/>
|
||||||
<xsd:element name="transform" type="transform"/>
|
<xsd:element name="transform" type="transform"/>
|
||||||
<xsd:element name="string" type="string"/>
|
<xsd:element name="string" type="string"/>
|
||||||
<xsd:element name="spectrum" type="string"/>
|
<xsd:element name="spectrum" type="spectrum"/>
|
||||||
<xsd:element name="rgb" type="string"/>
|
<xsd:element name="rgb" type="string"/>
|
||||||
<xsd:element name="srgb" type="string"/>
|
<xsd:element name="srgb" type="string"/>
|
||||||
<xsd:element name="blackbody" type="blackbody"/>
|
<xsd:element name="blackbody" type="blackbody"/>
|
||||||
|
@ -276,6 +276,12 @@
|
||||||
<xsd:attribute name="value" type="xsd:string" use="required"/>
|
<xsd:attribute name="value" type="xsd:string" use="required"/>
|
||||||
</xsd:complexType>
|
</xsd:complexType>
|
||||||
|
|
||||||
|
<xsd:complexType name="spectrum">
|
||||||
|
<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:complexType>
|
||||||
|
|
||||||
<xsd:complexType name="include">
|
<xsd:complexType name="include">
|
||||||
<xsd:attribute name="filename" type="xsd:string" use="required"/>
|
<xsd:attribute name="filename" type="xsd:string" use="required"/>
|
||||||
</xsd:complexType>
|
</xsd:complexType>
|
||||||
|
|
|
@ -21,9 +21,27 @@
|
||||||
#include <mitsuba/core/timer.h>
|
#include <mitsuba/core/timer.h>
|
||||||
#include <boost/math/distributions/chi_squared.hpp>
|
#include <boost/math/distributions/chi_squared.hpp>
|
||||||
#include <boost/bind.hpp>
|
#include <boost/bind.hpp>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
MTS_NAMESPACE_BEGIN
|
MTS_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
/* Simple ordering for storing vectors in a set */
|
||||||
|
struct VectorOrder {
|
||||||
|
inline int compare(const Vector &v1, const Vector &v2) const {
|
||||||
|
if (v1.x < v2.x) return -1;
|
||||||
|
else if (v1.x > v2.x) return 1;
|
||||||
|
if (v1.y < v2.y) return -1;
|
||||||
|
else if (v1.y > v2.y) return 1;
|
||||||
|
if (v1.z < v2.z) return -1;
|
||||||
|
else if (v1.z > v2.z) return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator()(const Vector &v1, const Vector &v2) const {
|
||||||
|
return compare(v1, v2) < 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
ChiSquare::ChiSquare(int thetaBins, int phiBins, int numTests,
|
ChiSquare::ChiSquare(int thetaBins, int phiBins, int numTests,
|
||||||
size_t sampleCount) : m_logLevel(EInfo), m_thetaBins(thetaBins),
|
size_t sampleCount) : m_logLevel(EInfo), m_thetaBins(thetaBins),
|
||||||
m_phiBins(phiBins), m_numTests(numTests), m_sampleCount(sampleCount) {
|
m_phiBins(phiBins), m_numTests(numTests), m_sampleCount(sampleCount) {
|
||||||
|
@ -69,29 +87,53 @@ void ChiSquare::dumpTables(const fs::path &filename) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChiSquare::fill(
|
void ChiSquare::fill(
|
||||||
const boost::function<std::pair<Vector, Float>()> &sampleFn,
|
const boost::function<boost::tuple<Vector, Float, EMeasure>()> &sampleFn,
|
||||||
const boost::function<Float (const Vector &)> &pdfFn) {
|
const boost::function<Float (const Vector &, EMeasure measure)> &pdfFn) {
|
||||||
memset(m_table, 0, m_thetaBins*m_phiBins*sizeof(Float));
|
memset(m_table, 0, m_thetaBins*m_phiBins*sizeof(Float));
|
||||||
|
memset(m_refTable, 0, m_thetaBins*m_phiBins*sizeof(Float));
|
||||||
|
|
||||||
Log(m_logLevel, "Accumulating " SIZE_T_FMT " samples into a %ix%i"
|
Log(m_logLevel, "Accumulating " SIZE_T_FMT " samples into a %ix%i"
|
||||||
" contingency table", m_sampleCount, m_thetaBins, m_phiBins);
|
" contingency table", m_sampleCount, m_thetaBins, m_phiBins);
|
||||||
Point2 factor(m_thetaBins / M_PI, m_phiBins / (2*M_PI));
|
Point2 factor(m_thetaBins / M_PI, m_phiBins / (2*M_PI));
|
||||||
|
|
||||||
|
std::set<Vector, VectorOrder> discreteDirections;
|
||||||
|
|
||||||
ref<Timer> timer = new Timer();
|
ref<Timer> timer = new Timer();
|
||||||
for (size_t i=0; i<m_sampleCount; ++i) {
|
for (size_t i=0; i<m_sampleCount; ++i) {
|
||||||
std::pair<Vector, Float> sample = sampleFn();
|
boost::tuple<Vector, Float, EMeasure> sample = sampleFn();
|
||||||
Point2 sphCoords = toSphericalCoordinates(sample.first);
|
Point2 sphCoords = toSphericalCoordinates(boost::get<0>(sample));
|
||||||
|
|
||||||
int thetaBin = std::min(std::max(0,
|
int thetaBin = std::min(std::max(0,
|
||||||
floorToInt(sphCoords.x * factor.x)), m_thetaBins-1);
|
floorToInt(sphCoords.x * factor.x)), m_thetaBins-1);
|
||||||
int phiBin = std::min(std::max(0,
|
int phiBin = std::min(std::max(0,
|
||||||
floorToInt(sphCoords.y * factor.y)), m_phiBins-1);
|
floorToInt(sphCoords.y * factor.y)), m_phiBins-1);
|
||||||
|
m_table[thetaBin * m_phiBins + phiBin] += boost::get<1>(sample);
|
||||||
m_table[thetaBin * m_phiBins + phiBin] += sample.second;
|
if (boost::get<1>(sample) > 0 && boost::get<2>(sample) == EDiscrete)
|
||||||
|
discreteDirections.insert(boost::get<0>(sample));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (discreteDirections.size() > 0) {
|
||||||
|
Log(EDebug, "Incorporating the disrete density over "
|
||||||
|
SIZE_T_FMT " direction(s) into the contingency table", discreteDirections.size());
|
||||||
|
for (std::set<Vector, VectorOrder>::const_iterator it = discreteDirections.begin();
|
||||||
|
it != discreteDirections.end(); ++it) {
|
||||||
|
const Vector &direction = *it;
|
||||||
|
Point2 sphCoords = toSphericalCoordinates(direction);
|
||||||
|
Float pdf = pdfFn(direction, EDiscrete);
|
||||||
|
|
||||||
|
int thetaBin = std::min(std::max(0,
|
||||||
|
floorToInt(sphCoords.x * factor.x)), m_thetaBins-1);
|
||||||
|
int phiBin = std::min(std::max(0,
|
||||||
|
floorToInt(sphCoords.y * factor.y)), m_phiBins-1);
|
||||||
|
|
||||||
|
m_refTable[thetaBin * m_phiBins + phiBin] += pdf * m_sampleCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
factor = Point2(M_PI / m_thetaBins, (2*M_PI) / m_phiBins);
|
factor = Point2(M_PI / m_thetaBins, (2*M_PI) / m_phiBins);
|
||||||
|
|
||||||
Log(m_logLevel, "Done, took %i ms. Integrating reference contingency table ..", timer->getMilliseconds());
|
Log(m_logLevel, "Done, took %i ms. Integrating reference "
|
||||||
|
"contingency table ..", timer->getMilliseconds());
|
||||||
timer->reset();
|
timer->reset();
|
||||||
Float min[2], max[2];
|
Float min[2], max[2];
|
||||||
size_t idx = 0;
|
size_t idx = 0;
|
||||||
|
@ -113,10 +155,11 @@ void ChiSquare::fill(
|
||||||
);
|
);
|
||||||
|
|
||||||
integral += result;
|
integral += result;
|
||||||
m_refTable[idx++] = result * m_sampleCount;
|
m_refTable[idx++] += result * m_sampleCount;
|
||||||
maxError = std::max(maxError, error);
|
maxError = std::max(maxError, error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Log(m_logLevel, "Done, took %i ms (max error = %f, integral=%f).",
|
Log(m_logLevel, "Done, took %i ms (max error = %f, integral=%f).",
|
||||||
timer->getMilliseconds(), maxError, integral);
|
timer->getMilliseconds(), maxError, integral);
|
||||||
}
|
}
|
||||||
|
@ -132,7 +175,7 @@ struct SortedCellFunctor {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ChiSquare::ETestResult ChiSquare::runTest(int distParams, Float pvalThresh) {
|
ChiSquare::ETestResult ChiSquare::runTest(Float pvalThresh) {
|
||||||
/* Compute the chi-square statistic */
|
/* Compute the chi-square statistic */
|
||||||
Float pooledCounts = 0, pooledRef = 0, chsq = 0.0f;
|
Float pooledCounts = 0, pooledRef = 0, chsq = 0.0f;
|
||||||
int pooledCells = 0, df = 0;
|
int pooledCells = 0, df = 0;
|
||||||
|
@ -188,12 +231,14 @@ ChiSquare::ETestResult ChiSquare::runTest(int distParams, Float pvalThresh) {
|
||||||
++df;
|
++df;
|
||||||
}
|
}
|
||||||
|
|
||||||
df -= distParams + 1;
|
/* All parameters are assumed to be known, so there is no
|
||||||
|
DF reduction due to model parameters */
|
||||||
|
df -= 1;
|
||||||
|
|
||||||
Log(m_logLevel, "Chi-square statistic = %e (df=%i)", chsq, df);
|
Log(m_logLevel, "Chi-square statistic = %e (df=%i)", chsq, df);
|
||||||
|
|
||||||
if (df <= 0) {
|
if (df <= 0) {
|
||||||
Log(EWarn, "The number of degrees of freedom (%i) is too low!", df);
|
Log(m_logLevel, "The number of degrees of freedom (%i) is too low!", df);
|
||||||
return ELowDoF;
|
return ELowDoF;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <mitsuba/mitsuba.h>
|
#include <mitsuba/mitsuba.h>
|
||||||
|
#include <boost/filesystem/fstream.hpp>
|
||||||
|
|
||||||
MTS_NAMESPACE_BEGIN
|
MTS_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
@ -47,7 +48,7 @@ void Spectrum::staticInitialization() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Spectrum::staticShutdown() {
|
void Spectrum::staticShutdown() {
|
||||||
/* Do nothing */
|
/* Nothing to do */
|
||||||
}
|
}
|
||||||
|
|
||||||
void Spectrum::fromSmoothSpectrum(const SmoothSpectrum *smooth) {
|
void Spectrum::fromSmoothSpectrum(const SmoothSpectrum *smooth) {
|
||||||
|
@ -261,8 +262,23 @@ Float BlackBodySpectrum::eval(Float l) const {
|
||||||
return (Float) I;
|
return (Float) I;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
InterpolatedSpectrum::InterpolatedSpectrum(const fs::path &path) {
|
||||||
|
fs::ifstream is(path);
|
||||||
|
if (is.bad() || is.fail())
|
||||||
|
SLog(EError, "InterpolatedSpectrum: could not open \"%s\"",
|
||||||
|
path.file_string().c_str());
|
||||||
|
|
||||||
|
while (is.good() && !is.eof()) {
|
||||||
|
Float lambda, value;
|
||||||
|
is >> lambda >> value;
|
||||||
|
appendSample(lambda, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void InterpolatedSpectrum::appendSample(Float lambda, Float value) {
|
void InterpolatedSpectrum::appendSample(Float lambda, Float value) {
|
||||||
SAssert(m_wavelength.size() == 0 || m_wavelength[m_wavelength.size()-1] < lambda);
|
if (m_wavelength.size() != 0 && m_wavelength[m_wavelength.size()-1] >= lambda)
|
||||||
|
SLog(EError, "InterpolatedSpectrum: spectral power distribution values must "
|
||||||
|
"be provided in order of increasing wavelength!");
|
||||||
m_wavelength.push_back(lambda);
|
m_wavelength.push_back(lambda);
|
||||||
m_value.push_back(value);
|
m_value.push_back(value);
|
||||||
}
|
}
|
||||||
|
|
|
@ -344,36 +344,50 @@ void SceneHandler::endElement(const XMLCh* const xmlName) {
|
||||||
discrete.fromSmoothSpectrum(&bb);
|
discrete.fromSmoothSpectrum(&bb);
|
||||||
context.parent->properties.setSpectrum(context.attributes["name"], discrete);
|
context.parent->properties.setSpectrum(context.attributes["name"], discrete);
|
||||||
} else if (name == "spectrum") {
|
} else if (name == "spectrum") {
|
||||||
std::vector<std::string> tokens = tokenize(
|
bool hasValue = context.attributes.find("value") != context.attributes.end();
|
||||||
context.attributes["value"], ", ");
|
bool hasFilename = context.attributes.find("filename") != context.attributes.end();
|
||||||
Float value[SPECTRUM_SAMPLES];
|
|
||||||
if (tokens.size() == 1) {
|
if (hasValue == hasFilename) {
|
||||||
value[0] = parseFloat(name, tokens[0]);
|
SLog(EError, "Spectrum: please provide one of 'value' or 'filename'");
|
||||||
context.parent->properties.setSpectrum(context.attributes["name"],
|
} else if (hasFilename) {
|
||||||
Spectrum(value[0]));
|
FileResolver *resolver = Thread::getThread()->getFileResolver();
|
||||||
} else {
|
fs::path path = resolver->resolve(context.attributes["filename"]);
|
||||||
if (tokens[0].find(':') != std::string::npos) {
|
InterpolatedSpectrum interp(path);
|
||||||
InterpolatedSpectrum interp(tokens.size());
|
Spectrum discrete;
|
||||||
/* Wavelength -> Value mapping */
|
discrete.fromSmoothSpectrum(&interp);
|
||||||
for (size_t i=0; i<tokens.size(); i++) {
|
context.parent->properties.setSpectrum(context.attributes["name"], discrete);
|
||||||
std::vector<std::string> tokens2 = tokenize(tokens[i], ":");
|
} else if (hasValue) {
|
||||||
if (tokens2.size() != 2)
|
std::vector<std::string> tokens = tokenize(
|
||||||
XMLLog(EError, "Invalid spectrum->value mapping specified");
|
context.attributes["value"], ", ");
|
||||||
Float wavelength = parseFloat(name, tokens2[0]);
|
Float value[SPECTRUM_SAMPLES];
|
||||||
Float value = parseFloat(name, tokens2[1]);
|
if (tokens.size() == 1) {
|
||||||
interp.appendSample(wavelength, value);
|
value[0] = parseFloat(name, tokens[0]);
|
||||||
}
|
|
||||||
Spectrum discrete;
|
|
||||||
discrete.fromSmoothSpectrum(&interp);
|
|
||||||
context.parent->properties.setSpectrum(context.attributes["name"],
|
context.parent->properties.setSpectrum(context.attributes["name"],
|
||||||
discrete);
|
Spectrum(value[0]));
|
||||||
} else {
|
} else {
|
||||||
if (tokens.size() != SPECTRUM_SAMPLES)
|
if (tokens[0].find(':') != std::string::npos) {
|
||||||
XMLLog(EError, "Invalid spectrum value specified (incorrect length)");
|
InterpolatedSpectrum interp(tokens.size());
|
||||||
for (int i=0; i<SPECTRUM_SAMPLES; i++)
|
/* Wavelength -> Value mapping */
|
||||||
value[i] = parseFloat(name, tokens[i]);
|
for (size_t i=0; i<tokens.size(); i++) {
|
||||||
context.parent->properties.setSpectrum(context.attributes["name"],
|
std::vector<std::string> tokens2 = tokenize(tokens[i], ":");
|
||||||
Spectrum(value));
|
if (tokens2.size() != 2)
|
||||||
|
XMLLog(EError, "Invalid spectrum->value mapping specified");
|
||||||
|
Float wavelength = parseFloat(name, tokens2[0]);
|
||||||
|
Float value = parseFloat(name, tokens2[1]);
|
||||||
|
interp.appendSample(wavelength, value);
|
||||||
|
}
|
||||||
|
Spectrum discrete;
|
||||||
|
discrete.fromSmoothSpectrum(&interp);
|
||||||
|
context.parent->properties.setSpectrum(context.attributes["name"],
|
||||||
|
discrete);
|
||||||
|
} else {
|
||||||
|
if (tokens.size() != SPECTRUM_SAMPLES)
|
||||||
|
XMLLog(EError, "Invalid spectrum value specified (incorrect length)");
|
||||||
|
for (int i=0; i<SPECTRUM_SAMPLES; i++)
|
||||||
|
value[i] = parseFloat(name, tokens[i]);
|
||||||
|
context.parent->properties.setSpectrum(context.attributes["name"],
|
||||||
|
Spectrum(value));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (name == "transform") {
|
} else if (name == "transform") {
|
||||||
|
|
|
@ -101,7 +101,7 @@ public:
|
||||||
m_fakeSampler = new FakeSampler(m_sampler);
|
m_fakeSampler = new FakeSampler(m_sampler);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<Vector, Float> generateSample() {
|
boost::tuple<Vector, Float, EMeasure> generateSample() {
|
||||||
Point2 sample(m_sampler->next2D());
|
Point2 sample(m_sampler->next2D());
|
||||||
Intersection its;
|
Intersection its;
|
||||||
BSDFQueryRecord bRec(its);
|
BSDFQueryRecord bRec(its);
|
||||||
|
@ -136,7 +136,7 @@ public:
|
||||||
#if defined(MTS_DEBUG_FP)
|
#if defined(MTS_DEBUG_FP)
|
||||||
disableFPExceptions();
|
disableFPExceptions();
|
||||||
#endif
|
#endif
|
||||||
return std::make_pair(bRec.wo, 0.0f);
|
return boost::make_tuple(bRec.wo, 0.0f, ESolidAngle);
|
||||||
} else if (sampled.isZero()) {
|
} else if (sampled.isZero()) {
|
||||||
if (!f.isZero() && pdfVal != 0)
|
if (!f.isZero() && pdfVal != 0)
|
||||||
Log(EWarn, "Inconsistency (2): f=%s, pdf=%f, sampled f/pdf=%s, bRec=%s",
|
Log(EWarn, "Inconsistency (2): f=%s, pdf=%f, sampled f/pdf=%s, bRec=%s",
|
||||||
|
@ -144,14 +144,14 @@ public:
|
||||||
#if defined(MTS_DEBUG_FP)
|
#if defined(MTS_DEBUG_FP)
|
||||||
disableFPExceptions();
|
disableFPExceptions();
|
||||||
#endif
|
#endif
|
||||||
return std::make_pair(bRec.wo, 0.0f);
|
return boost::make_tuple(bRec.wo, 0.0f, ESolidAngle);
|
||||||
}
|
}
|
||||||
|
|
||||||
Spectrum sampled2 = f/pdfVal;
|
Spectrum sampled2 = f/pdfVal;
|
||||||
if (!sampled.isValid() || !sampled2.isValid()) {
|
if (!sampled.isValid() || !sampled2.isValid()) {
|
||||||
Log(EWarn, "Ooops: f=%s, pdf=%f, sampled f/pdf=%s, bRec=%s",
|
Log(EWarn, "Ooops: f=%s, pdf=%f, sampled f/pdf=%s, bRec=%s",
|
||||||
f.toString().c_str(), pdfVal, sampled.toString().c_str(), bRec.toString().c_str());
|
f.toString().c_str(), pdfVal, sampled.toString().c_str(), bRec.toString().c_str());
|
||||||
return std::make_pair(bRec.wo, 0.0f);
|
return boost::make_tuple(bRec.wo, 0.0f, ESolidAngle);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool mismatch = false;
|
bool mismatch = false;
|
||||||
|
@ -175,10 +175,11 @@ public:
|
||||||
disableFPExceptions();
|
disableFPExceptions();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return std::make_pair(bRec.wo, 1.0f);
|
return boost::make_tuple(bRec.wo, 1.0f,
|
||||||
|
BSDF::getMeasure(bRec.sampledType));
|
||||||
}
|
}
|
||||||
|
|
||||||
Float pdf(const Vector &wo) {
|
Float pdf(const Vector &wo, EMeasure measure) {
|
||||||
Intersection its;
|
Intersection its;
|
||||||
BSDFQueryRecord bRec(its);
|
BSDFQueryRecord bRec(its);
|
||||||
bRec.component = m_component;
|
bRec.component = m_component;
|
||||||
|
@ -191,9 +192,10 @@ public:
|
||||||
enableFPExceptions();
|
enableFPExceptions();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (m_bsdf->eval(bRec).isZero())
|
if (m_bsdf->eval(bRec, measure).isZero())
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
Float result = m_bsdf->pdf(bRec);
|
|
||||||
|
Float result = m_bsdf->pdf(bRec, measure);
|
||||||
|
|
||||||
#if defined(MTS_DEBUG_FP)
|
#if defined(MTS_DEBUG_FP)
|
||||||
disableFPExceptions();
|
disableFPExceptions();
|
||||||
|
@ -220,7 +222,7 @@ public:
|
||||||
: m_mRec(mRec), m_phase(phase), m_sampler(sampler), m_wi(wi),
|
: m_mRec(mRec), m_phase(phase), m_sampler(sampler), m_wi(wi),
|
||||||
m_largestWeight(0) { }
|
m_largestWeight(0) { }
|
||||||
|
|
||||||
std::pair<Vector, Float> generateSample() {
|
boost::tuple<Vector, Float, EMeasure> generateSample() {
|
||||||
Point2 sample(m_sampler->next2D());
|
Point2 sample(m_sampler->next2D());
|
||||||
PhaseFunctionQueryRecord pRec(m_mRec, m_wi);
|
PhaseFunctionQueryRecord pRec(m_mRec, m_wi);
|
||||||
|
|
||||||
|
@ -240,7 +242,7 @@ public:
|
||||||
#if defined(MTS_DEBUG_FP)
|
#if defined(MTS_DEBUG_FP)
|
||||||
disableFPExceptions();
|
disableFPExceptions();
|
||||||
#endif
|
#endif
|
||||||
return std::make_pair(pRec.wo, 0.0f);
|
return boost::make_tuple(pRec.wo, 0.0f, ESolidAngle);
|
||||||
} else if (sampled == 0) {
|
} else if (sampled == 0) {
|
||||||
if (f != 0 && pdfVal != 0)
|
if (f != 0 && pdfVal != 0)
|
||||||
Log(EWarn, "Inconsistency: f=%f, pdf=%f, sampled f/pdf=%f",
|
Log(EWarn, "Inconsistency: f=%f, pdf=%f, sampled f/pdf=%f",
|
||||||
|
@ -248,7 +250,7 @@ public:
|
||||||
#if defined(MTS_DEBUG_FP)
|
#if defined(MTS_DEBUG_FP)
|
||||||
disableFPExceptions();
|
disableFPExceptions();
|
||||||
#endif
|
#endif
|
||||||
return std::make_pair(pRec.wo, 0.0f);
|
return boost::make_tuple(pRec.wo, 0.0f, ESolidAngle);
|
||||||
}
|
}
|
||||||
|
|
||||||
Float sampled2 = f/pdfVal;
|
Float sampled2 = f/pdfVal;
|
||||||
|
@ -271,10 +273,13 @@ public:
|
||||||
#if defined(MTS_DEBUG_FP)
|
#if defined(MTS_DEBUG_FP)
|
||||||
disableFPExceptions();
|
disableFPExceptions();
|
||||||
#endif
|
#endif
|
||||||
return std::make_pair(pRec.wo, 1.0f);
|
return boost::make_tuple(pRec.wo, 1.0f, ESolidAngle);
|
||||||
}
|
}
|
||||||
|
|
||||||
Float pdf(const Vector &wo) const {
|
Float pdf(const Vector &wo, EMeasure measure) const {
|
||||||
|
if (measure != ESolidAngle)
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
PhaseFunctionQueryRecord pRec(m_mRec, m_wi, wo);
|
PhaseFunctionQueryRecord pRec(m_mRec, m_wi, wo);
|
||||||
#if defined(MTS_DEBUG_FP)
|
#if defined(MTS_DEBUG_FP)
|
||||||
enableFPExceptions();
|
enableFPExceptions();
|
||||||
|
@ -351,11 +356,11 @@ public:
|
||||||
// Initialize the tables used by the chi-square test
|
// Initialize the tables used by the chi-square test
|
||||||
chiSqr->fill(
|
chiSqr->fill(
|
||||||
boost::bind(&BSDFAdapter::generateSample, &adapter),
|
boost::bind(&BSDFAdapter::generateSample, &adapter),
|
||||||
boost::bind(&BSDFAdapter::pdf, &adapter, _1)
|
boost::bind(&BSDFAdapter::pdf, &adapter, _1, _2)
|
||||||
);
|
);
|
||||||
|
|
||||||
// (the following assumes that the distribution has 1 parameter, e.g. exponent value)
|
// (the following assumes that the distribution has 1 parameter, e.g. exponent value)
|
||||||
ChiSquare::ETestResult result = chiSqr->runTest(1, SIGNIFICANCE_LEVEL);
|
ChiSquare::ETestResult result = chiSqr->runTest(SIGNIFICANCE_LEVEL);
|
||||||
if (result == ChiSquare::EReject) {
|
if (result == ChiSquare::EReject) {
|
||||||
std::string filename = formatString("failure_%i.m", failureCount++);
|
std::string filename = formatString("failure_%i.m", failureCount++);
|
||||||
chiSqr->dumpTables(filename);
|
chiSqr->dumpTables(filename);
|
||||||
|
@ -394,11 +399,11 @@ public:
|
||||||
// Initialize the tables used by the chi-square test
|
// Initialize the tables used by the chi-square test
|
||||||
chiSqr->fill(
|
chiSqr->fill(
|
||||||
boost::bind(&BSDFAdapter::generateSample, &adapter),
|
boost::bind(&BSDFAdapter::generateSample, &adapter),
|
||||||
boost::bind(&BSDFAdapter::pdf, &adapter, _1)
|
boost::bind(&BSDFAdapter::pdf, &adapter, _1, _2)
|
||||||
);
|
);
|
||||||
|
|
||||||
// (the following assumes that the distribution has 1 parameter, e.g. exponent value)
|
// (the following assumes that the distribution has 1 parameter, e.g. exponent value)
|
||||||
ChiSquare::ETestResult result = chiSqr->runTest(1, SIGNIFICANCE_LEVEL);
|
ChiSquare::ETestResult result = chiSqr->runTest(SIGNIFICANCE_LEVEL);
|
||||||
if (result == ChiSquare::EReject) {
|
if (result == ChiSquare::EReject) {
|
||||||
std::string filename = formatString("failure_%i.m", failureCount++);
|
std::string filename = formatString("failure_%i.m", failureCount++);
|
||||||
chiSqr->dumpTables(filename);
|
chiSqr->dumpTables(filename);
|
||||||
|
@ -460,11 +465,11 @@ public:
|
||||||
// Initialize the tables used by the chi-square test
|
// Initialize the tables used by the chi-square test
|
||||||
chiSqr->fill(
|
chiSqr->fill(
|
||||||
boost::bind(&PhaseFunctionAdapter::generateSample, &adapter),
|
boost::bind(&PhaseFunctionAdapter::generateSample, &adapter),
|
||||||
boost::bind(&PhaseFunctionAdapter::pdf, &adapter, _1)
|
boost::bind(&PhaseFunctionAdapter::pdf, &adapter, _1, _2)
|
||||||
);
|
);
|
||||||
|
|
||||||
// (the following assumes that the distribution has 1 parameter, e.g. exponent value)
|
// (the following assumes that the distribution has 1 parameter, e.g. exponent value)
|
||||||
ChiSquare::ETestResult result = chiSqr->runTest(1, SIGNIFICANCE_LEVEL);
|
ChiSquare::ETestResult result = chiSqr->runTest(SIGNIFICANCE_LEVEL);
|
||||||
if (result == ChiSquare::EReject) {
|
if (result == ChiSquare::EReject) {
|
||||||
std::string filename = formatString("failure_%i.m", failureCount++);
|
std::string filename = formatString("failure_%i.m", failureCount++);
|
||||||
chiSqr->dumpTables(filename);
|
chiSqr->dumpTables(filename);
|
||||||
|
|
Loading…
Reference in New Issue