--User changes

-Add two new variations, hyperbolic and hypershift2.
 -Allow for animating final xforms.
 -More detailed diagnostics when any action in the OpenCL renderer fails.
 -Allow for creating an OpenCL renderer which does not share a texture with the main window, and instead manually copies its final output image from GPU to CPU then back to GPU.

--Bug fixes
 -Text was not properly being copied out of the Info | Bounds text box.

--Code changes
 -Remove Renderer::AccumulatorToFinalImage(v4F* pixels, size_t finalOffset), it's no longer needed or makes sense.
 -Controllers no longer keep track of shared status, it's kept inside the renderers.
 -Make getter functions in FractoriumOptionsDialog be public.
This commit is contained in:
Person
2018-04-28 22:28:05 -07:00
parent 0c67c52720
commit 92e9836151
39 changed files with 852 additions and 405 deletions

View File

@ -407,6 +407,8 @@ uint Timing::m_ProcessorCount;
EXPORTPREPOSTREGVAR(Sphereblur, T) \
EXPORTPREPOSTREGVAR(Cpow3, T) \
EXPORTPREPOSTREGVAR(Concentric, T) \
EXPORTPREPOSTREGVAR(Hyperbolic, T) \
EXPORTPREPOSTREGVAR(Hypershift2, T) \
template EMBER_API class PostSmartcropVariation<T>; /*Only implemented as post.*/ \
EXPORTPREPOSTREGVAR(DCBubble, T) \
EXPORTPREPOSTREGVAR(DCCarpet, T) \

View File

@ -998,17 +998,19 @@ public:
/// <param name="angle">The angle to rotate by</param>
void RotateAffines(T angle)
{
for (size_t i = 0; i < XformCount(); i++)//Only look at normal xforms, exclude final.
size_t i = 0;
while (auto xform = GetTotalXform(i++))//Flam3 only allowed animation with normal xforms. This has been changed to allow animations of final xforms.
{
//Don't rotate xforms with animate set to 0.
if (m_Xforms[i].m_Animate == 0)
if (xform->m_Animate == 0)
continue;
//Assume that if there are no variations, then it's a padding xform.
if (m_Xforms[i].Empty() && m_AffineInterp != eAffineInterp::AFFINE_INTERP_LOG)
if (xform->Empty() && m_AffineInterp != eAffineInterp::AFFINE_INTERP_LOG)
continue;
m_Xforms[i].m_Affine.Rotate(angle * DEG_2_RAD_T);
xform->m_Affine.Rotate(angle * DEG_2_RAD_T);
//Don't rotate post.
}
}

View File

