mitsuba/include/mitsuba/core/spectrum.h

458 lines
11 KiB
C++

/*
This file is part of Mitsuba, a physically based rendering system.
Copyright (c) 2007-2010 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/>.
*/
#if !defined(__SPECTRUM_H)
#define __SPECTRUM_H
#include <mitsuba/mitsuba.h>
#define SPECTRUM_MIN_WAVELENGTH 400
#define SPECTRUM_MAX_WAVELENGTH 700
#define SPECTRUM_RANGE (SPECTRUM_MAX_WAVELENGTH-SPECTRUM_MIN_WAVELENGTH+1)
#define SPECTRUM_SAMPLES 3
MTS_NAMESPACE_BEGIN
/**
* \brief Abstract smooth spectral power distribution data type,
* which supports evaluation at arbitrary wavelengths
*/
class MTS_EXPORT_CORE SmoothSpectrum {
public:
/**
* Evaluate the value of the spectral power distribution
* at the given wavelength.
*/
virtual Float eval(Float lambda) const = 0;
};
/**
* \brief Spectral power distribution based on Planck's black body law
*
* Computes the spectral power distribution of a black body of the
* specified temperature.
*/
class MTS_EXPORT_CORE BlackBodySpectrum : public SmoothSpectrum {
public:
/**
* \brief Construct a new black body spectrum given the emitter's
* temperature in Kelvin.
*/
inline BlackBodySpectrum(Float temperature) {
m_temperature = temperature;
}
/** \brief Return the value of the spectral power distribution
* at the given wavelength.
*/
virtual Float eval(Float lambda) const;
private:
Float m_temperature;
};
/**
* \brief Linearly interpolated spectral power distribution
*/
class MTS_EXPORT_CORE InterpolatedSpectrum : public SmoothSpectrum {
public:
/**
* \brief Create a new interpolated spectrum with space
* for the specified number of samples
*/
inline InterpolatedSpectrum(size_t size = 0) {
m_wavelength.reserve(size);
m_value.reserve(size);
}
/**
* \brief Append an entry to the spectral power distribution.
*
* Entries must be added in order of increasing wavelength
*/
void appendSample(Float lambda, Float value);
/**
* \brief Return the value of the spectral power distribution
* at the given wavelength.
*/
virtual Float eval(Float lambda) const;
private:
std::vector<Float> m_wavelength, m_value;
};
/** \brief Discrete spectral power distribution
* based on a (usually small) number of wavelength samples.
*
* When SPECTRUM_SAMPLES is set to 3 (the default), this class
* falls back to linear RGB as its internal representation.
*/
struct MTS_EXPORT_CORE Spectrum {
public:
/// Create a new spectral power distribution, but don't initialize the contents
inline Spectrum() { }
/// 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 spd[SPECTRUM_SAMPLES]) {
for (int i=0; i<SPECTRUM_SAMPLES; i++)
s[i] = spd[i];
}
/// Unserialize a spectral power distribution from a binary data stream
explicit inline Spectrum(Stream *stream) {
stream->readFloatArray(s, SPECTRUM_SAMPLES);
}
/// Add two spectral power distributions
inline Spectrum operator+(const Spectrum &spd) const {
Spectrum value = *this;
for (int i=0; i<SPECTRUM_SAMPLES; i++)
value.s[i] += spd.s[i];
return value;
}
/// Add a spectral power distribution to this instance
inline Spectrum& operator+=(const Spectrum &spd) {
for (int i=0; i<SPECTRUM_SAMPLES; i++)
s[i] += spd.s[i];
return *this;
}
/// Subtract a spectral power distribution
inline Spectrum operator-(const Spectrum &spd) const {
Spectrum value = *this;
for (int i=0; i<SPECTRUM_SAMPLES; i++)
value.s[i] -= spd.s[i];
return value;
}
/// Subtract a spectral power distribution from this instance
inline Spectrum& operator-=(const Spectrum &spd) {
for (int i=0; i<SPECTRUM_SAMPLES; i++)
s[i] -= spd.s[i];
return *this;
}
/// Multiply by a scalar
inline Spectrum operator*(Float f) const {
Spectrum value = *this;
for (int i=0; i<SPECTRUM_SAMPLES; i++)
value.s[i] *= f;
return value;
}
/// Multiply by a scalar
inline friend Spectrum operator*(Float f, Spectrum &spd) {
return spd * f;
}
/// Multiply by a scalar
inline Spectrum& operator*=(Float f) {
for (int i=0; i<SPECTRUM_SAMPLES; i++)
s[i] *= f;
return *this;
}
/// Perform a component-wise multiplication by another spectrum
inline Spectrum operator*(const Spectrum &spd) const {
Spectrum value = *this;
for (int i=0; i<SPECTRUM_SAMPLES; i++)
value.s[i] *= spd.s[i];
return value;
}
/// Perform a component-wise multiplication by another spectrum
inline Spectrum& operator*=(const Spectrum &spd) {
for (int i=0; i<SPECTRUM_SAMPLES; i++)
s[i] *= spd.s[i];
return *this;
}
/// Perform a component-wise division by another spectrum
inline Spectrum& operator/=(const Spectrum &spd) {
for (int i=0; i<SPECTRUM_SAMPLES; i++)
s[i] /= spd.s[i];
return *this;
}
/// Perform a component-wise division by another spectrum
inline Spectrum operator/(Spectrum spd) const {
Spectrum value = *this;
for (int i=0; i<SPECTRUM_SAMPLES; i++)
value.s[i] /= spd.s[i];
return value;
}
/// Divide by a scalar
inline Spectrum operator/(Float f) const {
Spectrum value = *this;
#ifdef MTS_DEBUG
if (f == 0) {
SLog(EWarn, "Spectrum: Division by zero!");
// exit(-1);
}
#endif
Float recip = 1.0f / f;
for (int i=0; i<SPECTRUM_SAMPLES; i++)
value.s[i] *= recip;
return value;
}
/// Equality test
inline bool operator==(Spectrum spd) const {
for (int i=0; i<SPECTRUM_SAMPLES; i++) {
if (s[i] != spd.s[i])
return false;
}
return true;
}
/// Inequality test
inline bool operator!=(Spectrum spd) const {
return !operator==(spd);
}
/// Divide by a scalar
inline friend Spectrum operator/(Float f, Spectrum &spd) {
return spd / f;
}
/// Divide by a scalar
inline Spectrum& operator/=(Float f) {
#ifdef MTS_DEBUG
if (f == 0) {
SLog(EWarn, "Spectrum: Division by zero!");
//exit(-1);
}
#endif
Float recip = 1.0f / f;
for (int i=0; i<SPECTRUM_SAMPLES; i++)
s[i] *= recip;
return *this;
}
/// Check for NaNs
inline bool isNaN() const {
for (int i=0; i<SPECTRUM_SAMPLES; i++)
if (ubi_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<SPECTRUM_SAMPLES; i++)
if (ubi_isnan(s[i]) || s[i] < 0.0f)
return false;
return true;
}
/// Multiply-accumulate operation, adds \a weight * \a spd
inline void addWeighted(Float weight, const Spectrum &spd) {
for (int i=0; i<SPECTRUM_SAMPLES; i++)
s[i] += weight * spd.s[i];
}
/// Return the average over all wavelengths
inline Float average() const {
Float result = 0.0f;
for (int i=0; i<SPECTRUM_SAMPLES; i++)
result += s[i];
return result / SPECTRUM_SAMPLES;
}
/// Component-wise square root
inline Spectrum sqrt() const {
Spectrum value;
for (int i=0; i<SPECTRUM_SAMPLES; i++)
value.s[i] = std::sqrt(s[i]);
return value;
}
/// Component-wise exponentation
inline Spectrum exp() const {
Spectrum value;
for (int i=0; i<SPECTRUM_SAMPLES; i++)
value.s[i] = std::exp(s[i]);
return value;
}
/// Component-wise power
inline Spectrum pow(Float f) const {
Spectrum value;
for (int i=0; i<SPECTRUM_SAMPLES; i++)
value.s[i] = std::pow(s[i], f);
return value;
}
/// Clamp negative values
inline void clampNegative() {
for (int i=0; i<SPECTRUM_SAMPLES; i++)
s[i] = std::max((Float) 0.0f, s[i]);
}
/// Return the highest-valued spectral sample
inline Float max() const {
Float result = s[0];
for (int i=1; i<SPECTRUM_SAMPLES; i++)
result = std::max(result, s[i]);
return result;
}
/// Return the lowest-valued spectral sample
inline Float min() const {
Float result = s[0];
for (int i=1; i<SPECTRUM_SAMPLES; i++)
result = std::min(result, s[i]);
return result;
}
/// Negate
inline Spectrum operator-() const {
Spectrum value;
for (int i=0; i<SPECTRUM_SAMPLES; i++)
value.s[i] = -s[i];
return value;
}
/// Indexing operator
inline Float &operator[](int entry) {
return s[entry];
}
/// Indexing operator
inline Float operator[](int entry) const {
return s[entry];
}
/// Check if this spectrum is zero at all wavelengths
bool isBlack() const;
/**
* \brief Evaluate the SPD at an arbitrary wavelength
* (uses interpolation)
*/
Float eval(Float lambda) const;
/// 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
/// Convert from a spectral power distribution to XYZ colors
void toXYZ(Float &x, Float &y, Float &z) const;
/// Convert from XYZ to a spectral power distribution
void fromXYZ(Float x, Float y, Float z);
#if SPECTRUM_SAMPLES == 3
/// Convert to linear RGB
inline void toLinearRGB(Float &r, Float &g, Float &b) const {
r = s[0];
g = s[1];
b = s[2];
}
/// Convert from linear RGB
inline void fromLinearRGB(Float r, Float g, Float b) {
s[0] = r;
s[1] = g;
s[2] = b;
}
#else
/// Convert to linear RGB
void toLinearRGB(Float &r, Float &g, Float &b) const;
/// Convert from linear RGB
void fromLinearRGB(Float r, Float g, Float b);
#endif
/// Convert to sRGB
void toSRGB(Float &r, Float &g, Float &b) const;
/// Convert from sRGB
void fromSRGB(Float r, Float g, Float b);
/// Linear RGBE conversion based on Bruce Walter's and Greg Ward's code
void fromRGBE(const uint8_t rgbe[4]);
/// 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 fromSmoothSpectrum(const SmoothSpectrum *smooth);
/// Serialize this spectrum to a stream
inline void serialize(Stream *stream) const {
stream->writeFloatArray(s, SPECTRUM_SAMPLES);
}
/// Return the wavelength corresponding to an index
inline static Float getWavelength(int index) {
SAssert(index < SPECTRUM_SAMPLES);
return m_wavelengths[index];
}
/// Return a string representation
std::string toString() const;
/**
* Static initialization (should be called once during the
* application's initialization phase
*/
static void staticInitialization();
static void staticShutdown();
protected:
Float s[SPECTRUM_SAMPLES];
/// Configured wavelengths in nanometers
static Float m_wavelengths[SPECTRUM_SAMPLES];
/// Normalization factor for XYZ<->RGB conversion
static Float m_normalization;
/// Inverse of \ref m_normalization
static Float m_invNormalization;
/**
* @{ \name CIE 1931 XYZ color matching functions.
* From http://www.cvrl.org/database/data/cmfs/ciexyz31_1.txt
*/
static const int CIE_start = 360;
static const int CIE_end = 830;
static const int CIE_count = CIE_end - CIE_start + 1;
static const Float CIE_normalization;
static const Float CIE_X[CIE_count];
static const Float CIE_Y[CIE_count];
static const Float CIE_Z[CIE_count];
/// @}
};
MTS_NAMESPACE_END
#endif /* __SPECTRUM_H */