Files
fractorium/Source/Ember/TemporalFilter.h
mfeemster 330074cfb2 --User changes
-Add new variations: bubbleT3D, crob, hexaplay3D, hexcrop, hexes, hexnix3D, loonie2, loonie3, nBlur, octapol and synth.
 -Allow for pre/post versions of dc_bubble, dc_cylinder and dc_linear whereas before they were omitted.
 -When saving a file with multiple embers in it, detect if time values are all the same and if so, start them at zero and increment by 1 for each ember.
 -Allow for numerous quality increases to be coalesced into one. It will pick up at the end of the current render.
 -Show selection highlight on variations tree in response to mouse hover. This makes it easier to see for which variation or param the current mouse wheel action will apply.
 -Make default temporal samples be 100, whereas before it was 1000 which was overkill.
 -Require the shift key to be held with delete for deleting an ember to prevent it from triggering when the user enters delete in the edit box.
  -This wasn't otherwise fixable without writing a lot more code.

--Bug fixes
 -EmberGenome was crashing when generating a sequence from a source file with more than 2 embers in it.
 -EmberGenome was improperly handling the first frame of a merge after the last frame of the loop.
  -These bugs were due to a previous commit. Revert parts of that commit.
 -Prevent a zoom value of less than 0 when reading from xml.
 -Slight optimization of the crescents, and mask variations, if the compiler wasn't doing it already.
 -Unique file naming was broken because it was looking for _# and the default names ended with -#.
 -Disallow renaming of an ember in the library tree to an empty string.
 -Severe bug that prevented some variations from being read correctly from params generated outside this program.
 -Severe OpenCL randomization bug. The first x coordinates of the first points in the first kernel call of the first ember of a render since the OpenCL renderer object was created were not random and were mostly -1.
 -Severe bug when populating xform selection distributions that could sometimes cause a crash due to roundoff error. Fix by using double.
 -Limit the max number of variations in a random ember to MAX_CL_VARS, which is 8. This ensures they'll look the same on CPU and GPU.
 -Prevent user from saving stylesheet to default.qss, it's a special reserved filename.

--Code changes
 -Generalize using the running sum output point inside of a variation for all cases: pre, reg and post.
 -Allow for array variables in variations where the address of each element is stored in m_Params.
 -Qualify all math functions with std::
 -No longer use our own Clamp() in OpenCL, instead use the standard clamp().
 -Redesign how functions are used in the variations OpenCL code.
 -Add tests to EmberTester to verify some of the new functionality.
 -Place more const and override qualifiers on functions where appropriate.
 -Add a global rand with a lock to be used very sparingly.
 -Use a map instead of a vector for bad param names in Xml parsing.
 -Prefix affine interpolation mode defines with "AFFINE_" to make their purpose more clear.
 -Allow for variations that change state during iteration by sending a separate copy of the ember to each rendering thread.
 -Implement this same functionality with a local struct in OpenCL. It's members are the total of all variables that need to change state within an ember.
 -Add Contains() function to Utils.h.
 -EmberRender: print names of kernels being printed with --dump_kernel option.
 -Clean up EmberTester to handle some of the recent changes.
 -Fix various casts.
 -Replace % 2 with & 1, even though the compiler was likely doing this already.
 -Add new file Variations06.h to accommodate new variations.
 -General cleanup.
2015-11-22 14:15:07 -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] = std::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;
}
};
}