@ -37,7 +37,7 @@ static void sincos(float x, float* s, float* c)
namespace EmberNs
{
#define EMBER_VERSION "1.0.0.7"
#define EMBER_VERSION "1.0.0.8"
#define EPS6 T(1e-6)
#define EPS std::numeric_limits<T>::epsilon()//Apoplugin.h uses -20, but it's more mathematically correct to do it this way.
#define ISAAC_SIZE 4

View File

@ -1093,7 +1093,8 @@ eRenderStatus Renderer<T, bucketT>::GaussianDensityFilter()
}
/// <summary>
/// Thin wrapper around AccumulatorToFinalImage().
/// Produce a final, visible image by clipping, gamma correcting and spatial filtering the color values
/// in the density filtering buffer and save to the passed in buffer.
/// </summary>
/// <param name="pixels">The pixel vector to allocate and store the final image in</param>
/// <param name="finalOffset">Offset in the buffer to store the pixels to</param>
@ -1101,31 +1102,20 @@ eRenderStatus Renderer<T, bucketT>::GaussianDensityFilter()
template <typename T, typename bucketT>
eRenderStatus Renderer<T, bucketT>::AccumulatorToFinalImage(vector<v4F>& pixels, size_t finalOffset)
{
if (PrepFinalAccumVector(pixels))
return AccumulatorToFinalImage(pixels.data(), finalOffset);
return eRenderStatus::RENDER_ERROR;
}
/// <summary>
/// Produce a final, visible image by clipping, gamma correcting and spatial filtering the color values
/// in the density filtering buffer and save to the passed in buffer.
/// </summary>
/// <param name="pixels">The pre-allocated pixel buffer to store the final image in</param>
/// <param name="finalOffset">Offset in the buffer to store the pixels to. Default: 0.</param>
/// <returns>True if not prematurely aborted, else false.</returns>
template <typename T, typename bucketT>
eRenderStatus Renderer<T, bucketT>::AccumulatorToFinalImage(v4F* pixels, size_t finalOffset)
{
if (!pixels)
return eRenderStatus::RENDER_ERROR;
EnterFinalAccum();
if (!PrepFinalAccumVector(pixels))
{
LeaveFinalAccum();
return eRenderStatus::RENDER_ERROR;
}
//Timing t(4);
size_t filterWidth = m_SpatialFilter->FinalFilterWidth();
bucketT g, linRange, vibrancy;
Color<bucketT> background;
pixels += finalOffset;
auto p = pixels.data();
p += finalOffset;
PrepFinalAccumVals(background, g, linRange, vibrancy);//After this, background has been scaled from 0-1 to 0-255.
//If early clip, go through the entire accumulator and perform gamma correction first.
@ -1165,7 +1155,7 @@ eRenderStatus Renderer<T, bucketT>::AccumulatorToFinalImage(v4F* pixels, size_t
size_t pixelsRowStart = (m_YAxisUp ? ((FinalRasH() - j) - 1) : j) * FinalRasW();//Pull out of inner loop for optimization.
size_t y = m_DensityFilterOffset + (j * Supersample());//Start at the beginning row of each super sample block.
size_t clampedFilterH = std::min(filterWidth, m_SuperRasH - y);//Make sure the filter doesn't go past the bottom of the gutter.
auto pv4T = pixels + pixelsRowStart;
auto pv4T = p + pixelsRowStart;
for (size_t i = 0; i < FinalRasW(); i++, pv4T++)
{
@ -1210,11 +1200,11 @@ eRenderStatus Renderer<T, bucketT>::AccumulatorToFinalImage(v4F* pixels, size_t
{
for (i = 0; i < FinalRasW(); i++)
{
auto p = pixels + (i + j * FinalRasW());
p->r = m_TempEmber.m_Palette[i * 256 / FinalRasW()][0];
p->g = m_TempEmber.m_Palette[i * 256 / FinalRasW()][1];
p->b = m_TempEmber.m_Palette[i * 256 / FinalRasW()][2];
p->a = 1;
auto pp = p + (i + j * FinalRasW());
pp->r = m_TempEmber.m_Palette[i * 256 / FinalRasW()][0];
pp->g = m_TempEmber.m_Palette[i * 256 / FinalRasW()][1];
pp->b = m_TempEmber.m_Palette[i * 256 / FinalRasW()][2];
pp->a = 1;
}
}
}

View File

@ -77,7 +77,6 @@ protected:
virtual eRenderStatus LogScaleDensityFilter(bool forceOutput = false);
virtual eRenderStatus GaussianDensityFilter();
virtual eRenderStatus AccumulatorToFinalImage(vector<v4F>& pixels, size_t finalOffset);
virtual eRenderStatus AccumulatorToFinalImage(v4F* pixels, size_t finalOffset);
virtual EmberStats Iterate(size_t iterCount, size_t temporalSample);
virtual void ComputeCurves();

View File

@ -514,6 +514,13 @@ size_t RendererBase::ThreadCount() const { return m_ThreadsToUse; }
/// <returns>eRendererType::CPU_RENDERER</returns>
eRendererType RendererBase::RendererType() const { return eRendererType::CPU_RENDERER; }
/// <summary>
/// Get whether the renderer uses a shared texture with OpenGL.
/// This only applies to the OpenCL renderer (which can be shared or unshared), so it's always false in the base.
/// </summary>
/// <returns>True if shared, else false. Always false in the base.</returns>
bool RendererBase::Shared() const { return false; }
/// <summary>
/// //Non-virtual threading control.
/// </summary>

View File

@ -164,6 +164,7 @@ public:
//Virtual render properties, getters and setters.
virtual size_t ThreadCount() const;
virtual eRendererType RendererType() const;
virtual bool Shared() const;
//Abstract render properties, getters only.
virtual size_t TemporalSamples() const = 0;

View File

@ -197,6 +197,8 @@ enum class eVariationId : et
VAR_HOLE ,
VAR_HORSESHOE ,
VAR_HYPERBOLIC ,
VAR_HYPERCROP ,
VAR_HYPERSHIFT2 ,
VAR_HYPERTILE ,
VAR_HYPERTILE1 ,
VAR_HYPERTILE2 ,
@ -540,6 +542,8 @@ enum class eVariationId : et
VAR_PRE_HOLE,
VAR_PRE_HORSESHOE,
VAR_PRE_HYPERBOLIC,
VAR_PRE_HYPERCROP,
VAR_PRE_HYPERSHIFT2,
VAR_PRE_HYPERTILE,
VAR_PRE_HYPERTILE1,
VAR_PRE_HYPERTILE2,
@ -883,6 +887,8 @@ enum class eVariationId : et
VAR_POST_HOLE,
VAR_POST_HORSESHOE,
VAR_POST_HYPERBOLIC,
VAR_POST_HYPERCROP,
VAR_POST_HYPERSHIFT2,
VAR_POST_HYPERTILE,
VAR_POST_HYPERTILE1,
VAR_POST_HYPERTILE2,

View File

@ -359,6 +359,8 @@ VariationList<T>::VariationList()
ADDPREPOSTREGVAR(Sphereblur)
ADDPREPOSTREGVAR(Cpow3)
ADDPREPOSTREGVAR(Concentric)
ADDPREPOSTREGVAR(Hypercrop)
ADDPREPOSTREGVAR(Hypershift2)
//ADDPREPOSTREGVAR(LinearXZ)
//ADDPREPOSTREGVAR(LinearYZ)
//DC are special.

View File

@ -1487,12 +1487,6 @@ public:
PARVARCOPY(CrobVariation)
/// <summary>
/// Functions the specified helper.
/// </summary>
/// <param name="helper">The helper.</param>
/// <param name="outPoint">The out point.</param>
/// <param name="rand">The rand.</param>
virtual void Func(IteratorHelper<T>& helper, Point<T>& outPoint, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override
{
T gradTmp, secTmp, xTmp = 0, yTmp = 0;

View File

@ -2013,6 +2013,256 @@ private:
T m_Zblur;
};
/// <summary>
/// hypercrop.
/// </summary>
template <typename T>
class HypercropVariation : public ParametricVariation<T>
{
public:
HypercropVariation(T weight = 1.0) : ParametricVariation<T>("hypercrop", eVariationId::VAR_HYPERCROP, weight, false, false, false, false, true)
{
Init();
}
PARVARCOPY(HypercropVariation)
virtual void Func(IteratorHelper<T>& helper, Point<T>& outPoint, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override
{
T fx = helper.In.x;
T fy = helper.In.y;
T fz = helper.In.z;
T a0 = T(M_PI) / m_N;
T len = 1 / Zeps(std::cos(a0));
T d = m_Rad * std::sin(a0) * len;
T angle = Floor<T>(helper.m_PrecalcAtanyx * m_Coeff) / m_Coeff + T(M_PI) / m_N;
T x0 = std::cos(angle) * len;
T y0 = std::sin(angle) * len;
if (std::sqrt(Sqr(helper.In.x - x0) + Sqr(helper.In.y - y0)) < d)
{
if (m_Zero > 1.5)
{
fx = x0;
fy = y0;
fz = 0;
}
else
{
if (m_Zero > 0.5)
{
fx = 0;
fy = 0;
fz = 0;
}
else
{
T rangle = std::atan2(helper.In.y - y0, helper.In.x - x0);
fx = x0 + std::cos(rangle) * d;
fy = y0 + std::sin(rangle) * d;
fz = 0;
}
}
}
helper.Out.x = fx * m_Weight;
helper.Out.y = fy * m_Weight;
helper.Out.z = fz * m_Weight;
}
virtual string OpenCLString() const override
{
ostringstream ss, ss2;
intmax_t i = 0, varIndex = IndexInXform();
ss2 << "_" << XformIndexInEmber() << "]";
string index = ss2.str();
string weight = WeightDefineString();
string n = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
string rad = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
string zero = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
string coeff = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
ss << "\t{\n"
<< "\t\treal_t fx = vIn.x;\n"
<< "\t\treal_t fy = vIn.y;\n"
<< "\t\treal_t fz = vIn.z;\n"
<< "\t\treal_t a0 = M_PI / " << n << ";\n"
<< "\t\treal_t len = 1 / Zeps(cos(a0));\n"
<< "\t\treal_t d = " << rad << " * sin(a0) * len;\n"
<< "\t\treal_t angle = floor(precalcAtanyx * " << coeff << ") / " << coeff << " + M_PI / " << n << ";\n"
<< "\t\treal_t x0 = cos(angle) * len;\n"
<< "\t\treal_t y0 = sin(angle) * len;\n"
<< "\n"
<< "\t\tif (sqrt(Sqr(vIn.x - x0) + Sqr(vIn.y - y0)) < d)\n"
<< "\t\t{\n"
<< "\t\t if (" << zero << " > 1.5)\n"
<< "\t\t {\n"
<< "\t\t fx = x0;\n"
<< "\t\t fy = y0;\n"
<< "\t\t fz = 0;\n"
<< "\t\t }\n"
<< "\t\t else\n"
<< "\t\t {\n"
<< "\t\t if (" << zero << " > 0.5)\n"
<< "\t\t {\n"
<< "\t\t fx = 0;\n"
<< "\t\t fy = 0;\n"
<< "\t\t fz = 0;\n"
<< "\t\t }\n"
<< "\t\t else\n"
<< "\t\t {\n"
<< "\t\t real_t rangle = atan2(vIn.y - y0, vIn.x - x0);\n"
<< "\t\t fx = x0 + cos(rangle) * d;\n"
<< "\t\t fy = y0 + sin(rangle) * d;\n"
<< "\t\t fz = 0;\n"
<< "\t\t }\n"
<< "\t\t }\n"
<< "\t\t}\n"
<< "\n"
<< "\t\tvOut.x = fx * " << weight << ";\n"
<< "\t\tvOut.y = fy * " << weight << ";\n"
<< "\t\tvOut.z = fz * " << weight << ";\n"
<< "\t}\n";
return ss.str();
}
virtual void Precalc() override
{
m_N = Zeps(m_N);
m_Coeff = Zeps<T>(m_N * T(0.5) / T(M_PI));
}
virtual vector<string> OpenCLGlobalFuncNames() const override
{
return vector<string> { "Zeps", "Sqr" };
}
protected:
void Init()
{
string prefix = Prefix();
m_Params.clear();
m_Params.push_back(ParamWithName<T>(&m_N, prefix + "hypercrop_n", 4));
m_Params.push_back(ParamWithName<T>(&m_Rad, prefix + "hypercrop_rad", 1));
m_Params.push_back(ParamWithName<T>(&m_Zero, prefix + "hypercrop_zero"));
m_Params.push_back(ParamWithName<T>(true, &m_Coeff, prefix + "hypercrop_coeff"));//Precalc.
}
private:
T m_N;
T m_Rad;
T m_Zero;
T m_Coeff;//Precalc.
};
/// <summary>
/// hypershift2.
/// </summary>
template <typename T>
class Hypershift2Variation : public ParametricVariation<T>
{
public:
Hypershift2Variation(T weight = 1.0) : ParametricVariation<T>("hypershift2", eVariationId::VAR_HYPERSHIFT2, weight)
{
Init();
}
PARVARCOPY(Hypershift2Variation)
virtual void Func(IteratorHelper<T>& helper, Point<T>& outPoint, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override
{
T fx = helper.In.x * m_Scale2;
T fy = helper.In.y * m_Scale2;
T rad = 1 / Zeps(fx * fx + fy * fy);
T x = rad * fx + m_Shift;
T y = rad * fy;
rad = m_Weight * m_Scale / Zeps(x * x + y * y);
T angle = ((rand.Rand() % int(m_P)) * 2 + 1) * T(M_PI) / m_P;
T X = rad * x + m_Shift;
T Y = rad * y;
T cosa = std::cos(angle);
T sina = std::sin(angle);
if (m_VarType == eVariationType::VARTYPE_REG)
outPoint.m_X = outPoint.m_Y = outPoint.m_Z = 0;//This variation assigns, instead of summing, so order will matter.
helper.Out.x = cosa * X - sina * Y;
helper.Out.y = sina * X + cosa * Y;
helper.Out.z = helper.In.z * rad;
}
virtual string OpenCLString() const override
{
ostringstream ss, ss2;
intmax_t i = 0, varIndex = IndexInXform();
ss2 << "_" << XformIndexInEmber() << "]";
string index = ss2.str();
string weight = WeightDefineString();
string p = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
string q = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
string shift = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
string scale = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
string scale2 = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
ss << "\t{\n"
<< "\t\treal_t fx = vIn.x * " << scale2 << ";\n"
<< "\t\treal_t fy = vIn.y * " << scale2 << ";\n"
<< "\t\treal_t rad = 1 / Zeps(fx * fx + fy * fy);\n"
<< "\t\treal_t x = rad * fx + " << shift << ";\n"
<< "\t\treal_t y = rad * fy;\n"
<< "\t\trad = " << weight << " * " << shift << " / Zeps(x * x + y * y);\n"
<< "\t\treal_t angle = ((MwcNext(mwc) % (int)" << p << ") * 2 + 1) * M_PI / " << p << ";\n"
<< "\t\treal_t X = rad * x + " << shift << ";\n"
<< "\t\treal_t Y = rad * y;\n"
<< "\t\treal_t cosa = cos(angle);\n"
<< "\t\treal_t sina = sin(angle);\n";
if (m_VarType == eVariationType::VARTYPE_REG)
ss << "\t\toutPoint->m_X = outPoint->m_Y = outPoint->m_Z = 0;\n";
ss << "\t\tvOut.x = cosa * X - sina * Y;\n"
<< "\t\tvOut.y = sina * X + cosa * Y;\n"
<< "\t\tvOut.z = vIn.z * rad;\n"
<< "\t}\n";
return ss.str();
}
virtual void Precalc() override
{
T pq = T(M_PI) / m_Q;
T pp = T(M_PI) / m_P;
T spq = std::sin(pq);
T spp = std::sin(pp);
m_Shift = std::sin(T(M_PI) * T(0.5) - pq - pp);
m_Shift = m_Shift / std::sqrt(1 - Sqr(spq) - Sqr(spp));
m_Scale2 = 1 / std::sqrt(Sqr(sin(T(M_PI) / 2 + pp)) / Sqr(spq) - 1);
m_Scale2 = m_Scale2 * (std::sin(T(M_PI) / 2 + pp) / spq - 1);
m_Scale = 1 - m_Shift * m_Shift;
}
virtual vector<string> OpenCLGlobalFuncNames() const override
{
return vector<string> { "Zeps" };
}
protected:
void Init()
{
string prefix = Prefix();
m_Params.clear();
m_Params.push_back(ParamWithName<T>(&m_P, prefix + "hypershift2_p", 3, eParamType::INTEGER_NONZERO));
m_Params.push_back(ParamWithName<T>(&m_Q, prefix + "hypershift2_q", 7, eParamType::INTEGER_NONZERO));
m_Params.push_back(ParamWithName<T>(true, &m_Shift, prefix + "hypershift2_shift"));//Precalc.
m_Params.push_back(ParamWithName<T>(true, &m_Scale, prefix + "hypershift2_scale"));
m_Params.push_back(ParamWithName<T>(true, &m_Scale2, prefix + "hypershift2_scale2"));
}
private:
T m_P;
T m_Q;
T m_Shift;//Precalc.
T m_Scale;
T m_Scale2;
};
MAKEPREPOSTPARVAR(Splits3D, splits3D, SPLITS3D)
MAKEPREPOSTPARVAR(Waves2B, waves2b, WAVES2B)
MAKEPREPOSTPARVAR(JacCn, jac_cn, JAC_CN)
@ -2035,4 +2285,6 @@ MAKEPREPOSTPARVAR(Helix, helix, HELIX)
MAKEPREPOSTPARVAR(Sphereblur, sphereblur, SPHEREBLUR)
MAKEPREPOSTPARVAR(Cpow3, cpow3, CPOW3)
MAKEPREPOSTPARVAR(Concentric, concentric, CONCENTRIC)
MAKEPREPOSTPARVAR(Hypercrop, hypercrop, HYPERCROP)
MAKEPREPOSTPARVAR(Hypershift2, hypershift2, HYPERSHIFT2)
}