Files
fractorium/Source/Ember/TemporalFilter.h
mfeemster b2600f0fff Remove passes.
Fix some table dimensions.
Proper locale for normalized weight.
2014-11-02 23:16:34 -08:00

370 lines
10 KiB
C++

#pragma once
#include "EmberDefines.h"
/// <summary>
/// TemporalFilter base, derived and factory classes.
/// </summary>
namespace EmberNs
{
/// <summary>
/// The types of temporal filters available.
/// </summary>
enum eTemporalFilterType
{
BOX_TEMPORAL_FILTER = 0,
GAUSSIAN_TEMPORAL_FILTER = 1,
EXP_TEMPORAL_FILTER = 2
};
/// <summary>
/// g++ needs a forward declaration here.
/// </summary>
template <typename T> class TemporalFilterCreator;
#define TEMPORALFILTERUSINGS \
using TemporalFilter<T>::m_Filter; \
using TemporalFilter<T>::m_FilterExp; \
using TemporalFilter<T>::Size; \
using TemporalFilter<T>::FinishFilter;
/// <summary>
/// Temporal filter is for doing motion blur while rendering a series of frames for animation.
/// The filter created is used as a vector of scalar values to multiply the time value by in between embers.
/// There are three possible types: Gaussian, Box and Exp.
/// Template argument expected to be float or double.
/// </summary>
template <typename T>
class EMBER_API TemporalFilter
{
public:
/// <summary>
/// Constructor to set up basic filtering parameters, allocate buffers and calculate deltas.
/// Derived class constructors will complete the final part of filter setup.
/// </summary>
/// <param name="filterType">Type of the filter.</param>
/// <param name="temporalSamples">The number of temporal samples in the ember being rendered</param>
/// <param name="filterWidth">The width of the filter.</param>
TemporalFilter(eTemporalFilterType filterType, size_t temporalSamples, T filterWidth)
{
size_t i, steps = temporalSamples;
m_TemporalSamples = temporalSamples;
m_FilterWidth = filterWidth;
m_Deltas.resize(steps);
m_Filter.resize(steps);
m_FilterType = filterType;
m_FilterExp = 1;
if (steps == 1)
{
m_SumFilt = 1;
m_Deltas[0] = 0;
m_Filter[0] = 1;
}
else
{
//Define the temporal deltas.
for (i = 0; i < steps; i++)
m_Deltas[i] = (T(i) / T(steps - 1) - T(0.5)) * filterWidth;
}
}
/// <summary>
/// Copy constructor.
/// </summary>
/// <param name="filter">The TemporalFilter object to copy</param>
TemporalFilter(const TemporalFilter<T>& filter)
{
*this = filter;
}
/// <summary>
/// Virtual destructor so derived class destructors get called.
/// </summary>
virtual ~TemporalFilter()
{
}
/// <summary>
/// Assignment operator.
/// </summary>
/// <param name="filter">The TemporalFilter object to copy.</param>
/// <returns>Reference to updated self</returns>
TemporalFilter<T>& operator = (const TemporalFilter<T>& filter)
{
if (this != &filter)
{
m_TemporalSamples = filter.m_TemporalSamples;
m_FilterWidth = filter.m_FilterWidth;
m_FilterExp = filter.m_FilterExp;
m_SumFilt = filter.m_SumFilt;
m_Deltas = filter.m_Deltas;
m_Filter = filter.m_Filter;
m_FilterType = filter.m_FilterType;
}
return *this;
}
/// <summary>
/// Return a string representation of this filter.
/// </summary>
/// <returns>The string representation of this filter</returns>
string ToString() const
{
size_t i;
stringstream ss;
ss << "Temporal Filter:" << endl
<< " Size: " << Size() << endl
<< " Type: " << TemporalFilterCreator<T>::ToString(m_FilterType) << endl
<< " Sum Filt: " << SumFilt() << endl;
ss << "Deltas: " << endl;
for (i = 0; i < m_Deltas.size(); i++)
{
ss << "Deltas[" << i << "]: " << m_Deltas[i] << endl;
}
ss << "Filter: " << endl;
for (i = 0; i < m_Filter.size(); i++)
{
ss << "Filter[" << i << "]: " << m_Filter[i] << endl;
}
return ss.str();
}
/// <summary>
/// Accessors.
/// </summary>
size_t Size() const { return m_Filter.size(); }
size_t TemporalSamples() const { return m_TemporalSamples; }
T FilterWidth() const { return m_FilterWidth; }
T FilterExp() const { return m_FilterExp; }
T SumFilt() const { return m_SumFilt; }
T* Deltas() { return &m_Deltas[0]; }
T* Filter() { return &m_Filter[0]; }
eTemporalFilterType FilterType() const { return m_FilterType; }
protected:
/// <summary>
/// Normalize the filter and the sum filt.
/// </summary>
/// <param name="maxFilt">The maximum filter value contained in the filter vector after it was created</param>
void FinishFilter(T maxFilt)
{
m_SumFilt = 0;
for (size_t i = 0; i < Size(); i++)
{
m_Filter[i] /= maxFilt;
m_SumFilt += m_Filter[i];
}
m_SumFilt /= Size();
}
T m_SumFilt;//The sum of all filter values.
T m_FilterWidth;
T m_FilterExp;
size_t m_TemporalSamples;
vector<T> m_Deltas;//Delta vector.
vector<T> m_Filter;//Filter vector.
eTemporalFilterType m_FilterType;//The type of filter this is.
};
/// <summary>
/// Derivation which implements the Exp filter.
/// </summary>
template <typename T>
class EMBER_API ExpTemporalFilter : public TemporalFilter<T>
{
TEMPORALFILTERUSINGS
public:
/// <summary>
/// Constructor to create an Exp filter.
/// </summary>
/// <param name="temporalSamples">The number of temporal samples in the ember being rendered</param>
/// <param name="filterWidth">The width of the filter.</param>
/// <param name="filterExp">The filter exp.</param>
ExpTemporalFilter(size_t temporalSamples, T filterWidth, T filterExp)
: TemporalFilter<T>(BOX_TEMPORAL_FILTER, temporalSamples, filterWidth)
{
if (Size() > 1)
{
T slpx, maxFilt = 0;
for (size_t i = 0; i < Size(); i++)
{
if (filterExp >= 0)
slpx = (T(i) + 1) / Size();
else
slpx = T(Size() - i) / Size();
//Scale the color based on these values.
m_Filter[i] = pow(slpx, fabs(filterExp));
//Keep the max.
if (m_Filter[i] > maxFilt)
maxFilt = m_Filter[i];
}
m_FilterExp = filterExp;
FinishFilter(maxFilt);
}
}
};
/// <summary>
/// Derivation which implements the Gaussian filter.
/// </summary>
template <typename T>
class EMBER_API GaussianTemporalFilter : public TemporalFilter<T>
{
TEMPORALFILTERUSINGS
public:
/// <summary>
/// Constructor to create a Gaussian filter.
/// </summary>
/// <param name="temporalSamples">The number of temporal samples in the ember being rendered</param>
/// <param name="filterWidth">The width of the filter.</param>
GaussianTemporalFilter(size_t temporalSamples, T filterWidth)
: TemporalFilter<T>(GAUSSIAN_TEMPORAL_FILTER, temporalSamples, filterWidth)
{
if (Size() > 1)
{
T maxFilt = 0, halfSteps = T(Size()) / T(2);
GaussianFilter<T> gaussian(1, 1);//Just pass dummy values, they are unused in this case.
for (size_t i = 0; i < Size(); i++)
{
m_Filter[i] = gaussian.Filter(gaussian.Support() * fabs(i - halfSteps) / halfSteps);
//Keep the max.
if (m_Filter[i] > maxFilt)
maxFilt = m_Filter[i];
}
FinishFilter(maxFilt);
}
}
};
/// <summary>
/// Derivation which implements the Box filter.
/// </summary>
template <typename T>
class EMBER_API BoxTemporalFilter : public TemporalFilter<T>
{
TEMPORALFILTERUSINGS
public:
/// <summary>
/// Constructor to create a Box filter.
/// </summary>
/// <param name="temporalSamples">The number of temporal samples in the ember being rendered</param>
/// <param name="filterWidth">The width of the filter.</param>
BoxTemporalFilter(size_t temporalSamples, T filterWidth)
: TemporalFilter<T>(BOX_TEMPORAL_FILTER, temporalSamples, filterWidth)
{
if (Size() > 1)
{
for (size_t i = 0; i < Size(); i++)
m_Filter[i] = 1;
FinishFilter(1);
}
}
};
/// <summary>
/// Convenience class to assist in converting between filter names and the filter objects themselves.
/// </summary>
template <typename T>
class EMBER_API TemporalFilterCreator
{
public:
/// <summary>
/// Creates the specified filter type based on the filterType enum parameter.
/// </summary>
/// <param name="filterType">Type of the filter</param>
/// <param name="temporalSamples">The number of temporal samples in the ember being rendered</param>
/// <param name="filterWidth">The width of the filter</param>
/// <param name="filterExp">The filter exp, only used with Exp filter, otherwise ignored.</param>
/// <returns>A pointer to the newly created filter object</returns>
static TemporalFilter<T>* Create(eTemporalFilterType filterType, size_t temporalSamples, T filterWidth, T filterExp = 1)
{
TemporalFilter<T>* filter = nullptr;
if (filterType == BOX_TEMPORAL_FILTER)
filter = new BoxTemporalFilter<T>(temporalSamples, filterWidth);
else if (filterType == GAUSSIAN_TEMPORAL_FILTER)
filter = new GaussianTemporalFilter<T>(temporalSamples, filterWidth);
else if (filterType == EXP_TEMPORAL_FILTER)
filter = new ExpTemporalFilter<T>(temporalSamples, filterWidth, filterExp);
else
filter = new BoxTemporalFilter<T>(temporalSamples, filterWidth);//Default to box if bad enum passed in.
return filter;
}
/// <summary>
/// Return a string vector of the available filter types.
/// </summary>
/// <returns>A vector of strings populated with the available filter types</returns>
static vector<string> FilterTypes()
{
vector<string> v;
v.reserve(3);
v.push_back("Box");
v.push_back("Gaussian");
v.push_back("Exp");
return v;
}
/// <summary>
/// Convert between the filter name string and its type enum.
/// </summary>
/// <param name="filterType">The string name of the filter</param>
/// <returns>The filter type enum</returns>
static eTemporalFilterType FromString(const string& filterType)
{
if (!_stricmp(filterType.c_str(), "box"))
return BOX_TEMPORAL_FILTER;
else if (!_stricmp(filterType.c_str(), "gaussian"))
return GAUSSIAN_TEMPORAL_FILTER;
else if (!_stricmp(filterType.c_str(), "exp"))
return EXP_TEMPORAL_FILTER;
else
return BOX_TEMPORAL_FILTER;
}
/// <summary>
/// Convert between the filter type enum and its name string.
/// </summary>
/// <param name="eTemporalFilterType">The filter type enum</param>
/// <returns>The string name of the filter</returns>
static string ToString(eTemporalFilterType filterType)
{
string filter;
if (filterType == BOX_TEMPORAL_FILTER)
filter = "Box";
else if (filterType == GAUSSIAN_TEMPORAL_FILTER)
filter = "Gaussian";
else if (filterType == EXP_TEMPORAL_FILTER)
filter = "Exp";
else
filter = "Box";
return filter;
}
};
}