872 lines
23 KiB
C++
872 lines
23 KiB
C++
/*
|
|
This file is part of Mitsuba, a physically based rendering system.
|
|
|
|
Copyright (c) 2007-2012 by Wenzel Jakob and others.
|
|
|
|
Mitsuba is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License Version 3
|
|
as published by the Free Software Foundation.
|
|
|
|
Mitsuba is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#pragma once
|
|
#if !defined(__MITSUBA_CORE_SPECTRUM_H_)
|
|
#define __MITSUBA_CORE_SPECTRUM_H_
|
|
|
|
#include <mitsuba/mitsuba.h>
|
|
|
|
#if !defined(SPECTRUM_SAMPLES)
|
|
#error The desired number of spectral samples must be \
|
|
specified in the configuration file!
|
|
#endif
|
|
|
|
#define SPECTRUM_MIN_WAVELENGTH 360
|
|
#define SPECTRUM_MAX_WAVELENGTH 830
|
|
#define SPECTRUM_RANGE \
|
|
(SPECTRUM_MAX_WAVELENGTH-SPECTRUM_MIN_WAVELENGTH)
|
|
|
|
MTS_NAMESPACE_BEGIN
|
|
|
|
/**
|
|
* \brief Abstract continous spectral power distribution data type,
|
|
* which supports evaluation at arbitrary wavelengths.
|
|
*
|
|
* Here, the term 'continous' doesn't necessarily mean that the
|
|
* underlying spectrum is continous, but rather emphasizes the fact
|
|
* that it is a function over the reals (as opposed to the discrete
|
|
* spectrum, which only stores samples for a discrete set of wavelengths).
|
|
*
|
|
* \ingroup libpython
|
|
* \ingroup libcore
|
|
*/
|
|
class MTS_EXPORT_CORE ContinuousSpectrum {
|
|
public:
|
|
/**
|
|
* Evaluate the value of the spectral power distribution
|
|
* at the given wavelength.
|
|
*
|
|
* \param lambda A wavelength in nanometers
|
|
*/
|
|
virtual Float eval(Float lambda) const = 0;
|
|
|
|
/**
|
|
* \brief Integrate the spectral power distribution
|
|
* over a given interval and return the average value
|
|
*
|
|
* Unless overridden in a subclass, the integration is done
|
|
* using adaptive Gauss-Lobatto quadrature.
|
|
*
|
|
* \param lambdaMin
|
|
* The lower interval bound in nanometers
|
|
*
|
|
* \param lambdaMax
|
|
* The upper interval bound in nanometers
|
|
*
|
|
* \remark If \c lambdaMin >= \c lambdaMax, the
|
|
* implementation will return zero.
|
|
*/
|
|
virtual Float average(Float lambdaMin, Float lambdaMax) const;
|
|
|
|
/// \brief Return a string representation
|
|
virtual std::string toString() const = 0;
|
|
|
|
/// Virtual destructor
|
|
virtual ~ContinuousSpectrum() { }
|
|
};
|
|
|
|
/**
|
|
* \brief Spectral power distribution based on Planck's black body law
|
|
*
|
|
* Computes the spectral power distribution of a black body of the
|
|
* specified temperature.
|
|
*
|
|
* \ingroup libcore
|
|
*/
|
|
class MTS_EXPORT_CORE BlackBodySpectrum : public ContinuousSpectrum {
|
|
public:
|
|
/**
|
|
* \brief Construct a new black body spectrum given the emitter's
|
|
* temperature in Kelvin.
|
|
*/
|
|
inline BlackBodySpectrum(Float temperature) {
|
|
m_temperature = temperature;
|
|
}
|
|
|
|
virtual ~BlackBodySpectrum() { }
|
|
|
|
/** \brief Return the value of the spectral power distribution
|
|
* at the given wavelength.
|
|
*
|
|
* The units are Watts per unit surface area (m^-2)
|
|
* per unit wavelength (nm^-1) per steradian (sr^-1)
|
|
*/
|
|
virtual Float eval(Float lambda) const;
|
|
|
|
/// Return a string representation
|
|
std::string toString() const;
|
|
private:
|
|
Float m_temperature;
|
|
};
|
|
|
|
/**
|
|
* \brief Spectral distribution for rendering participating media
|
|
* with Rayleigh scattering.
|
|
*
|
|
* This distribution captures the 1/lambda^4 wavelength dependence
|
|
* of Rayleigh scattering. It can provide both the scattering and
|
|
* extinction coefficient needed for simulating planetary
|
|
* atmospheres with participating media.
|
|
*
|
|
* \ingroup libcore
|
|
*/
|
|
class MTS_EXPORT_CORE RayleighSpectrum : public ContinuousSpectrum {
|
|
public:
|
|
enum EMode {
|
|
/// Compute the scattering coefficient
|
|
ESigmaS,
|
|
/// Compute the extinction coefficient
|
|
ESigmaT
|
|
};
|
|
|
|
/**
|
|
* \brief Create a Rayleigh spectrum instance
|
|
*
|
|
* \param mode Specifies the requested type of spectrum
|
|
* \param eta Refractive index of the medium (e.g. air)
|
|
* \param height Height above sea level (in meters)
|
|
*/
|
|
RayleighSpectrum(EMode mode, Float eta = 1.000277f, Float height = 0);
|
|
|
|
virtual ~RayleighSpectrum() { }
|
|
|
|
/** \brief Evaluate the extinction/scattering coefficient for
|
|
* a specified wavelength.
|
|
*
|
|
* The returned value is in units of 1/meter.
|
|
*/
|
|
virtual Float eval(Float lambda) const;
|
|
|
|
/// Return a string representation
|
|
std::string toString() const;
|
|
private:
|
|
Float m_precomp;
|
|
};
|
|
|
|
/**
|
|
* \brief This spectral power distribution is defined as the
|
|
* product of two other continuous spectra.
|
|
*/
|
|
class MTS_EXPORT_CORE ProductSpectrum : public ContinuousSpectrum {
|
|
public:
|
|
/** \brief Return the value of the spectral power distribution
|
|
* at the given wavelength.
|
|
*/
|
|
ProductSpectrum(const ContinuousSpectrum &s1,
|
|
const ContinuousSpectrum &s2) : m_spec1(s1),
|
|
m_spec2(s2) { }
|
|
|
|
/** \brief Return the value of the spectral power distribution
|
|
* at the given wavelength.
|
|
*/
|
|
virtual Float eval(Float lambda) const;
|
|
|
|
/// Virtual destructor
|
|
virtual ~ProductSpectrum() { }
|
|
|
|
/// Return a string representation
|
|
std::string toString() const;
|
|
private:
|
|
const ContinuousSpectrum &m_spec1;
|
|
const ContinuousSpectrum &m_spec2;
|
|
};
|
|
|
|
/**
|
|
* \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. Outside of the
|
|
* specified range, the spectrum is assumed to be zero. Hence,
|
|
* at least two entries are required to produce a nonzero
|
|
* spectrum.
|
|
*
|
|
* \ingroup libcore
|
|
* \ingroup libpython
|
|
*/
|
|
class MTS_EXPORT_CORE InterpolatedSpectrum : public ContinuousSpectrum {
|
|
public:
|
|
/**
|
|
* \brief Create a new interpolated spectrum with space
|
|
* for the specified number of samples
|
|
*/
|
|
InterpolatedSpectrum(size_t size = 0);
|
|
|
|
/**
|
|
* \brief Create a interpolated spectrum instance from
|
|
* a float array
|
|
*/
|
|
InterpolatedSpectrum(const Float *wavelengths,
|
|
const Float *values, size_t nEntries);
|
|
|
|
/**
|
|
* \brief Read an interpolated spectrum from a simple
|
|
* ASCII format.
|
|
*
|
|
* Each line of the file should contain an entry of the form
|
|
* \verbatim
|
|
* <wavelength in nm> <value>
|
|
* \endverbatim
|
|
* Comments preceded by '#' are also valid.
|
|
*/
|
|
InterpolatedSpectrum(const fs::path &path);
|
|
|
|
/**
|
|
* \brief Append an entry to the spectral power distribution.
|
|
*
|
|
* Entries must be added in order of increasing wavelength
|
|
*/
|
|
void append(Float lambda, Float value);
|
|
|
|
/**
|
|
* \brief This function adds a zero entry before and after
|
|
* the stored wavelength range.
|
|
*
|
|
* This is useful when handling datasets that don't fall
|
|
* off to zero at the ends. The spacing of the added entries
|
|
* is determined by computing the average spacing of the
|
|
* existing samples.
|
|
*/
|
|
void zeroExtend();
|
|
|
|
/// Clear all stored entries
|
|
void clear();
|
|
|
|
/**
|
|
* \brief Return the value of the spectral power distribution
|
|
* at the given wavelength.
|
|
*/
|
|
Float eval(Float lambda) const;
|
|
|
|
/**
|
|
* \brief Integrate the spectral power distribution
|
|
* over a given interval and return the average value
|
|
*
|
|
* This method overrides the implementation in
|
|
* \ref ContinousSpectrum, since the integral can be
|
|
* analytically computed for linearly interpolated spectra.
|
|
*
|
|
* \param lambdaMin
|
|
* The lower interval bound in nanometers
|
|
*
|
|
* \param lambdaMax
|
|
* The upper interval bound in nanometers
|
|
*
|
|
* \remark If \c lambdaMin >= \c lambdaMax, the
|
|
* implementation will return zero.
|
|
*/
|
|
Float average(Float lambdaMin, Float lambdaMax) const;
|
|
|
|
/// \brief Return a string representation
|
|
std::string toString() const;
|
|
|
|
/// Virtual destructor
|
|
virtual ~InterpolatedSpectrum() { }
|
|
protected:
|
|
std::vector<Float> m_wavelengths, m_values;
|
|
};
|
|
|
|
/**
|
|
* \brief Abstract spectral power distribution data type
|
|
*
|
|
* This class defines a vector-like data type that can be used for
|
|
* computations involving radiance. A concrete instantiation for the
|
|
* precision and spectral discretization chosen at compile time is
|
|
* given by the \ref Spectrum data type.
|
|
*
|
|
* \ingroup libcore
|
|
*/
|
|
template <typename T, int N> struct TSpectrum {
|
|
public:
|
|
typedef T Scalar;
|
|
|
|
/// Number of dimensions
|
|
const static int dim = N;
|
|
|
|
/// Create a new spectral power distribution, but don't initialize the contents
|
|
#if !defined(MTS_DEBUG_UNINITIALIZED)
|
|
inline TSpectrum() { }
|
|
#else
|
|
inline TSpectrum() {
|
|
for (int i=0; i<N; i++)
|
|
s[i] = std::numeric_limits<Scalar>::quiet_NaN();
|
|
}
|
|
#endif
|
|
|
|
/// Create a new spectral power distribution with all samples set to the given value
|
|
explicit inline TSpectrum(Scalar v) {
|
|
for (int i=0; i<N; i++)
|
|
s[i] = v;
|
|
}
|
|
|
|
/// Copy a spectral power distribution
|
|
explicit inline TSpectrum(Scalar spec[N]) {
|
|
memcpy(s, spec, sizeof(Scalar)*N);
|
|
}
|
|
|
|
/// Unserialize a spectral power distribution from a binary data stream
|
|
explicit inline TSpectrum(Stream *stream) {
|
|
stream->readArray(s, N);
|
|
}
|
|
|
|
/// Initialize with a TSpectrum data type based on a alternate representation
|
|
template <typename AltScalar> explicit TSpectrum(const TSpectrum<AltScalar, N> &v) {
|
|
for (int i=0; i<N; ++i)
|
|
s[i] = (Scalar) v[i];
|
|
}
|
|
|
|
/// Add two spectral power distributions
|
|
inline TSpectrum operator+(const TSpectrum &spec) const {
|
|
TSpectrum value = *this;
|
|
for (int i=0; i<N; i++)
|
|
value.s[i] += spec.s[i];
|
|
return value;
|
|
}
|
|
|
|
/// Add a spectral power distribution to this instance
|
|
inline TSpectrum& operator+=(const TSpectrum &spec) {
|
|
for (int i=0; i<N; i++)
|
|
s[i] += spec.s[i];
|
|
return *this;
|
|
}
|
|
|
|
/// Subtract a spectral power distribution
|
|
inline TSpectrum operator-(const TSpectrum &spec) const {
|
|
TSpectrum value = *this;
|
|
for (int i=0; i<N; i++)
|
|
value.s[i] -= spec.s[i];
|
|
return value;
|
|
}
|
|
|
|
/// Subtract a spectral power distribution from this instance
|
|
inline TSpectrum& operator-=(const TSpectrum &spec) {
|
|
for (int i=0; i<N; i++)
|
|
s[i] -= spec.s[i];
|
|
return *this;
|
|
}
|
|
|
|
/// Multiply by a scalar
|
|
inline TSpectrum operator*(Scalar f) const {
|
|
TSpectrum value = *this;
|
|
for (int i=0; i<N; i++)
|
|
value.s[i] *= f;
|
|
return value;
|
|
}
|
|
|
|
/// Multiply by a scalar
|
|
inline friend TSpectrum operator*(Scalar f, const TSpectrum &spec) {
|
|
return spec * f;
|
|
}
|
|
|
|
/// Multiply by a scalar
|
|
inline TSpectrum& operator*=(Scalar f) {
|
|
for (int i=0; i<N; i++)
|
|
s[i] *= f;
|
|
return *this;
|
|
}
|
|
|
|
/// Perform a component-wise multiplication by another spectrum
|
|
inline TSpectrum operator*(const TSpectrum &spec) const {
|
|
TSpectrum value = *this;
|
|
for (int i=0; i<N; i++)
|
|
value.s[i] *= spec.s[i];
|
|
return value;
|
|
}
|
|
|
|
/// Perform a component-wise multiplication by another spectrum
|
|
inline TSpectrum& operator*=(const TSpectrum &spec) {
|
|
for (int i=0; i<N; i++)
|
|
s[i] *= spec.s[i];
|
|
return *this;
|
|
}
|
|
|
|
/// Perform a component-wise division by another spectrum
|
|
inline TSpectrum& operator/=(const TSpectrum &spec) {
|
|
for (int i=0; i<N; i++)
|
|
s[i] /= spec.s[i];
|
|
return *this;
|
|
}
|
|
|
|
/// Perform a component-wise division by another spectrum
|
|
inline TSpectrum operator/(const TSpectrum &spec) const {
|
|
TSpectrum value = *this;
|
|
for (int i=0; i<N; i++)
|
|
value.s[i] /= spec.s[i];
|
|
return value;
|
|
}
|
|
|
|
/// Divide by a scalar
|
|
inline TSpectrum operator/(Scalar f) const {
|
|
TSpectrum value = *this;
|
|
#ifdef MTS_DEBUG
|
|
if (f == 0)
|
|
SLog(EWarn, "TSpectrum: Division by zero!");
|
|
#endif
|
|
Scalar recip = 1.0f / f;
|
|
for (int i=0; i<N; i++)
|
|
value.s[i] *= recip;
|
|
return value;
|
|
}
|
|
|
|
/// Equality test
|
|
inline bool operator==(const TSpectrum &spec) const {
|
|
for (int i=0; i<N; i++) {
|
|
if (s[i] != spec.s[i])
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// Inequality test
|
|
inline bool operator!=(const TSpectrum &spec) const {
|
|
return !operator==(spec);
|
|
}
|
|
|
|
/// Divide by a scalar
|
|
inline friend TSpectrum operator/(Scalar f, TSpectrum &spec) {
|
|
return spec / f;
|
|
}
|
|
|
|
/// Divide by a scalar
|
|
inline TSpectrum& operator/=(Scalar f) {
|
|
#ifdef MTS_DEBUG
|
|
if (f == 0)
|
|
SLog(EWarn, "TTSpectrum: Division by zero!");
|
|
#endif
|
|
Scalar recip = 1.0f / f;
|
|
for (int i=0; i<N; i++)
|
|
s[i] *= recip;
|
|
return *this;
|
|
}
|
|
|
|
/// Check for NaNs
|
|
inline bool isNaN() const {
|
|
for (int i=0; i<N; i++)
|
|
if (std::isnan(s[i]))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
/// Returns whether the spectrum only contains valid (non-NaN, nonnegative) samples
|
|
inline bool isValid() const {
|
|
for (int i=0; i<N; i++)
|
|
if (!std::isfinite(s[i]) || s[i] < 0.0f)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
/// Multiply-accumulate operation, adds \a weight * \a spec
|
|
inline void addWeighted(Scalar weight, const TSpectrum &spec) {
|
|
for (int i=0; i<N; i++)
|
|
s[i] += weight * spec.s[i];
|
|
}
|
|
|
|
/// Return the average over all wavelengths
|
|
inline Scalar average() const {
|
|
Scalar result = 0.0f;
|
|
for (int i=0; i<N; i++)
|
|
result += s[i];
|
|
return result * (1.0f / N);
|
|
}
|
|
|
|
/// Component-wise square root
|
|
inline TSpectrum sqrt() const {
|
|
TSpectrum value;
|
|
for (int i=0; i<N; i++)
|
|
value.s[i] = std::sqrt(s[i]);
|
|
return value;
|
|
}
|
|
|
|
/// Component-wise square root
|
|
inline TSpectrum safe_sqrt() const {
|
|
TSpectrum value;
|
|
for (int i=0; i<N; i++)
|
|
value.s[i] = math::safe_sqrt(s[i]);
|
|
return value;
|
|
}
|
|
|
|
/// Component-wise exponentation
|
|
inline TSpectrum exp() const {
|
|
TSpectrum value;
|
|
for (int i=0; i<N; i++)
|
|
value.s[i] = math::fastexp(s[i]);
|
|
return value;
|
|
}
|
|
|
|
/// Component-wise power
|
|
inline TSpectrum pow(Scalar f) const {
|
|
TSpectrum value;
|
|
for (int i=0; i<N; i++)
|
|
value.s[i] = std::pow(s[i], f);
|
|
return value;
|
|
}
|
|
|
|
/// Clamp negative values
|
|
inline void clampNegative() {
|
|
for (int i=0; i<N; i++)
|
|
s[i] = std::max((Scalar) 0.0f, s[i]);
|
|
}
|
|
|
|
/// Return the highest-valued spectral sample
|
|
inline Scalar max() const {
|
|
Scalar result = s[0];
|
|
for (int i=1; i<N; i++)
|
|
result = std::max(result, s[i]);
|
|
return result;
|
|
}
|
|
|
|
/// Return the lowest-valued spectral sample
|
|
inline Scalar min() const {
|
|
Scalar result = s[0];
|
|
for (int i=1; i<N; i++)
|
|
result = std::min(result, s[i]);
|
|
return result;
|
|
}
|
|
|
|
/// Negate
|
|
inline TSpectrum operator-() const {
|
|
TSpectrum value;
|
|
for (int i=0; i<N; i++)
|
|
value.s[i] = -s[i];
|
|
return value;
|
|
}
|
|
|
|
/// Indexing operator
|
|
inline Scalar &operator[](int entry) {
|
|
return s[entry];
|
|
}
|
|
|
|
/// Indexing operator
|
|
inline Scalar operator[](int entry) const {
|
|
return s[entry];
|
|
}
|
|
|
|
/// Check if this spectrum is zero at all wavelengths
|
|
inline bool isZero() const {
|
|
for (int i=0; i<N; i++) {
|
|
if (s[i] != 0.0f)
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// Serialize this spectrum to a stream
|
|
inline void serialize(Stream *stream) const {
|
|
stream->writeArray(s, N);
|
|
}
|
|
|
|
std::string toString() const {
|
|
std::ostringstream oss;
|
|
oss << "[";
|
|
for (int i=0; i<N; i++) {
|
|
oss << s[i];
|
|
if (i < N - 1)
|
|
oss << ", ";
|
|
}
|
|
oss << "]";
|
|
return oss.str();
|
|
}
|
|
|
|
protected:
|
|
Scalar s[N];
|
|
};
|
|
|
|
|
|
/** \brief RGB color data type
|
|
*
|
|
* \ingroup libcore
|
|
*/
|
|
struct MTS_EXPORT_CORE Color3 : public TSpectrum<Float, 3> {
|
|
public:
|
|
typedef TSpectrum<Float, 3> Parent;
|
|
|
|
/// Create a new color value, but don't initialize the contents
|
|
#if !defined(MTS_DEBUG_UNINITIALIZED)
|
|
inline Color3() { }
|
|
#else
|
|
inline Color3() {
|
|
for (int i=0; i<3; i++)
|
|
s[i] = std::numeric_limits<Scalar>::quiet_NaN();
|
|
}
|
|
#endif
|
|
|
|
/// Copy constructor
|
|
inline Color3(const Parent &s) : Parent(s) { }
|
|
|
|
/// Initialize to a constant value
|
|
inline Color3(Float value) : Parent(value) { }
|
|
|
|
/// Initialize to the given RGB value
|
|
inline Color3(Float r, Float g, Float b) {
|
|
s[0] = r; s[1] = g; s[2] = b;
|
|
}
|
|
};
|
|
|
|
|
|
/** \brief Discrete spectral power distribution based on a number
|
|
* of wavelength bins over the 360-830 nm range.
|
|
*
|
|
* This class defines a vector-like data type that can be used for
|
|
* computations involving radiance.
|
|
*
|
|
* When configured for spectral rendering (i.e. when the compile-time flag
|
|
* \c SPECTRUM_SAMPLES is set to a value != 3), the implementation discretizes
|
|
* the visible spectrum of light into a set of intervals, where the
|
|
* distribution within each bin is modeled as being uniform.
|
|
*
|
|
* When SPECTRUM_SAMPLES == 3, the class reverts to a simple linear
|
|
* RGB-based internal representation.
|
|
*
|
|
* The implementation of this class is based on PBRT.
|
|
*
|
|
* \ingroup libcore
|
|
* \ingroup libpython
|
|
*/
|
|
struct MTS_EXPORT_CORE Spectrum : public TSpectrum<Float, SPECTRUM_SAMPLES> {
|
|
public:
|
|
typedef TSpectrum<Float, SPECTRUM_SAMPLES> Parent;
|
|
|
|
/**
|
|
* \brief When converting from RGB reflectance values to
|
|
* discretized color spectra, the following `intent' flag
|
|
* can be provided to improve the results of this highly
|
|
* under-constrained problem.
|
|
*/
|
|
enum EConversionIntent {
|
|
/// Unitless reflectance data is converted
|
|
EReflectance,
|
|
|
|
/// Radiance-valued illumination data is converted
|
|
EIlluminant
|
|
};
|
|
|
|
/// Create a new spectral power distribution, but don't initialize the contents
|
|
#if !defined(MTS_DEBUG_UNINITIALIZED)
|
|
inline Spectrum() { }
|
|
#else
|
|
inline Spectrum() {
|
|
for (int i=0; i<SPECTRUM_SAMPLES; i++)
|
|
s[i] = std::numeric_limits<Scalar>::quiet_NaN();
|
|
}
|
|
#endif
|
|
|
|
/// Construct from a TSpectrum instance
|
|
inline Spectrum(const Parent &s) : Parent(s) { }
|
|
|
|
/// Initialize with a TSpectrum data type based on a alternate representation
|
|
template <typename AltScalar> explicit Spectrum(const TSpectrum<AltScalar, SPECTRUM_SAMPLES> &v) {
|
|
for (int i=0; i<SPECTRUM_SAMPLES; ++i)
|
|
s[i] = (Scalar) v[i];
|
|
}
|
|
|
|
/// Create a new spectral power distribution with all samples set to the given value
|
|
explicit inline Spectrum(Float v) {
|
|
for (int i=0; i<SPECTRUM_SAMPLES; i++)
|
|
s[i] = v;
|
|
}
|
|
|
|
/// Copy a spectral power distribution
|
|
explicit inline Spectrum(Float value[SPECTRUM_SAMPLES]) {
|
|
memcpy(s, value, sizeof(Float)*SPECTRUM_SAMPLES);
|
|
}
|
|
|
|
/// Unserialize a spectral power distribution from a binary data stream
|
|
explicit inline Spectrum(Stream *stream) : Parent(stream) { }
|
|
|
|
/**
|
|
* \brief Evaluate the SPD for the given wavelength
|
|
* in nanometers.
|
|
*/
|
|
Float eval(Float lambda) const;
|
|
|
|
/// \brief Return the wavelength range covered by a spectral bin
|
|
static std::pair<Float, Float> getBinCoverage(size_t index);
|
|
|
|
/// Return the luminance in candelas.
|
|
#if SPECTRUM_SAMPLES == 3
|
|
inline Float getLuminance() const {
|
|
return s[0] * 0.212671f + s[1] * 0.715160f + s[2] * 0.072169f;
|
|
}
|
|
#else
|
|
Float getLuminance() const;
|
|
#endif
|
|
|
|
/**
|
|
* \brief Convert from a spectral power distribution to XYZ
|
|
* tristimulus values
|
|
*
|
|
* In the Python API, this function returns a 3-tuple
|
|
* with the result of the operation.
|
|
*/
|
|
void toXYZ(Float &x, Float &y, Float &z) const;
|
|
|
|
/**
|
|
* \brief Convert XYZ tristimulus into a plausible spectral
|
|
* power distribution
|
|
*
|
|
* The \ref EConversionIntent parameter can be used to provide more
|
|
* information on how to solve this highly under-constrained problem.
|
|
* The default is \ref EReflectance.
|
|
*/
|
|
void fromXYZ(Float x, Float y, Float z,
|
|
EConversionIntent intent = EReflectance);
|
|
|
|
#if SPECTRUM_SAMPLES == 3
|
|
/**
|
|
* \brief Convert to linear RGB
|
|
*
|
|
* In the Python API, this function returns a 3-tuple
|
|
* with the result of the operation.
|
|
*/
|
|
inline void toLinearRGB(Float &r, Float &g, Float &b) const {
|
|
/* Nothing to do -- the renderer is in RGB mode */
|
|
r = s[0]; g = s[1]; b = s[2];
|
|
}
|
|
|
|
/// Convert from linear RGB
|
|
inline void fromLinearRGB(Float r, Float g, Float b,
|
|
EConversionIntent intent = EReflectance /* unused */) {
|
|
/* Nothing to do -- the renderer is in RGB mode */
|
|
s[0] = r; s[1] = g; s[2] = b;
|
|
}
|
|
#else
|
|
/**
|
|
* \brief Convert to linear RGB
|
|
*
|
|
* In the Python API, this function returns a 3-tuple
|
|
* with the result of the operation.
|
|
*/
|
|
void toLinearRGB(Float &r, Float &g, Float &b) const;
|
|
|
|
/**
|
|
* \brief Convert linear RGB colors into a plausible
|
|
* spectral power distribution
|
|
*
|
|
* The \ref EConversionIntent parameter can be used to provide more
|
|
* information on how to solve this highly under-constrained problem.
|
|
* The default is \ref EReflectance.
|
|
*/
|
|
void fromLinearRGB(Float r, Float g, Float b,
|
|
EConversionIntent intent = EReflectance);
|
|
#endif
|
|
|
|
/**
|
|
* \brief Convert to sRGB
|
|
*
|
|
* In the Python API, this function returns a 3-tuple
|
|
* with the result of the operation.
|
|
*/
|
|
void toSRGB(Float &r, Float &g, Float &b) const;
|
|
|
|
/**
|
|
* \brief Convert sRGB color values into a plausible spectral
|
|
* power distribution
|
|
*
|
|
* Note that compared to \ref fromLinearRGB, no \c intent parameter
|
|
* is available. For sRGB colors, it is assumed that the intent is
|
|
* always \ref EReflectance.
|
|
*/
|
|
void fromSRGB(Float r, Float g, Float b);
|
|
|
|
/**
|
|
* \brief Convert linear RGBE colors into a plausible
|
|
* spectral power distribution
|
|
*
|
|
* Based on code by Bruce Walter and Greg ward.
|
|
*
|
|
* The \ref EConversionIntent parameter can be used to provide more
|
|
* information on how to solve this highly under-constrained problem.
|
|
* For RGBE values, the default is \ref EIlluminant.
|
|
*/
|
|
void fromRGBE(const uint8_t rgbe[4], EConversionIntent intent = EIlluminant);
|
|
|
|
/// Linear RGBE conversion based on Bruce Walter's and Greg Ward's code
|
|
void toRGBE(uint8_t rgbe[4]) const;
|
|
|
|
/// Initialize with spectral values from a smooth spectrum representation
|
|
void fromContinuousSpectrum(const ContinuousSpectrum &smooth);
|
|
|
|
/// Equality test
|
|
inline bool operator==(const Spectrum &val) const {
|
|
for (int i=0; i<SPECTRUM_SAMPLES; i++) {
|
|
if (s[i] != val.s[i])
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// Inequality test
|
|
inline bool operator!=(const Spectrum &val) const {
|
|
return !operator==(val);
|
|
}
|
|
|
|
/// Return a string representation
|
|
std::string toString() const;
|
|
|
|
/**
|
|
* \brief Static initialization (should be called once during the
|
|
* application's initialization phase)
|
|
*
|
|
* This function is responsible for choosing the wavelengths
|
|
* that will be used during rendering. It also pre-integrates
|
|
* the CIE matching curves so that sampled spectra can
|
|
* efficiently be converted to XYZ tristimulus values.
|
|
* Finally, it sets up pre-integrated color spectra for conversions
|
|
* from linear RGB to plausible spectral color distributions.
|
|
*/
|
|
static void staticInitialization();
|
|
static void staticShutdown();
|
|
protected:
|
|
#if SPECTRUM_SAMPLES != 3
|
|
/// Configured wavelengths bins in nanometers
|
|
static Float m_wavelengths[SPECTRUM_SAMPLES+1];
|
|
|
|
/// @{ \name Pre-integrated CIE 1931 XYZ color matching functions.
|
|
static Spectrum CIE_X;
|
|
static Spectrum CIE_Y;
|
|
static Spectrum CIE_Z;
|
|
static Float CIE_normalization;
|
|
/// @}
|
|
|
|
/**
|
|
* @{ \name Pre-integrated Smits-style RGB to Spectrum
|
|
* conversion spectra, data by Karl vom Berge
|
|
*/
|
|
static Spectrum rgbRefl2SpecWhite;
|
|
static Spectrum rgbRefl2SpecCyan;
|
|
static Spectrum rgbRefl2SpecMagenta;
|
|
static Spectrum rgbRefl2SpecYellow;
|
|
static Spectrum rgbRefl2SpecRed;
|
|
static Spectrum rgbRefl2SpecGreen;
|
|
static Spectrum rgbRefl2SpecBlue;
|
|
static Spectrum rgbIllum2SpecWhite;
|
|
static Spectrum rgbIllum2SpecCyan;
|
|
static Spectrum rgbIllum2SpecMagenta;
|
|
static Spectrum rgbIllum2SpecYellow;
|
|
static Spectrum rgbIllum2SpecRed;
|
|
static Spectrum rgbIllum2SpecGreen;
|
|
static Spectrum rgbIllum2SpecBlue;
|
|
/// @}
|
|
#endif
|
|
};
|
|
|
|
MTS_NAMESPACE_END
|
|
|
|
#endif /* __MITSUBA_CORE_SPECTRUM_H_ */
|