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.
|
||||
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
|
||||
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
|
||||
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.
|
||||
|
||||
This example already contains the most important things to know about format: you can have
|
||||
\emph{objects} (such as the objects instantiated by the \code{scene} or \code{shape} tags), which are allowed to be nested within
|
||||
each other. Each object optionally accepts \emph{properties} (such as the \code{string} tag),
|
||||
which further characterize its behavior. All objects except for the root object (the \code{scene})
|
||||
cause the renderer to load and instantiate a plugin, hence you must provide the plugin name using
|
||||
\code{type=".."} parameter.
|
||||
\emph{objects} (such as the objects instantiated by the \code{scene} or \code{shape} tags),
|
||||
which are allowed to be nested within each other. Each object optionally accepts \emph{properties}
|
||||
(such as the \code{string} tag), which further characterize its behavior. All objects except
|
||||
for the root object (the \code{scene}) cause the renderer to search and load a plugin from disk,
|
||||
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,
|
||||
any plugin loaded using the \code{shape} tag must conform to the \emph{Shape} interface, which
|
||||
the certainly case for the plugin named \code{obj} (it contains a WaveFront OBJ loader).
|
||||
any plugin loaded using the \code{shape} tag must conform to the \emph{Shape} interface, which is
|
||||
certainly the case for the plugin named \code{obj} (it contains a WaveFront OBJ loader).
|
||||
Similarly, you could write
|
||||
\begin{xml}
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
@ -41,16 +41,20 @@ Similarly, you could write
|
|||
\end{xml}
|
||||
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 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
|
||||
and one or more luminaires. Here is a more complex example:
|
||||
\begin{xml}
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<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"/>
|
||||
</integrator>
|
||||
|
||||
<!-- Instantiate a perspective camera with 45 degrees field of view -->
|
||||
<camera type="perspective">
|
||||
<!-- 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>
|
||||
</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">
|
||||
<string name="filename" value="dragon.obj"/>
|
||||
|
||||
<bsdf type="roughglass">
|
||||
<bsdf type="roughdielectric">
|
||||
<!-- Tweak the roughness parameter of the material -->
|
||||
<float name="alphaB" value="0.01"/>
|
||||
<float name="alpha" value="0.01"/>
|
||||
</bsdf>
|
||||
</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">
|
||||
<string name="filename" value="lightsource.serialized"/>
|
||||
<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.
|
||||
|
||||
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.
|
||||
For instance, the below example assumes that 6 spectral samples are being used:
|
||||
active. This unfortunately closely couples the interpretation of a scene to how Mitsuba is compiled, which
|
||||
can be a disadvantage.
|
||||
For instance, the below example assumes that 6 spectral samples in the 400-700nm range are being used:
|
||||
\begin{xml}
|
||||
<spectrum name="spectrumProperty" value="0.2, 0.8, 0.4, 0.6, 0.1, 0.9"/>
|
||||
\end{xml}
|
||||
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}
|
||||
<spectrum name="spectrumProperty" value="400:0.56, 500:0.18, 600:0.58, 700:0.24"/>
|
||||
\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.
|
||||
To specify a constant spectrum, simply provide just one value
|
||||
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.
|
||||
|
||||
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}
|
||||
<spectrum name="spectrumProperty" value="0.56"/>
|
||||
\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
|
||||
used consistently in all places.
|
||||
\subsubsection{Transformations}
|
||||
Transformations are the only kind of property, which 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
|
||||
Transformations are the only kind of property that require more than a single tag. The idea is that, starting
|
||||
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:
|
||||
\begin{xml}
|
||||
<transform name="trafoProperty">
|
||||
|
@ -211,9 +239,9 @@ of how this works:
|
|||
<string name="filename" value="textures/myImage.jpg"/>
|
||||
</texture>
|
||||
|
||||
<bsdf type="lambertian" id="myMaterial">
|
||||
<bsdf type="diffuse" id="myMaterial">
|
||||
<!-- 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"/>
|
||||
</bsdf>
|
||||
|
||||
|
@ -225,14 +253,28 @@ of how this works:
|
|||
</shape>
|
||||
</scene>
|
||||
\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}
|
||||
A scene can be split into multiple pieces for better readability.
|
||||
to include an external file, please use the following command:
|
||||
\begin{xml}
|
||||
<include filename="nested-scene.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 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}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
\section{Introduction}
|
||||
\textbf{Disclaimer:} This is manual is currently a work in progress --
|
||||
please bear with me as things are coming together.
|
||||
\textbf{Disclaimer:} This is manual is currently a work in progress---please
|
||||
bear with me while things are coming together.
|
||||
|
||||
\subsection{About Mitsuba}
|
||||
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
|
||||
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}
|
||||
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] {
|
||||
shape,bsdf,scene,texture,phase,integer,float,
|
||||
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{compiling}
|
||||
%\include{basics}
|
||||
%\include{format}
|
||||
\include{format}
|
||||
\IfFileExists{plugins_generated.tex}{\include{plugins_generated}}{}
|
||||
%\include{import}
|
||||
%\include{development}
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
#define __CHI_SQUARE_TEST_H
|
||||
|
||||
#include <mitsuba/core/fresolver.h>
|
||||
#include <mitsuba/render/common.h>
|
||||
#include <boost/tuple/tuple.hpp>
|
||||
#include <boost/function.hpp>
|
||||
|
||||
MTS_NAMESPACE_BEGIN
|
||||
|
@ -33,7 +35,8 @@ MTS_NAMESPACE_BEGIN
|
|||
* This class performs a chi-square goodness-of-fit test of the null hypothesis
|
||||
* that a specified sampling procedure produces samples that are distributed
|
||||
* 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
|
||||
* 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
|
||||
* // in the return value is needed when the sampling distribution
|
||||
* // 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
|
||||
* Float pdf(const Vector &direction) const;
|
||||
* /// Compute the probability density for the specified direction and measure
|
||||
* Float pdf(const Vector &direction, EMeasure) const;
|
||||
* };
|
||||
* </code>
|
||||
*
|
||||
|
@ -64,14 +67,13 @@ MTS_NAMESPACE_BEGIN
|
|||
* // Initialize the tables used by the chi-square test
|
||||
* chiSqr.fill(
|
||||
* 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
|
||||
* chiSqr.dumpTables("debug.m");
|
||||
*
|
||||
* // (the following assumes that the distribution has 1 parameter, e.g. exponent value)
|
||||
* if (!chiSqr.runTest(1))
|
||||
* if (!chiSqr.runTest())
|
||||
* Log(EError, "Uh oh -- test failed, the implementation is probably incorrect!");
|
||||
* </code>
|
||||
*/
|
||||
|
@ -139,8 +141,8 @@ public:
|
|||
* on how to invoke this function
|
||||
*/
|
||||
void fill(
|
||||
const boost::function<std::pair<Vector, Float>()> &sampleFn,
|
||||
const boost::function<Float (const Vector &)> &pdfFn);
|
||||
const boost::function<boost::tuple<Vector, Float, EMeasure>()> &sampleFn,
|
||||
const boost::function<Float (const Vector &, EMeasure)> &pdfFn);
|
||||
|
||||
/**
|
||||
* \brief Dump the bin counts to a file using MATLAB format
|
||||
|
@ -150,11 +152,6 @@ public:
|
|||
/**
|
||||
* \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
|
||||
* The implementation will reject the null hypothesis
|
||||
* when the computed p-value lies below this parameter
|
||||
|
@ -162,7 +159,7 @@ public:
|
|||
*
|
||||
* \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()
|
||||
protected:
|
||||
|
@ -171,11 +168,12 @@ protected:
|
|||
|
||||
/// Functor to evaluate the pdf values in parallel using OpenMP
|
||||
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) {
|
||||
#pragma omp parallel for
|
||||
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:
|
||||
ELogLevel m_logLevel;
|
||||
|
|
|
@ -20,6 +20,9 @@
|
|||
#define __SPECTRUM_H
|
||||
|
||||
#include <mitsuba/mitsuba.h>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
#define SPECTRUM_MIN_WAVELENGTH 400
|
||||
#define SPECTRUM_MAX_WAVELENGTH 700
|
||||
|
@ -76,6 +79,10 @@ private:
|
|||
/**
|
||||
* \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
|
||||
*/
|
||||
class MTS_EXPORT_CORE InterpolatedSpectrum : public SmoothSpectrum {
|
||||
|
@ -89,6 +96,12 @@ public:
|
|||
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.
|
||||
*
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
<xsd:element name="boolean" type="boolean"/>
|
||||
<xsd:element name="transform" type="transform"/>
|
||||
<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="srgb" type="string"/>
|
||||
<xsd:element name="blackbody" type="blackbody"/>
|
||||
|
@ -41,7 +41,7 @@
|
|||
<xsd:element name="boolean" type="boolean"/>
|
||||
<xsd:element name="transform" type="transform"/>
|
||||
<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="srgb" type="string"/>
|
||||
<xsd:element name="blackbody" type="blackbody"/>
|
||||
|
@ -276,6 +276,12 @@
|
|||
<xsd:attribute name="value" type="xsd:string" use="required"/>
|
||||
</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:attribute name="filename" type="xsd:string" use="required"/>
|
||||
</xsd:complexType>
|
||||
|
|
|
@ -21,8 +21,26 @@
|
|||
#include <mitsuba/core/timer.h>
|
||||
#include <boost/math/distributions/chi_squared.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
#include <set>
|
||||
|
||||
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,
|
||||
size_t sampleCount) : m_logLevel(EInfo), m_thetaBins(thetaBins),
|
||||
|
@ -69,29 +87,53 @@ void ChiSquare::dumpTables(const fs::path &filename) {
|
|||
}
|
||||
|
||||
void ChiSquare::fill(
|
||||
const boost::function<std::pair<Vector, Float>()> &sampleFn,
|
||||
const boost::function<Float (const Vector &)> &pdfFn) {
|
||||
const boost::function<boost::tuple<Vector, Float, EMeasure>()> &sampleFn,
|
||||
const boost::function<Float (const Vector &, EMeasure measure)> &pdfFn) {
|
||||
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"
|
||||
" contingency table", m_sampleCount, m_thetaBins, m_phiBins);
|
||||
Point2 factor(m_thetaBins / M_PI, m_phiBins / (2*M_PI));
|
||||
|
||||
std::set<Vector, VectorOrder> discreteDirections;
|
||||
|
||||
ref<Timer> timer = new Timer();
|
||||
for (size_t i=0; i<m_sampleCount; ++i) {
|
||||
std::pair<Vector, Float> sample = sampleFn();
|
||||
Point2 sphCoords = toSphericalCoordinates(sample.first);
|
||||
boost::tuple<Vector, Float, EMeasure> sample = sampleFn();
|
||||
Point2 sphCoords = toSphericalCoordinates(boost::get<0>(sample));
|
||||
|
||||
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_table[thetaBin * m_phiBins + phiBin] += sample.second;
|
||||
m_table[thetaBin * m_phiBins + phiBin] += boost::get<1>(sample);
|
||||
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);
|
||||
|
||||
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();
|
||||
Float min[2], max[2];
|
||||
size_t idx = 0;
|
||||
|
@ -113,10 +155,11 @@ void ChiSquare::fill(
|
|||
);
|
||||
|
||||
integral += result;
|
||||
m_refTable[idx++] = result * m_sampleCount;
|
||||
m_refTable[idx++] += result * m_sampleCount;
|
||||
maxError = std::max(maxError, error);
|
||||
}
|
||||
}
|
||||
|
||||
Log(m_logLevel, "Done, took %i ms (max error = %f, integral=%f).",
|
||||
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 */
|
||||
Float pooledCounts = 0, pooledRef = 0, chsq = 0.0f;
|
||||
int pooledCells = 0, df = 0;
|
||||
|
@ -188,12 +231,14 @@ ChiSquare::ETestResult ChiSquare::runTest(int distParams, Float pvalThresh) {
|
|||
++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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
*/
|
||||
|
||||
#include <mitsuba/mitsuba.h>
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
|
||||
MTS_NAMESPACE_BEGIN
|
||||
|
||||
|
@ -47,7 +48,7 @@ void Spectrum::staticInitialization() {
|
|||
}
|
||||
|
||||
void Spectrum::staticShutdown() {
|
||||
/* Do nothing */
|
||||
/* Nothing to do */
|
||||
}
|
||||
|
||||
void Spectrum::fromSmoothSpectrum(const SmoothSpectrum *smooth) {
|
||||
|
@ -260,9 +261,24 @@ Float BlackBodySpectrum::eval(Float l) const {
|
|||
/ (1e9*1e4*(std::exp((h/k)*c/(lambda*m_temperature)) - 1.0));
|
||||
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) {
|
||||
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_value.push_back(value);
|
||||
}
|
||||
|
|
|
@ -344,36 +344,50 @@ void SceneHandler::endElement(const XMLCh* const xmlName) {
|
|||
discrete.fromSmoothSpectrum(&bb);
|
||||
context.parent->properties.setSpectrum(context.attributes["name"], discrete);
|
||||
} else if (name == "spectrum") {
|
||||
std::vector<std::string> tokens = tokenize(
|
||||
context.attributes["value"], ", ");
|
||||
Float value[SPECTRUM_SAMPLES];
|
||||
if (tokens.size() == 1) {
|
||||
value[0] = parseFloat(name, tokens[0]);
|
||||
context.parent->properties.setSpectrum(context.attributes["name"],
|
||||
Spectrum(value[0]));
|
||||
} else {
|
||||
if (tokens[0].find(':') != std::string::npos) {
|
||||
InterpolatedSpectrum interp(tokens.size());
|
||||
/* Wavelength -> Value mapping */
|
||||
for (size_t i=0; i<tokens.size(); i++) {
|
||||
std::vector<std::string> tokens2 = tokenize(tokens[i], ":");
|
||||
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);
|
||||
bool hasValue = context.attributes.find("value") != context.attributes.end();
|
||||
bool hasFilename = context.attributes.find("filename") != context.attributes.end();
|
||||
|
||||
if (hasValue == hasFilename) {
|
||||
SLog(EError, "Spectrum: please provide one of 'value' or 'filename'");
|
||||
} else if (hasFilename) {
|
||||
FileResolver *resolver = Thread::getThread()->getFileResolver();
|
||||
fs::path path = resolver->resolve(context.attributes["filename"]);
|
||||
InterpolatedSpectrum interp(path);
|
||||
Spectrum discrete;
|
||||
discrete.fromSmoothSpectrum(&interp);
|
||||
context.parent->properties.setSpectrum(context.attributes["name"], discrete);
|
||||
} else if (hasValue) {
|
||||
std::vector<std::string> tokens = tokenize(
|
||||
context.attributes["value"], ", ");
|
||||
Float value[SPECTRUM_SAMPLES];
|
||||
if (tokens.size() == 1) {
|
||||
value[0] = parseFloat(name, tokens[0]);
|
||||
context.parent->properties.setSpectrum(context.attributes["name"],
|
||||
discrete);
|
||||
Spectrum(value[0]));
|
||||
} 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));
|
||||
if (tokens[0].find(':') != std::string::npos) {
|
||||
InterpolatedSpectrum interp(tokens.size());
|
||||
/* Wavelength -> Value mapping */
|
||||
for (size_t i=0; i<tokens.size(); i++) {
|
||||
std::vector<std::string> tokens2 = tokenize(tokens[i], ":");
|
||||
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") {
|
||||
|
|
|
@ -101,7 +101,7 @@ public:
|
|||
m_fakeSampler = new FakeSampler(m_sampler);
|
||||
}
|
||||
|
||||
std::pair<Vector, Float> generateSample() {
|
||||
boost::tuple<Vector, Float, EMeasure> generateSample() {
|
||||
Point2 sample(m_sampler->next2D());
|
||||
Intersection its;
|
||||
BSDFQueryRecord bRec(its);
|
||||
|
@ -136,7 +136,7 @@ public:
|
|||
#if defined(MTS_DEBUG_FP)
|
||||
disableFPExceptions();
|
||||
#endif
|
||||
return std::make_pair(bRec.wo, 0.0f);
|
||||
return boost::make_tuple(bRec.wo, 0.0f, ESolidAngle);
|
||||
} else if (sampled.isZero()) {
|
||||
if (!f.isZero() && pdfVal != 0)
|
||||
Log(EWarn, "Inconsistency (2): f=%s, pdf=%f, sampled f/pdf=%s, bRec=%s",
|
||||
|
@ -144,14 +144,14 @@ public:
|
|||
#if defined(MTS_DEBUG_FP)
|
||||
disableFPExceptions();
|
||||
#endif
|
||||
return std::make_pair(bRec.wo, 0.0f);
|
||||
return boost::make_tuple(bRec.wo, 0.0f, ESolidAngle);
|
||||
}
|
||||
|
||||
Spectrum sampled2 = f/pdfVal;
|
||||
if (!sampled.isValid() || !sampled2.isValid()) {
|
||||
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());
|
||||
return std::make_pair(bRec.wo, 0.0f);
|
||||
return boost::make_tuple(bRec.wo, 0.0f, ESolidAngle);
|
||||
}
|
||||
|
||||
bool mismatch = false;
|
||||
|
@ -175,10 +175,11 @@ public:
|
|||
disableFPExceptions();
|
||||
#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;
|
||||
BSDFQueryRecord bRec(its);
|
||||
bRec.component = m_component;
|
||||
|
@ -191,9 +192,10 @@ public:
|
|||
enableFPExceptions();
|
||||
#endif
|
||||
|
||||
if (m_bsdf->eval(bRec).isZero())
|
||||
if (m_bsdf->eval(bRec, measure).isZero())
|
||||
return 0.0f;
|
||||
Float result = m_bsdf->pdf(bRec);
|
||||
|
||||
Float result = m_bsdf->pdf(bRec, measure);
|
||||
|
||||
#if defined(MTS_DEBUG_FP)
|
||||
disableFPExceptions();
|
||||
|
@ -220,7 +222,7 @@ public:
|
|||
: m_mRec(mRec), m_phase(phase), m_sampler(sampler), m_wi(wi),
|
||||
m_largestWeight(0) { }
|
||||
|
||||
std::pair<Vector, Float> generateSample() {
|
||||
boost::tuple<Vector, Float, EMeasure> generateSample() {
|
||||
Point2 sample(m_sampler->next2D());
|
||||
PhaseFunctionQueryRecord pRec(m_mRec, m_wi);
|
||||
|
||||
|
@ -240,7 +242,7 @@ public:
|
|||
#if defined(MTS_DEBUG_FP)
|
||||
disableFPExceptions();
|
||||
#endif
|
||||
return std::make_pair(pRec.wo, 0.0f);
|
||||
return boost::make_tuple(pRec.wo, 0.0f, ESolidAngle);
|
||||
} else if (sampled == 0) {
|
||||
if (f != 0 && pdfVal != 0)
|
||||
Log(EWarn, "Inconsistency: f=%f, pdf=%f, sampled f/pdf=%f",
|
||||
|
@ -248,7 +250,7 @@ public:
|
|||
#if defined(MTS_DEBUG_FP)
|
||||
disableFPExceptions();
|
||||
#endif
|
||||
return std::make_pair(pRec.wo, 0.0f);
|
||||
return boost::make_tuple(pRec.wo, 0.0f, ESolidAngle);
|
||||
}
|
||||
|
||||
Float sampled2 = f/pdfVal;
|
||||
|
@ -271,10 +273,13 @@ public:
|
|||
#if defined(MTS_DEBUG_FP)
|
||||
disableFPExceptions();
|
||||
#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);
|
||||
#if defined(MTS_DEBUG_FP)
|
||||
enableFPExceptions();
|
||||
|
@ -351,11 +356,11 @@ public:
|
|||
// Initialize the tables used by the chi-square test
|
||||
chiSqr->fill(
|
||||
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)
|
||||
ChiSquare::ETestResult result = chiSqr->runTest(1, SIGNIFICANCE_LEVEL);
|
||||
ChiSquare::ETestResult result = chiSqr->runTest(SIGNIFICANCE_LEVEL);
|
||||
if (result == ChiSquare::EReject) {
|
||||
std::string filename = formatString("failure_%i.m", failureCount++);
|
||||
chiSqr->dumpTables(filename);
|
||||
|
@ -394,11 +399,11 @@ public:
|
|||
// Initialize the tables used by the chi-square test
|
||||
chiSqr->fill(
|
||||
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)
|
||||
ChiSquare::ETestResult result = chiSqr->runTest(1, SIGNIFICANCE_LEVEL);
|
||||
ChiSquare::ETestResult result = chiSqr->runTest(SIGNIFICANCE_LEVEL);
|
||||
if (result == ChiSquare::EReject) {
|
||||
std::string filename = formatString("failure_%i.m", failureCount++);
|
||||
chiSqr->dumpTables(filename);
|
||||
|
@ -460,11 +465,11 @@ public:
|
|||
// Initialize the tables used by the chi-square test
|
||||
chiSqr->fill(
|
||||
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)
|
||||
ChiSquare::ETestResult result = chiSqr->runTest(1, SIGNIFICANCE_LEVEL);
|
||||
ChiSquare::ETestResult result = chiSqr->runTest(SIGNIFICANCE_LEVEL);
|
||||
if (result == ChiSquare::EReject) {
|
||||
std::string filename = formatString("failure_%i.m", failureCount++);
|
||||
chiSqr->dumpTables(filename);
|
||||
|
|
Loading…
Reference in New Issue