mirror of
https://bitbucket.org/mfeemster/fractorium.git
synced 2025-07-02 06:16:17 -04:00
Remove ReadMe.txt from all project files.
Add Curves.h, and CurvesGraphicsView.h/cpp to support bezier color curves. Add Curves member to Ember. Add curves capability to EmberCL. Remove some unused variables in the kernel created in RendererCL::CreateFinalAccumKernelString(). Use glm namespace for vec classes if GLM_VERSION >= 96, else use glm::detail. As a result of using glm namespace, all instances of min and max had to be qualified with std:: Split ComputeCamera into that and ComputeQuality(). Reduce the amount of ComputeCamera() and MakeDmap() calls on each incremental iter that doesn't use temporal samples. Fix clamping bug with DE filter widths. Provide functions to return the kernels from RendererCL to assist with diagnostics and debugging. Prevent extra newline in EmberRender when only rendering a single image. Add the ability to delete an ember at a given index in EmberFile. Allow deleting/focusing ember in library tab with delete and enter keys. Reorder some code in Fractorium.h to match the tabs order. Add and call ClearFinalImages() to clear buffers in controller to fix bug where previous CPU render would be shown for a split second when switching from OpenCL back to CPU. Refactor ember library pointer syncing to a function SyncPointers(). Add the ability to save ember Xmls to an unique automatically generated name after the first time the user has specified a name.
This commit is contained in:
284
Source/Ember/Curves.h
Normal file
284
Source/Ember/Curves.h
Normal file
@ -0,0 +1,284 @@
|
||||
#pragma once
|
||||
|
||||
#include "Utils.h"
|
||||
#include "Isaac.h"
|
||||
|
||||
/// <summary>
|
||||
/// Curves class.
|
||||
/// </summary>
|
||||
|
||||
namespace EmberNs
|
||||
{
|
||||
/// <summary>
|
||||
/// The Bezier curves used to adjust the colors during final accumulation.
|
||||
/// This functionality was gotten directly from Apophysis.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
class EMBER_API Curves
|
||||
{
|
||||
public:
|
||||
/// <summary>
|
||||
/// Constructor which sets the curve and weight values to their defaults.
|
||||
/// </summary>
|
||||
Curves(bool init = false)
|
||||
{
|
||||
if (init)
|
||||
Init();
|
||||
else
|
||||
Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default copy constructor.
|
||||
/// </summary>
|
||||
/// <param name="curves">The Curves object to copy</param>
|
||||
Curves(const Curves<T>& curves)
|
||||
{
|
||||
Curves<T>::operator=<T>(curves);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy constructor to copy a Curves object of type U.
|
||||
/// Special case that must be here in the header because it has
|
||||
/// a second template parameter.
|
||||
/// </summary>
|
||||
/// <param name="curves">The Curves object to copy</param>
|
||||
template <typename U>
|
||||
Curves(const Curves<U>& curves)
|
||||
{
|
||||
Curves<T>::operator=<U>(curves);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default assignment operator.
|
||||
/// </summary>
|
||||
/// <param name="curves">The Curves object to copy</param>
|
||||
Curves<T>& operator = (const Curves<T>& curves)
|
||||
{
|
||||
if (this != &curves)
|
||||
Curves<T>::operator=<T>(curves);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assignment operator to assign a Curves object of type U.
|
||||
/// </summary>
|
||||
/// <param name="curves">The Curves object to copy</param>
|
||||
/// <returns>Reference to updated self</returns>
|
||||
template <typename U>
|
||||
Curves<T>& operator = (const Curves<U>& curves)
|
||||
{
|
||||
for (uint i = 0; i < 4; i++)
|
||||
{
|
||||
m_Points[i][0].x = T(curves.m_Points[i][0].x); m_Points[i][0].y = T(curves.m_Points[i][0].y); m_Weights[i].x = T(curves.m_Weights[i].x);
|
||||
m_Points[i][1].x = T(curves.m_Points[i][1].x); m_Points[i][1].y = T(curves.m_Points[i][1].y); m_Weights[i].y = T(curves.m_Weights[i].y);
|
||||
m_Points[i][2].x = T(curves.m_Points[i][2].x); m_Points[i][2].y = T(curves.m_Points[i][2].y); m_Weights[i].z = T(curves.m_Weights[i].z);
|
||||
m_Points[i][3].x = T(curves.m_Points[i][3].x); m_Points[i][3].y = T(curves.m_Points[i][3].y); m_Weights[i].w = T(curves.m_Weights[i].w);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unary addition operator to add a Curves<T> object to this one.
|
||||
/// </summary>
|
||||
/// <param name="curves">The Curves object to add</param>
|
||||
/// <returns>Reference to updated self</returns>
|
||||
Curves<T>& operator += (const Curves<T>& curves)
|
||||
{
|
||||
for (uint i = 0; i < 4; i++)
|
||||
{
|
||||
m_Points[i][0] += curves.m_Points[i][0];
|
||||
m_Points[i][1] += curves.m_Points[i][1];
|
||||
m_Points[i][2] += curves.m_Points[i][2];
|
||||
m_Points[i][3] += curves.m_Points[i][3];
|
||||
|
||||
m_Weights[i] += curves.m_Weights[i];
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unary multiplication operator to multiply this object by another Curves<T> object.
|
||||
/// </summary>
|
||||
/// <param name="curves">The Curves object to multiply this one by</param>
|
||||
/// <returns>Reference to updated self</returns>
|
||||
Curves<T>& operator *= (const Curves<T>& curves)
|
||||
{
|
||||
for (uint i = 0; i < 4; i++)
|
||||
{
|
||||
m_Points[i][0] *= curves.m_Points[i][0];
|
||||
m_Points[i][1] *= curves.m_Points[i][1];
|
||||
m_Points[i][2] *= curves.m_Points[i][2];
|
||||
m_Points[i][3] *= curves.m_Points[i][3];
|
||||
|
||||
m_Weights[i] *= curves.m_Weights[i];
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unary multiplication operator to multiply this object by a scalar of type T.
|
||||
/// </summary>
|
||||
/// <param name="t">The scalar to multiply this object by</param>
|
||||
/// <returns>Reference to updated self</returns>
|
||||
Curves<T>& operator *= (const T& t)
|
||||
{
|
||||
for (uint i = 0; i < 4; i++)
|
||||
{
|
||||
m_Points[i][0] *= t;
|
||||
m_Points[i][1] *= t;
|
||||
m_Points[i][2] *= t;
|
||||
m_Points[i][3] *= t;
|
||||
|
||||
m_Weights[i] *= t;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the curve and weight values to their default state.
|
||||
/// </summary>
|
||||
void Init()
|
||||
{
|
||||
for (uint i = 0; i < 4; i++)
|
||||
{
|
||||
m_Points[i][0] = v2T(0);//0,0 -> 0,0 -> 1,1 -> 1,1.
|
||||
m_Points[i][1] = v2T(0);
|
||||
m_Points[i][2] = v2T(1);
|
||||
m_Points[i][3] = v2T(1);
|
||||
|
||||
m_Weights[i] = v4T(1);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the curve and weight values to an empty state.
|
||||
/// </summary>
|
||||
void Clear()
|
||||
{
|
||||
memset(&m_Points, 0, sizeof(m_Points));
|
||||
memset(&m_Weights, 0, sizeof(m_Weights));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether any points are not the default.
|
||||
/// </summary>
|
||||
/// <returns>True if any point has been set to a value other than the default, else false.</returns>
|
||||
bool CurvesSet()
|
||||
{
|
||||
bool set = false;
|
||||
|
||||
for (uint i = 0; i < 4; i++)
|
||||
{
|
||||
if ((m_Points[i][0] != v2T(0)) ||
|
||||
(m_Points[i][1] != v2T(0)) ||
|
||||
(m_Points[i][2] != v2T(1)) ||
|
||||
(m_Points[i][3] != v2T(1)))
|
||||
{
|
||||
set = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wrapper around calling BezierSolve() on each of the 4 weight and point vectors.
|
||||
/// </summary>
|
||||
/// <param name="t">The position to apply</param>
|
||||
/// <returns>vec4<T> that contains the y component of the solution for each vector in each component</returns>
|
||||
v4T BezierFunc(const T& t)
|
||||
{
|
||||
v4T result;
|
||||
v2T solution(0, 0);
|
||||
|
||||
BezierSolve(t, m_Points[0], &m_Weights[0], solution); result.x = solution.y;
|
||||
BezierSolve(t, m_Points[1], &m_Weights[1], solution); result.y = solution.y;
|
||||
BezierSolve(t, m_Points[2], &m_Weights[2], solution); result.z = solution.y;
|
||||
BezierSolve(t, m_Points[3], &m_Weights[3], solution); result.w = solution.y;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
/// <summary>
|
||||
/// Solve the given point and weight vectors for the given position and store
|
||||
/// the output in the solution vec2 passed in.
|
||||
/// </summary>
|
||||
/// <param name="t">The position to apply</param>
|
||||
/// <param name="src">A pointer to an array of 4 vec2</param>
|
||||
/// <param name="w">A pointer to an array of 4 weights</param>
|
||||
/// <param name="solution">The vec2 to store the solution in</param>
|
||||
void BezierSolve(const T& t, v2T* src, v4T* w, v2T& solution)
|
||||
{
|
||||
T s, s2, s3, t2, t3, nom_x, nom_y, denom;
|
||||
|
||||
s = 1 - t;
|
||||
s2 = s * s;
|
||||
s3 = s * s * s;
|
||||
t2 = t * t;
|
||||
t3 = t * t * t;
|
||||
|
||||
nom_x = (w->x * s3 * src->x) + (w->y * s2 * 3 * t * src[1].x) + (w->z * s * 3 * t2 * src[2].x) + (w->w * t3 * src[3].x);
|
||||
|
||||
nom_y = (w->x * s3 * src->y) + (w->y * s2 * 3 * t * src[1].y) + (w->z * s * 3 * t2 * src[2].y) + (w->w * t3 * src[3].y);
|
||||
|
||||
denom = (w->x * s3) + (w->y * s2 * 3 * t) + (w->z * s * 3 * t2) + (w->w * t3);
|
||||
|
||||
|
||||
if (isnan(nom_x) || isnan(nom_y) || isnan(denom) || denom == 0)
|
||||
return;
|
||||
|
||||
solution.x = nom_x / denom;
|
||||
solution.y = nom_y / denom;
|
||||
}
|
||||
|
||||
public:
|
||||
v2T m_Points[4][4];
|
||||
v4T m_Weights[4];
|
||||
};
|
||||
|
||||
//Must declare this outside of the class to provide for both orders of parameters.
|
||||
|
||||
/// <summary>
|
||||
/// Multiplication operator to multiply a Curves<T> object by a scalar of type T.
|
||||
/// </summary>
|
||||
/// <param name="curves">The curves object to multiply</param>
|
||||
/// <param name="t">The scalar to multiply curves by by</param>
|
||||
/// <returns>Copy of new Curves<T></returns>
|
||||
template<typename T>
|
||||
Curves<T> operator * (const Curves<T>& curves, const T& t)
|
||||
{
|
||||
Curves<T> c(curves);
|
||||
|
||||
for (uint i = 0; i < 4; i++)
|
||||
{
|
||||
c.m_Points[i][0] *= t;
|
||||
c.m_Points[i][1] *= t;
|
||||
c.m_Points[i][2] *= t;
|
||||
c.m_Points[i][3] *= t;
|
||||
|
||||
c.m_Weights[i] *= t;
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplication operator for reverse order.
|
||||
/// </summary>
|
||||
/// <param name="t">The scalar to multiply curves by by</param>
|
||||
/// <param name="curves">The curves object to multiply</param>
|
||||
/// <returns>Copy of new Curves<T></returns>
|
||||
template<typename T>
|
||||
Curves<T> operator * (const T& t, const Curves<T>& curves)
|
||||
{
|
||||
return curves * t;
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
#include "EmberPch.h"
|
||||
#include "EmberDefines.h"
|
||||
#include "Isaac.h"
|
||||
#include "Curves.h"
|
||||
#include "Ember.h"
|
||||
#include "Utils.h"
|
||||
#include "Iterator.h"
|
||||
@ -390,6 +391,7 @@ template<> unique_ptr<QTIsaac<ISAAC_SIZE, ISAAC_INT>> QTIsaac<ISAAC_SIZE, ISAAC_
|
||||
template EMBER_API class Ember<T>; \
|
||||
/*template EMBER_API class RenderCallback<T>;*/ \
|
||||
template EMBER_API class CarToRas<T>; \
|
||||
template EMBER_API class Curves<T>; \
|
||||
template EMBER_API class XmlToEmber<T>; \
|
||||
template EMBER_API class EmberToXml<T>;
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "Curves.h"
|
||||
#include "Xform.h"
|
||||
#include "PaletteList.h"
|
||||
#include "SpatialFilter.h"
|
||||
@ -150,6 +151,7 @@ public:
|
||||
m_Index = ember.m_Index;
|
||||
m_ScaleType = ember.ScaleType();
|
||||
m_Palette = ember.m_Palette;
|
||||
m_Curves = ember.m_Curves;
|
||||
|
||||
m_Xforms.clear();
|
||||
|
||||
@ -243,6 +245,9 @@ public:
|
||||
m_PaletteMode = PALETTE_STEP;
|
||||
m_PaletteInterp = INTERP_HSV;
|
||||
|
||||
//Curves.
|
||||
m_Curves.Init();
|
||||
|
||||
m_Name = "No name";
|
||||
m_ParentFilename = "No parent";
|
||||
|
||||
@ -795,6 +800,7 @@ public:
|
||||
InterpT<&Ember<T>::m_MinRadDE>(embers, coefs, size);
|
||||
InterpT<&Ember<T>::m_CurveDE>(embers, coefs, size);
|
||||
InterpT<&Ember<T>::m_SpatialFilterRadius>(embers, coefs, size);
|
||||
InterpX<Curves<T>, &Ember<T>::m_Curves>(embers, coefs, size);
|
||||
|
||||
//An extra step needed here due to the OOD that was not needed in the original.
|
||||
//A small price to pay for the conveniences it affords us elsewhere.
|
||||
@ -1362,6 +1368,7 @@ public:
|
||||
|
||||
m_Xforms.clear();
|
||||
m_FinalXform.Clear();
|
||||
m_Curves.Init();
|
||||
ClearEdit();
|
||||
}
|
||||
|
||||
@ -1651,6 +1658,9 @@ public:
|
||||
//Xml field: "color" or "colors" or "palette" .
|
||||
Palette<T> m_Palette;
|
||||
|
||||
//Curves used to adjust the color during final accumulation.
|
||||
Curves<T> m_Curves;
|
||||
|
||||
//Strings.
|
||||
|
||||
//The name of this ember.
|
||||
@ -1674,6 +1684,15 @@ private:
|
||||
/// </summary>
|
||||
eScaleType m_ScaleType;
|
||||
|
||||
//The vector containing all of the xforms.
|
||||
//Xml field: "xform".
|
||||
vector<Xform<T>> m_Xforms;
|
||||
|
||||
//Optional final xform. Default is empty.
|
||||
//Discussed in section 3.2 of the paper, page 6.
|
||||
//Xml field: "finalxform".
|
||||
Xform<T> m_FinalXform;
|
||||
|
||||
/// <summary>
|
||||
/// Interpolation function that takes the address of a member variable of type T as a template parameter.
|
||||
/// This is an alternative to using macros.
|
||||
@ -1739,15 +1758,6 @@ private:
|
||||
for (size_t k = 0; k < size; k++)
|
||||
xform->*m += coefs[k] * embers[k].GetTotalXform(i)->*m;
|
||||
}
|
||||
|
||||
//The vector containing all of the xforms.
|
||||
//Xml field: "xform".
|
||||
vector<Xform<T>> m_Xforms;
|
||||
|
||||
//Optional final xform. Default is empty.
|
||||
//Discussed in section 3.2 of the paper, page 6.
|
||||
//Xml field: "finalxform".
|
||||
Xform<T> m_FinalXform;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
@ -42,7 +42,7 @@
|
||||
|
||||
namespace EmberNs
|
||||
{
|
||||
#define EMBER_VERSION "0.4.1.8"
|
||||
#define EMBER_VERSION "0.4.1.9"
|
||||
#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
|
||||
@ -83,13 +83,23 @@ typedef std::chrono::high_resolution_clock Clock;
|
||||
#define DO_DOUBLE 1//Comment this out for shorter build times during development. Always uncomment for release.
|
||||
//#define ISAAC_FLAM3_DEBUG 1//This is almost never needed, but is very useful when troubleshooting difficult bugs. Enable it to do a side by side comparison with flam3.
|
||||
|
||||
#define v2T glm::detail::tvec2<T, glm::defaultp>
|
||||
#define v3T glm::detail::tvec3<T, glm::defaultp>
|
||||
#define v4T glm::detail::tvec4<T, glm::defaultp>
|
||||
#define m2T glm::detail::tmat2x2<T, glm::defaultp>
|
||||
#define m3T glm::detail::tmat3x3<T, glm::defaultp>
|
||||
#define m4T glm::detail::tmat4x4<T, glm::defaultp>
|
||||
#define m23T glm::detail::tmat2x3<T, glm::defaultp>
|
||||
#if GLM_VERSION >= 96
|
||||
#define v2T glm::tvec2<T, glm::defaultp>
|
||||
#define v3T glm::tvec3<T, glm::defaultp>
|
||||
#define v4T glm::tvec4<T, glm::defaultp>
|
||||
#define m2T glm::tmat2x2<T, glm::defaultp>
|
||||
#define m3T glm::tmat3x3<T, glm::defaultp>
|
||||
#define m4T glm::tmat4x4<T, glm::defaultp>
|
||||
#define m23T glm::tmat2x3<T, glm::defaultp>
|
||||
#else
|
||||
#define v2T glm::detail::tvec2<T, glm::defaultp>
|
||||
#define v3T glm::detail::tvec3<T, glm::defaultp>
|
||||
#define v4T glm::detail::tvec4<T, glm::defaultp>
|
||||
#define m2T glm::detail::tmat2x2<T, glm::defaultp>
|
||||
#define m3T glm::detail::tmat3x3<T, glm::defaultp>
|
||||
#define m4T glm::detail::tmat4x4<T, glm::defaultp>
|
||||
#define m23T glm::detail::tmat2x3<T, glm::defaultp>
|
||||
#endif
|
||||
|
||||
enum eInterp : uint { EMBER_INTERP_LINEAR = 0, EMBER_INTERP_SMOOTH = 1 };
|
||||
enum eAffineInterp : uint { INTERP_LINEAR = 0, INTERP_LOG = 1, INTERP_COMPAT = 2, INTERP_OLDER = 3 };
|
||||
|
@ -78,5 +78,7 @@
|
||||
using namespace tbb;
|
||||
using namespace std;
|
||||
using namespace std::chrono;
|
||||
using namespace glm;
|
||||
using namespace glm::detail;
|
||||
using glm::uint;
|
||||
using glm::uint16;
|
||||
|
@ -145,7 +145,7 @@ public:
|
||||
os << " zoom=\"" << ember.m_Zoom << "\"";
|
||||
|
||||
os << " rotate=\"" << ember.m_Rotate << "\"";
|
||||
os << " supersample=\"" << max<size_t>(1, ember.m_Supersample) << "\"";
|
||||
os << " supersample=\"" << std::max<size_t>(1, ember.m_Supersample) << "\"";
|
||||
os << " filter=\"" << ember.m_SpatialFilterRadius << "\"";
|
||||
|
||||
os << " filter_shape=\"" << ToLower(SpatialFilterCreator<T>::ToString(ember.m_SpatialFilterType)) << "\"";
|
||||
@ -207,7 +207,19 @@ public:
|
||||
os << "\"";
|
||||
|
||||
os << " new_linear=\"1\"";
|
||||
os << ">\n";
|
||||
os << " curves=\"";
|
||||
|
||||
for (glm::length_t ci = 0; ci < 4; ci++)
|
||||
{
|
||||
for (glm::length_t cj = 0; cj < 4; cj++)
|
||||
{
|
||||
os << ember.m_Curves.m_Points[ci][cj].x << " ";
|
||||
os << ember.m_Curves.m_Points[ci][cj].y << " ";
|
||||
os << ember.m_Curves.m_Weights[ci][cj] << " ";
|
||||
}
|
||||
}
|
||||
|
||||
os << "\">\n";
|
||||
|
||||
//This is a grey area, what to do about symmetry to avoid duplicating the symmetry xforms when reading back?//TODO//BUG.
|
||||
//if (ember.m_Symmetry)
|
||||
|
@ -533,7 +533,7 @@ public:
|
||||
}
|
||||
|
||||
//Identify the most saturated channel.
|
||||
maxc = max(max(cBuf[0], cBuf[1]), cBuf[2]);
|
||||
maxc = std::max(std::max(cBuf[0], cBuf[1]), cBuf[2]);
|
||||
maxa = ls * maxc;
|
||||
|
||||
//If a channel is saturated and highlight power is non-negative
|
||||
|
@ -28,41 +28,6 @@ Renderer<T, bucketT>::~Renderer()
|
||||
/// Non-virtual processing functions.
|
||||
/// </summary>
|
||||
|
||||
/// <summary>
|
||||
/// Compute the camera.
|
||||
/// This sets up the bounds of the cartesian plane that the raster bounds correspond to.
|
||||
/// This must be called after ComputeBounds() which sets up the raster bounds.
|
||||
/// </summary>
|
||||
template <typename T, typename bucketT>
|
||||
void Renderer<T, bucketT>::ComputeCamera()
|
||||
{
|
||||
m_Scale = pow(T(2.0), Zoom());
|
||||
m_ScaledQuality = Quality() * m_Scale * m_Scale;
|
||||
|
||||
m_PixelsPerUnitX = PixelsPerUnit() * m_Scale;
|
||||
m_PixelsPerUnitY = m_PixelsPerUnitX;
|
||||
m_PixelsPerUnitX /= PixelAspectRatio();
|
||||
|
||||
T shift = 0;
|
||||
T t0 = T(m_GutterWidth) / (Supersample() * m_PixelsPerUnitX);
|
||||
T t1 = T(m_GutterWidth) / (Supersample() * m_PixelsPerUnitY);
|
||||
|
||||
//These go from ll to ur, moving from negative to positive.
|
||||
m_LowerLeftX = CenterX() - FinalRasW() / m_PixelsPerUnitX / T(2.0);
|
||||
m_LowerLeftY = CenterY() - FinalRasH() / m_PixelsPerUnitY / T(2.0);
|
||||
m_UpperRightX = m_LowerLeftX + FinalRasW() / m_PixelsPerUnitX;
|
||||
m_UpperRightY = m_LowerLeftY + FinalRasH() / m_PixelsPerUnitY;
|
||||
|
||||
T carLlX = m_LowerLeftX - t0;
|
||||
T carLlY = m_LowerLeftY - t1 + shift;
|
||||
T carUrX = m_UpperRightX + t0;
|
||||
T carUrY = m_UpperRightY + t1 + shift;
|
||||
|
||||
m_RotMat.MakeID();
|
||||
m_RotMat.Rotate(-Rotate());
|
||||
m_CarToRas.Init(carLlX, carLlY, carUrX, carUrY, m_SuperRasW, m_SuperRasH, PixelAspectRatio());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add an ember to the end of the embers vector and reset the rendering process.
|
||||
/// Reset the rendering process.
|
||||
@ -124,7 +89,7 @@ void Renderer<T, bucketT>::ComputeBounds()
|
||||
//If the radius of the density estimation filter is greater than the
|
||||
//gutter width, have to pad with more. Otherwise, use the same value.
|
||||
for (size_t i = 0; i < m_Embers.size(); i++)
|
||||
maxDEFilterWidth = max(size_t(ceil(m_Embers[i].m_MaxRadDE) * m_Ember.m_Supersample), maxDEFilterWidth);
|
||||
maxDEFilterWidth = std::max<size_t>(size_t(ceil(m_Embers[i].m_MaxRadDE) * m_Ember.m_Supersample), maxDEFilterWidth);
|
||||
|
||||
//Need an extra ss = (int)floor(m_Supersample / 2.0) of pixels so that a local iteration count for DE can be determined.//SMOULDER
|
||||
if (maxDEFilterWidth > 0)
|
||||
@ -140,6 +105,50 @@ void Renderer<T, bucketT>::ComputeBounds()
|
||||
m_SuperSize = m_SuperRasW * m_SuperRasH;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compute the scale based on the zoom, then the quality based on the computed scale.
|
||||
/// This sets up the bounds of the cartesian plane that the raster bounds correspond to.
|
||||
/// This must be called before ComputeCamera() which will use scale.
|
||||
/// </summary>
|
||||
template <typename T, typename bucketT>
|
||||
void Renderer<T, bucketT>::ComputeQuality()
|
||||
{
|
||||
m_Scale = pow(T(2.0), Zoom());
|
||||
m_ScaledQuality = Quality() * m_Scale * m_Scale;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compute the camera.
|
||||
/// This sets up the bounds of the cartesian plane that the raster bounds correspond to.
|
||||
/// This must be called after ComputeBounds() which sets up the raster bounds.
|
||||
/// </summary>
|
||||
template <typename T, typename bucketT>
|
||||
void Renderer<T, bucketT>::ComputeCamera()
|
||||
{
|
||||
m_PixelsPerUnitX = PixelsPerUnit() * m_Scale;
|
||||
m_PixelsPerUnitY = m_PixelsPerUnitX;
|
||||
m_PixelsPerUnitX /= PixelAspectRatio();
|
||||
|
||||
T shift = 0;
|
||||
T t0 = T(m_GutterWidth) / (Supersample() * m_PixelsPerUnitX);
|
||||
T t1 = T(m_GutterWidth) / (Supersample() * m_PixelsPerUnitY);
|
||||
|
||||
//These go from ll to ur, moving from negative to positive.
|
||||
m_LowerLeftX = CenterX() - FinalRasW() / m_PixelsPerUnitX / T(2.0);
|
||||
m_LowerLeftY = CenterY() - FinalRasH() / m_PixelsPerUnitY / T(2.0);
|
||||
m_UpperRightX = m_LowerLeftX + FinalRasW() / m_PixelsPerUnitX;
|
||||
m_UpperRightY = m_LowerLeftY + FinalRasH() / m_PixelsPerUnitY;
|
||||
|
||||
T carLlX = m_LowerLeftX - t0;
|
||||
T carLlY = m_LowerLeftY - t1 + shift;
|
||||
T carUrX = m_UpperRightX + t0;
|
||||
T carUrY = m_UpperRightY + t1 + shift;
|
||||
|
||||
m_RotMat.MakeID();
|
||||
m_RotMat.Rotate(-Rotate());
|
||||
m_CarToRas.Init(carLlX, carLlY, carUrX, carUrY, m_SuperRasW, m_SuperRasH, PixelAspectRatio());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the current ember.
|
||||
/// This will also populate the vector of embers with a single element copy
|
||||
@ -327,7 +336,7 @@ eRenderStatus Renderer<T, bucketT>::Run(vector<byte>& finalImage, double time, s
|
||||
bool accumOnly = m_ProcessAction == ACCUM_ONLY;
|
||||
bool resume = m_ProcessState != NONE;
|
||||
bool newFilterAlloc;
|
||||
size_t temporalSample = 0;
|
||||
size_t i, temporalSample = 0;
|
||||
T deTime;
|
||||
eRenderStatus success = RENDER_OK;
|
||||
//double iterationTime = 0;
|
||||
@ -353,6 +362,7 @@ eRenderStatus Renderer<T, bucketT>::Run(vector<byte>& finalImage, double time, s
|
||||
m_Gamma = 0;
|
||||
m_Vibrancy = 0;//Accumulate these after each temporal sample.
|
||||
m_VibGamCount = 0;
|
||||
m_CurvesSet = false;
|
||||
m_Background.Clear();
|
||||
}
|
||||
//User requested an increase in quality after finishing.
|
||||
@ -365,6 +375,7 @@ eRenderStatus Renderer<T, bucketT>::Run(vector<byte>& finalImage, double time, s
|
||||
m_Vibrancy = 0;
|
||||
m_VibGamCount = 0;
|
||||
m_Background.Clear();
|
||||
ComputeQuality();//Must recompute quality when doing a quality increase.
|
||||
}
|
||||
|
||||
//Make sure values are within valid range.
|
||||
@ -427,8 +438,9 @@ eRenderStatus Renderer<T, bucketT>::Run(vector<byte>& finalImage, double time, s
|
||||
Interpolater<T>::Interpolate(m_Embers, deTime, 0, m_Ember);
|
||||
//it.Toc("Interp 2");
|
||||
|
||||
ClampGte<T>(m_Ember.m_MinRadDE, 0);
|
||||
ClampGte<T>(m_Ember.m_MaxRadDE, 0);
|
||||
ClampGteRef<T>(m_Ember.m_MinRadDE, 0);
|
||||
ClampGteRef<T>(m_Ember.m_MaxRadDE, 0);
|
||||
ClampGteRef<T>(m_Ember.m_MaxRadDE, m_Ember.m_MinRadDE);
|
||||
|
||||
if (!CreateDEFilter(newFilterAlloc))
|
||||
{
|
||||
@ -446,7 +458,7 @@ eRenderStatus Renderer<T, bucketT>::Run(vector<byte>& finalImage, double time, s
|
||||
|
||||
//Interpolate again.
|
||||
//it.Tic();
|
||||
if (m_Embers.size() > 1)
|
||||
if (TemporalSamples() > 1 && m_Embers.size() > 1)
|
||||
Interpolater<T>::Interpolate(m_Embers, temporalTime, 0, m_Ember);//This will perform all necessary precalcs via the ember/xform/variation assignment operators.
|
||||
|
||||
//it.Toc("Interp 3");
|
||||
@ -458,13 +470,16 @@ eRenderStatus Renderer<T, bucketT>::Run(vector<byte>& finalImage, double time, s
|
||||
goto Finish;
|
||||
}
|
||||
|
||||
ComputeCamera();
|
||||
|
||||
//For each temporal sample, the palette m_Dmap needs to be re-created with color scalar. 1 if no temporal samples.
|
||||
MakeDmap(colorScalar);
|
||||
//Don't need to do this every time through for a single image.
|
||||
if (TemporalSamples() > 1 || !resume)
|
||||
{
|
||||
ComputeQuality();
|
||||
ComputeCamera();
|
||||
MakeDmap(colorScalar);//For each temporal sample, the palette m_Dmap needs to be re-created with color scalar. 1 if no temporal samples.
|
||||
}
|
||||
|
||||
//The actual number of times to iterate. Each thread will get (totalIters / ThreadCount) iters to do.
|
||||
//This is based on zoom and scale calculated in ComputeCamera().
|
||||
//This is based on zoom and scale calculated in ComputeQuality().
|
||||
//Note that the iter count is based on the final image dimensions, and not the super sampled dimensions.
|
||||
size_t itersPerTemporalSample = ItersPerTemporalSample();//The total number of iterations for this temporal sample without overrides.
|
||||
size_t sampleItersToDo;//The number of iterations to actually do in this sample, considering overrides.
|
||||
@ -474,7 +489,7 @@ eRenderStatus Renderer<T, bucketT>::Run(vector<byte>& finalImage, double time, s
|
||||
else
|
||||
sampleItersToDo = itersPerTemporalSample;//Run as many iters as specified to complete this temporal sample.
|
||||
|
||||
sampleItersToDo = min(sampleItersToDo, itersPerTemporalSample - m_LastIter);
|
||||
sampleItersToDo = std::min<size_t>(sampleItersToDo, itersPerTemporalSample - m_LastIter);
|
||||
EmberStats stats = Iterate(sampleItersToDo, temporalSample);//The heavy work is done here.
|
||||
|
||||
//If no iters were executed, something went catastrophically wrong.
|
||||
@ -602,6 +617,13 @@ AccumOnly:
|
||||
//Make sure a filter has been created.
|
||||
CreateSpatialFilter(newFilterAlloc);
|
||||
|
||||
m_CurvesSet = m_Ember.m_Curves.CurvesSet();
|
||||
|
||||
//Color curves must be re-calculated as well.
|
||||
if (m_CurvesSet)
|
||||
for (i = 0; i < COLORMAP_LENGTH; i++)
|
||||
m_Csa[i] = m_Ember.m_Curves.BezierFunc(i / T(COLORMAP_LENGTH_MINUS_1)) * T(COLORMAP_LENGTH_MINUS_1);
|
||||
|
||||
if (AccumulatorToFinalImage(finalImage, finalOffset) == RENDER_OK)
|
||||
{
|
||||
m_Stats.m_RenderMs = m_RenderTimer.Toc();//Record total time from the very beginning to the very end, including all intermediate calls.
|
||||
@ -839,17 +861,17 @@ eRenderStatus Renderer<T, bucketT>::GaussianDensityFilter()
|
||||
parallel_for(size_t(0), threads, [&] (size_t threadIndex)
|
||||
{
|
||||
size_t pixelNumber = 0;
|
||||
int localStartRow = int(min(startRow + (threadIndex * chunkSize), endRow - 1));
|
||||
int localEndRow = int(min(localStartRow + chunkSize, endRow));
|
||||
int localStartRow = int(std::min<size_t>(startRow + (threadIndex * chunkSize), endRow - 1));
|
||||
int localEndRow = int(std::min<size_t>(localStartRow + chunkSize, endRow));
|
||||
size_t pixelsThisThread = size_t(localEndRow - localStartRow) * m_SuperRasW;
|
||||
double lastPercent = 0;
|
||||
glm::detail::tvec4<bucketT, glm::defaultp> logScaleBucket;
|
||||
tvec4<bucketT, glm::defaultp> logScaleBucket;
|
||||
|
||||
for (intmax_t j = localStartRow; (j < localEndRow) && !m_Abort; j++)
|
||||
{
|
||||
size_t bucketRowStart = j * m_SuperRasW;//Pull out of inner loop for optimization.
|
||||
const glm::detail::tvec4<bucketT, glm::defaultp>* bucket;
|
||||
const glm::detail::tvec4<bucketT, glm::defaultp>* buckets = m_HistBuckets.data();
|
||||
const tvec4<bucketT, glm::defaultp>* bucket;
|
||||
const tvec4<bucketT, glm::defaultp>* buckets = m_HistBuckets.data();
|
||||
const T* filterCoefs = m_DensityFilter->Coefs();
|
||||
const T* filterWidths = m_DensityFilter->Widths();
|
||||
|
||||
@ -875,10 +897,10 @@ eRenderStatus Renderer<T, bucketT>::GaussianDensityFilter()
|
||||
//The original contained a glaring flaw as it would run past the boundaries of the buffers
|
||||
//when calculating the density for a box centered on the last row or column.
|
||||
//Clamp here to not run over the edge.
|
||||
intmax_t densityBoxLeftX = (i - min(i, ss));
|
||||
intmax_t densityBoxRightX = (i + min(ss, intmax_t(m_SuperRasW) - i - 1));
|
||||
intmax_t densityBoxTopY = (j - min(j, ss));
|
||||
intmax_t densityBoxBottomY = (j + min(ss, intmax_t(m_SuperRasH) - j - 1));
|
||||
intmax_t densityBoxLeftX = (i - std::min(i, ss));
|
||||
intmax_t densityBoxRightX = (i + std::min(ss, intmax_t(m_SuperRasW) - i - 1));
|
||||
intmax_t densityBoxTopY = (j - std::min(j, ss));
|
||||
intmax_t densityBoxBottomY = (j + std::min(ss, intmax_t(m_SuperRasH) - j - 1));
|
||||
|
||||
//Count density in ssxss area.
|
||||
//Original went one col at a time, which is cache inefficient. Go one row at at time here for a slight speedup.
|
||||
@ -1048,7 +1070,7 @@ eRenderStatus Renderer<T, bucketT>::AccumulatorToFinalImage(byte* pixels, size_t
|
||||
Color<bucketT> newBucket;
|
||||
size_t pixelsRowStart = (m_YAxisUp ? ((FinalRasH() - j) - 1) : j) * FinalRowSize();//Pull out of inner loop for optimization.
|
||||
size_t y = m_DensityFilterOffset + (j * Supersample());//Start at the beginning row of each super sample block.
|
||||
uint16* p16;
|
||||
glm::uint16* p16;
|
||||
|
||||
for (size_t i = 0; i < FinalRasW(); i++, pixelsRowStart += PixelSize())
|
||||
{
|
||||
@ -1074,13 +1096,20 @@ eRenderStatus Renderer<T, bucketT>::AccumulatorToFinalImage(byte* pixels, size_t
|
||||
|
||||
if (BytesPerChannel() == 2)
|
||||
{
|
||||
p16 = reinterpret_cast<uint16*>(pixels + pixelsRowStart);
|
||||
p16 = reinterpret_cast<glm::uint16*>(pixels + pixelsRowStart);
|
||||
|
||||
if (EarlyClip())
|
||||
{
|
||||
p16[0] = uint16(Clamp<bucketT>(newBucket.r, 0, 255) * bucketT(256));
|
||||
p16[1] = uint16(Clamp<bucketT>(newBucket.g, 0, 255) * bucketT(256));
|
||||
p16[2] = uint16(Clamp<bucketT>(newBucket.b, 0, 255) * bucketT(256));
|
||||
if (m_CurvesSet)
|
||||
{
|
||||
CurveAdjust(newBucket.r, 1);
|
||||
CurveAdjust(newBucket.g, 2);
|
||||
CurveAdjust(newBucket.b, 3);
|
||||
}
|
||||
|
||||
p16[0] = glm::uint16(Clamp<bucketT>(newBucket.r, 0, 255) * bucketT(256));
|
||||
p16[1] = glm::uint16(Clamp<bucketT>(newBucket.g, 0, 255) * bucketT(256));
|
||||
p16[2] = glm::uint16(Clamp<bucketT>(newBucket.b, 0, 255) * bucketT(256));
|
||||
|
||||
if (NumChannels() > 3)
|
||||
{
|
||||
@ -1092,13 +1121,20 @@ eRenderStatus Renderer<T, bucketT>::AccumulatorToFinalImage(byte* pixels, size_t
|
||||
}
|
||||
else
|
||||
{
|
||||
GammaCorrection(*(reinterpret_cast<glm::detail::tvec4<bucketT, glm::defaultp>*>(&newBucket)), background, g, linRange, vibrancy, NumChannels() > 3, true, p16);
|
||||
GammaCorrection(*(reinterpret_cast<tvec4<bucketT, glm::defaultp>*>(&newBucket)), background, g, linRange, vibrancy, NumChannels() > 3, true, p16);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (EarlyClip())
|
||||
{
|
||||
if (m_CurvesSet)
|
||||
{
|
||||
CurveAdjust(newBucket.r, 1);
|
||||
CurveAdjust(newBucket.g, 2);
|
||||
CurveAdjust(newBucket.b, 3);
|
||||
}
|
||||
|
||||
pixels[pixelsRowStart] = byte(Clamp<bucketT>(newBucket.r, 0, 255));
|
||||
pixels[pixelsRowStart + 1] = byte(Clamp<bucketT>(newBucket.g, 0, 255));
|
||||
pixels[pixelsRowStart + 2] = byte(Clamp<bucketT>(newBucket.b, 0, 255));
|
||||
@ -1113,7 +1149,7 @@ eRenderStatus Renderer<T, bucketT>::AccumulatorToFinalImage(byte* pixels, size_t
|
||||
}
|
||||
else
|
||||
{
|
||||
GammaCorrection(*(reinterpret_cast<glm::detail::tvec4<bucketT, glm::defaultp>*>(&newBucket)), background, g, linRange, vibrancy, NumChannels() > 3, true, pixels + pixelsRowStart);
|
||||
GammaCorrection(*(reinterpret_cast<tvec4<bucketT, glm::defaultp>*>(&newBucket)), background, g, linRange, vibrancy, NumChannels() > 3, true, pixels + pixelsRowStart);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1183,7 +1219,7 @@ EmberStats Renderer<T, bucketT>::Iterate(size_t iterCount, size_t temporalSample
|
||||
IterParams<T> params;
|
||||
|
||||
m_BadVals[threadIndex] = 0;
|
||||
params.m_Count = min(totalItersPerThread, SubBatchSize());
|
||||
params.m_Count = std::min(totalItersPerThread, SubBatchSize());
|
||||
params.m_Skip = FuseCount();
|
||||
//params.m_OneColDiv2 = m_CarToRas.OneCol() / 2;
|
||||
//params.m_OneRowDiv2 = m_CarToRas.OneRow() / 2;
|
||||
@ -1193,7 +1229,7 @@ EmberStats Renderer<T, bucketT>::Iterate(size_t iterCount, size_t temporalSample
|
||||
{
|
||||
//Must recalculate the number of iters to run on each sub batch because the last batch will most likely have less than SubBatchSize iters.
|
||||
//For example, if 51,000 are requested, and the sbs is 10,000, it should run 5 sub batches of 10,000 iters, and one final sub batch of 1,000 iters.
|
||||
params.m_Count = min(params.m_Count, totalItersPerThread - m_SubBatch[threadIndex]);
|
||||
params.m_Count = std::min(params.m_Count, totalItersPerThread - m_SubBatch[threadIndex]);
|
||||
|
||||
//Use first as random point, the rest are iterated points.
|
||||
//Note that this gets reset with a new random point for each subBatchSize iterations.
|
||||
@ -1290,16 +1326,16 @@ void Renderer<T, bucketT>::PixelAspectRatio(T pixelAspectRatio)
|
||||
/// Non-virtual renderer properties, getters only.
|
||||
/// </summary>
|
||||
|
||||
template <typename T, typename bucketT> T Renderer<T, bucketT>::Scale() const { return m_Scale; }
|
||||
template <typename T, typename bucketT> T Renderer<T, bucketT>::PixelsPerUnitX() const { return m_PixelsPerUnitX; }
|
||||
template <typename T, typename bucketT> T Renderer<T, bucketT>::PixelsPerUnitY() const { return m_PixelsPerUnitY; }
|
||||
template <typename T, typename bucketT> T Renderer<T, bucketT>::K1() const { return m_K1; }
|
||||
template <typename T, typename bucketT> T Renderer<T, bucketT>::K2() const { return m_K2; }
|
||||
template <typename T, typename bucketT> const CarToRas<T>* Renderer<T, bucketT>::CoordMap() const { return &m_CarToRas; }
|
||||
template <typename T, typename bucketT> glm::detail::tvec4<bucketT, glm::defaultp>* Renderer<T, bucketT>::HistBuckets() { return m_HistBuckets.data(); }
|
||||
template <typename T, typename bucketT> glm::detail::tvec4<bucketT, glm::defaultp>* Renderer<T, bucketT>::AccumulatorBuckets() { return m_AccumulatorBuckets.data(); }
|
||||
template <typename T, typename bucketT> SpatialFilter<T>* Renderer<T, bucketT>::GetSpatialFilter() { return m_SpatialFilter.get(); }
|
||||
template <typename T, typename bucketT> TemporalFilter<T>* Renderer<T, bucketT>::GetTemporalFilter() { return m_TemporalFilter.get(); }
|
||||
template <typename T, typename bucketT> T Renderer<T, bucketT>::Scale() const { return m_Scale; }
|
||||
template <typename T, typename bucketT> T Renderer<T, bucketT>::PixelsPerUnitX() const { return m_PixelsPerUnitX; }
|
||||
template <typename T, typename bucketT> T Renderer<T, bucketT>::PixelsPerUnitY() const { return m_PixelsPerUnitY; }
|
||||
template <typename T, typename bucketT> T Renderer<T, bucketT>::K1() const { return m_K1; }
|
||||
template <typename T, typename bucketT> T Renderer<T, bucketT>::K2() const { return m_K2; }
|
||||
template <typename T, typename bucketT> const CarToRas<T>* Renderer<T, bucketT>::CoordMap() const { return &m_CarToRas; }
|
||||
template <typename T, typename bucketT> tvec4<bucketT, glm::defaultp>* Renderer<T, bucketT>::HistBuckets() { return m_HistBuckets.data(); }
|
||||
template <typename T, typename bucketT> tvec4<bucketT, glm::defaultp>* Renderer<T, bucketT>::AccumulatorBuckets() { return m_AccumulatorBuckets.data(); }
|
||||
template <typename T, typename bucketT> SpatialFilter<T>* Renderer<T, bucketT>::GetSpatialFilter() { return m_SpatialFilter.get(); }
|
||||
template <typename T, typename bucketT> TemporalFilter<T>* Renderer<T, bucketT>::GetTemporalFilter() { return m_TemporalFilter.get(); }
|
||||
|
||||
/// <summary>
|
||||
/// Virtual renderer properties overridden from RendererBase, getters only.
|
||||
@ -1405,7 +1441,7 @@ void Renderer<T, bucketT>::Accumulate(QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand, Poin
|
||||
{
|
||||
size_t histIndex, intColorIndex, histSize = m_HistBuckets.size();
|
||||
bucketT colorIndex, colorIndexFrac;
|
||||
const glm::detail::tvec4<bucketT, glm::defaultp>* dmap = &(palette->m_Entries[0]);
|
||||
const tvec4<bucketT, glm::defaultp>* dmap = &(palette->m_Entries[0]);
|
||||
//T oneColDiv2 = m_CarToRas.OneCol() / 2;
|
||||
//T oneRowDiv2 = m_CarToRas.OneRow() / 2;
|
||||
|
||||
@ -1512,7 +1548,7 @@ void Renderer<T, bucketT>::Accumulate(QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand, Poin
|
||||
/// <param name="j">The row of the bucket</param>
|
||||
/// <param name="jj">The offset to add to the row</param>
|
||||
template <typename T, typename bucketT>
|
||||
void Renderer<T, bucketT>::AddToAccum(const glm::detail::tvec4<bucketT, glm::defaultp>& bucket, intmax_t i, intmax_t ii, intmax_t j, intmax_t jj)
|
||||
void Renderer<T, bucketT>::AddToAccum(const tvec4<bucketT, glm::defaultp>& bucket, intmax_t i, intmax_t ii, intmax_t j, intmax_t jj)
|
||||
{
|
||||
if (j + jj >= 0 && j + jj < intmax_t(m_SuperRasH) && i + ii >= 0 && i + ii < intmax_t(m_SuperRasW))
|
||||
m_AccumulatorBuckets[(i + ii) + ((j + jj) * m_SuperRasW)] += bucket;
|
||||
@ -1536,7 +1572,7 @@ void Renderer<T, bucketT>::AddToAccum(const glm::detail::tvec4<bucketT, glm::def
|
||||
/// <param name="correctedChannels">The storage space for the corrected values to be written to</param>
|
||||
template <typename T, typename bucketT>
|
||||
template <typename accumT>
|
||||
void Renderer<T, bucketT>::GammaCorrection(glm::detail::tvec4<bucketT, glm::defaultp>& bucket, Color<T>& background, T g, T linRange, T vibrancy, bool doAlpha, bool scale, accumT* correctedChannels)
|
||||
void Renderer<T, bucketT>::GammaCorrection(tvec4<bucketT, glm::defaultp>& bucket, Color<T>& background, T g, T linRange, T vibrancy, bool doAlpha, bool scale, accumT* correctedChannels)
|
||||
{
|
||||
T alpha, ls, a;
|
||||
bucketT newRgb[3];//Would normally use a Color<bucketT>, but don't want to call a needless constructor every time this function is called, which is once per pixel.
|
||||
@ -1573,9 +1609,16 @@ void Renderer<T, bucketT>::GammaCorrection(glm::detail::tvec4<bucketT, glm::defa
|
||||
}
|
||||
|
||||
if (!scale)
|
||||
{
|
||||
correctedChannels[rgbi] = accumT(Clamp<T>(a, 0, 255));//Early clip, just assign directly.
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_CurvesSet)
|
||||
CurveAdjust(a, rgbi + 1);
|
||||
|
||||
correctedChannels[rgbi] = accumT(Clamp<T>(a, 0, 255) * scaleVal);//Final accum, multiply by 1 for 8 bpc, or 256 for 16 bpc.
|
||||
}
|
||||
}
|
||||
|
||||
if (doAlpha)
|
||||
@ -1589,6 +1632,15 @@ void Renderer<T, bucketT>::GammaCorrection(glm::detail::tvec4<bucketT, glm::defa
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename bucketT>
|
||||
void Renderer<T, bucketT>::CurveAdjust(T& a, const glm::length_t& index)
|
||||
{
|
||||
size_t tempIndex = size_t(Clamp<T>(a, 0, COLORMAP_LENGTH_MINUS_1));
|
||||
size_t tempIndex2 = size_t(Clamp<T>(m_Csa[tempIndex].x, 0, COLORMAP_LENGTH_MINUS_1));
|
||||
|
||||
a = std::round(m_Csa[tempIndex2][index]);
|
||||
}
|
||||
|
||||
//This class had to be implemented in a cpp file because the compiler was breaking.
|
||||
//So the explicit instantiation must be declared here rather than in Ember.cpp where
|
||||
//all of the other classes are done.
|
||||
|
@ -56,13 +56,14 @@ public:
|
||||
|
||||
//Virtual processing functions overriden from RendererBase.
|
||||
virtual void ComputeBounds() override;
|
||||
virtual void ComputeQuality() override;
|
||||
virtual void ComputeCamera() override;
|
||||
virtual void SetEmber(Ember<T>& ember, eProcessAction action = FULL_RENDER) override;
|
||||
virtual void SetEmber(vector<Ember<T>>& embers) override;
|
||||
virtual bool CreateDEFilter(bool& newAlloc) override;
|
||||
virtual bool CreateSpatialFilter(bool& newAlloc) override;
|
||||
virtual bool CreateTemporalFilter(bool& newAlloc) override;
|
||||
virtual size_t HistBucketSize() const override { return sizeof(glm::detail::tvec4<bucketT, glm::defaultp>); }
|
||||
virtual size_t HistBucketSize() const override { return sizeof(tvec4<bucketT, glm::defaultp>); }
|
||||
virtual eRenderStatus Run(vector<byte>& finalImage, double time = 0, size_t subBatchCountOverride = 0, bool forceOutput = false, size_t finalOffset = 0) override;
|
||||
virtual EmberImageComments ImageComments(EmberStats& stats, size_t printEditDepth = 0, bool intPalette = false, bool hexPalette = true) override;
|
||||
|
||||
@ -83,16 +84,16 @@ public:
|
||||
void PixelAspectRatio(T pixelAspectRatio);
|
||||
|
||||
//Non-virtual renderer properties, getters only.
|
||||
inline T Scale() const;
|
||||
inline T PixelsPerUnitX() const;
|
||||
inline T PixelsPerUnitY() const;
|
||||
inline T K1() const;
|
||||
inline T K2() const;
|
||||
inline const CarToRas<T>* CoordMap() const;
|
||||
inline glm::detail::tvec4<bucketT, glm::defaultp>* HistBuckets();
|
||||
inline glm::detail::tvec4<bucketT, glm::defaultp>* AccumulatorBuckets();
|
||||
inline SpatialFilter<T>* GetSpatialFilter();
|
||||
inline TemporalFilter<T>* GetTemporalFilter();
|
||||
inline T Scale() const;
|
||||
inline T PixelsPerUnitX() const;
|
||||
inline T PixelsPerUnitY() const;
|
||||
inline T K1() const;
|
||||
inline T K2() const;
|
||||
inline const CarToRas<T>* CoordMap() const;
|
||||
inline tvec4<bucketT, glm::defaultp>* HistBuckets();
|
||||
inline tvec4<bucketT, glm::defaultp>* AccumulatorBuckets();
|
||||
inline SpatialFilter<T>* GetSpatialFilter();
|
||||
inline TemporalFilter<T>* GetTemporalFilter();
|
||||
|
||||
//Virtual renderer properties overridden from RendererBase, getters only.
|
||||
virtual double ScaledQuality() const override;
|
||||
@ -150,8 +151,9 @@ protected:
|
||||
private:
|
||||
//Miscellaneous non-virtual functions used only in this class.
|
||||
void Accumulate(QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand, Point<T>* samples, size_t sampleCount, const Palette<bucketT>* palette);
|
||||
/*inline*/ void AddToAccum(const glm::detail::tvec4<bucketT, glm::defaultp>& bucket, intmax_t i, intmax_t ii, intmax_t j, intmax_t jj);
|
||||
template <typename accumT> void GammaCorrection(glm::detail::tvec4<bucketT, glm::defaultp>& bucket, Color<T>& background, T g, T linRange, T vibrancy, bool doAlpha, bool scale, accumT* correctedChannels);
|
||||
/*inline*/ void AddToAccum(const tvec4<bucketT, glm::defaultp>& bucket, intmax_t i, intmax_t ii, intmax_t j, intmax_t jj);
|
||||
template <typename accumT> void GammaCorrection(tvec4<bucketT, glm::defaultp>& bucket, Color<T>& background, T g, T linRange, T vibrancy, bool doAlpha, bool scale, accumT* correctedChannels);
|
||||
void CurveAdjust(T& a, const glm::length_t& index);
|
||||
|
||||
protected:
|
||||
T m_Scale;
|
||||
@ -177,9 +179,9 @@ protected:
|
||||
Iterator<T>* m_Iterator;
|
||||
unique_ptr<StandardIterator<T>> m_StandardIterator;
|
||||
unique_ptr<XaosIterator<T>> m_XaosIterator;
|
||||
Palette<bucketT> m_Dmap;
|
||||
vector<glm::detail::tvec4<bucketT, glm::defaultp>> m_HistBuckets;
|
||||
vector<glm::detail::tvec4<bucketT, glm::defaultp>> m_AccumulatorBuckets;
|
||||
Palette<bucketT> m_Dmap, m_Csa;
|
||||
vector<tvec4<bucketT, glm::defaultp>> m_HistBuckets;
|
||||
vector<tvec4<bucketT, glm::defaultp>> m_AccumulatorBuckets;
|
||||
unique_ptr<SpatialFilter<T>> m_SpatialFilter;
|
||||
unique_ptr<TemporalFilter<T>> m_TemporalFilter;
|
||||
unique_ptr<DensityFilter<T>> m_DensityFilter;
|
||||
|
@ -16,6 +16,7 @@ RendererBase::RendererBase()
|
||||
m_YAxisUp = false;
|
||||
m_InsertPalette = false;
|
||||
m_ReclaimOnResize = false;
|
||||
m_CurvesSet = false;
|
||||
m_NumChannels = 3;
|
||||
m_BytesPerChannel = 1;
|
||||
m_SuperSize = 0;
|
||||
@ -469,7 +470,7 @@ void RendererBase::ThreadCount(size_t threads, const char* seedString)
|
||||
if (seedString)
|
||||
{
|
||||
memset(seeds, 0, isaacSize * sizeof(ISAAC_INT));
|
||||
memcpy(reinterpret_cast<char*>(seeds), seedString, min(strlen(seedString), isaacSize * sizeof(ISAAC_INT)));
|
||||
memcpy(reinterpret_cast<char*>(seeds), seedString, std::min(strlen(seedString), isaacSize * sizeof(ISAAC_INT)));
|
||||
}
|
||||
|
||||
//This is critical for multithreading, otherwise the threads all happen
|
||||
|
@ -117,6 +117,7 @@ public:
|
||||
virtual bool CreateSpatialFilter(bool& newAlloc) = 0;
|
||||
virtual bool CreateTemporalFilter(bool& newAlloc) = 0;
|
||||
virtual void ComputeBounds() = 0;
|
||||
virtual void ComputeQuality() = 0;
|
||||
virtual void ComputeCamera() = 0;
|
||||
virtual eRenderStatus Run(vector<byte>& finalImage, double time = 0, size_t subBatchCountOverride = 0, bool forceOutput = false, size_t finalOffset = 0) = 0;
|
||||
virtual EmberImageComments ImageComments(EmberStats& stats, size_t printEditDepth = 0, bool intPalette = false, bool hexPalette = true) = 0;
|
||||
@ -201,6 +202,7 @@ protected:
|
||||
bool m_InFinalAccum;
|
||||
bool m_InsertPalette;
|
||||
bool m_ReclaimOnResize;
|
||||
bool m_CurvesSet;
|
||||
volatile bool m_Abort;
|
||||
size_t m_SuperRasW;
|
||||
size_t m_SuperRasH;
|
||||
|
@ -1288,6 +1288,7 @@ public:
|
||||
m_Renderer->CreateSpatialFilter(newAlloc);
|
||||
m_Renderer->CreateDEFilter(newAlloc);
|
||||
m_Renderer->ComputeBounds();
|
||||
m_Renderer->ComputeQuality();
|
||||
m_Renderer->ComputeCamera();
|
||||
|
||||
if (ember.XaosPresent())
|
||||
|
@ -1506,7 +1506,7 @@ public:
|
||||
{
|
||||
case REAL :
|
||||
{
|
||||
*m_Param = max(min(val, m_Max), m_Min);
|
||||
*m_Param = std::max(std::min(val, m_Max), m_Min);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1524,7 +1524,7 @@ public:
|
||||
|
||||
case REAL_NONZERO :
|
||||
{
|
||||
T vd = max(min(val, m_Max), m_Min);
|
||||
T vd = std::max(std::min(val, m_Max), m_Min);
|
||||
|
||||
if (IsNearZero(vd))
|
||||
*m_Param = EPS * SignNz(vd);
|
||||
@ -1536,14 +1536,14 @@ public:
|
||||
|
||||
case INTEGER :
|
||||
{
|
||||
*m_Param = T(int(max(min<T>(T(Floor<T>(val + T(0.5))), m_Max), m_Min)));
|
||||
*m_Param = T(int(std::max(std::min<T>(T(Floor<T>(val + T(0.5))), m_Max), m_Min)));
|
||||
break;
|
||||
}
|
||||
|
||||
case INTEGER_NONZERO :
|
||||
default:
|
||||
{
|
||||
int vi = int(max(min<T>(T(Floor<T>(val + T(0.5))), m_Max), m_Min));
|
||||
int vi = int(std::max(std::min<T>(T(Floor<T>(val + T(0.5))), m_Max), m_Min));
|
||||
|
||||
if (vi == 0)
|
||||
vi = int(SignNz<T>(val));
|
||||
|
@ -3773,8 +3773,8 @@ public:
|
||||
{
|
||||
m_XAmpV = m_Weight * m_XAmp;
|
||||
m_YAmpV = m_Weight * m_YAmp;
|
||||
m_XLengthV = 1 / max(SQR(m_XLength), T(1e-20));
|
||||
m_YLengthV = 1 / max(SQR(m_YLength), T(1e-20));
|
||||
m_XLengthV = 1 / std::max(SQR(m_XLength), T(1e-20));
|
||||
m_YLengthV = 1 / std::max(SQR(m_YLength), T(1e-20));
|
||||
}
|
||||
|
||||
virtual void Random(QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override
|
||||
|
@ -4610,7 +4610,7 @@ public:
|
||||
T y = (helper.In.y * m_S) + m_CenterY;
|
||||
|
||||
//Calculate distance from center but constrain it to EPS.
|
||||
T d = max(EPS, sqrt(SQR(x) * SQR(y)));
|
||||
T d = std::max(EPS, sqrt(SQR(x) * SQR(y)));
|
||||
|
||||
//Normalize x and y.
|
||||
T nx = x / d;
|
||||
|
@ -4530,7 +4530,7 @@ public:
|
||||
m_Ay = ((fabs(m_AreaY) < 0.1) ? T(0.1) : fabs(m_AreaY)) * agdoa;
|
||||
m_Cx = m_CenterX * agdoc;
|
||||
m_Cy = m_CenterY * agdoc;
|
||||
m_B = m_Gamma * agdoa / (max(m_Ax, m_Ay));
|
||||
m_B = m_Gamma * agdoa / (std::max(m_Ax, m_Ay));
|
||||
}
|
||||
|
||||
protected:
|
||||
|
@ -2119,8 +2119,8 @@ public:
|
||||
const T ay = rand.Frand<T>(T(-0.5), T(0.5));
|
||||
const T az = rand.Frand<T>(T(-0.5), T(0.5));
|
||||
const T r = sqrt(Sqr(helper.In.x - m_X0) + Sqr(helper.In.y - m_Y0) + Sqr(helper.In.z - m_Z0));
|
||||
const T rc = ((m_Invert != 0 ? max<T>(1 - r, 0) : max<T>(r, 0)) - m_MinDist) * m_InternalScatter;//Original called a macro named min, which internally performed max.
|
||||
const T rs = max<T>(rc, 0);
|
||||
const T rc = ((m_Invert != 0 ? std::max<T>(1 - r, 0) : std::max<T>(r, 0)) - m_MinDist) * m_InternalScatter;//Original called a macro named min, which internally performed max.
|
||||
const T rs = std::max<T>(rc, 0);
|
||||
|
||||
T sigma, phi, rad, sigmas, sigmac, phis, phic;
|
||||
T scale, denom;
|
||||
@ -2279,8 +2279,8 @@ public:
|
||||
{
|
||||
const v4T random(rand.Frand<T>(T(-0.5), T(0.5)), rand.Frand<T>(T(-0.5), T(0.5)), rand.Frand<T>(T(-0.5), T(0.5)), rand.Frand<T>(T(-0.5), T(0.5)));
|
||||
const T distA = sqrt(Sqr(helper.In.x - m_X0) + Sqr(helper.In.y - m_Y0) + Sqr(helper.In.z - m_Z0));
|
||||
const T distB = m_Invert != 0 ? max<T>(1 - distA, 0) : max<T>(distA, 0);//Original called a macro named min, which internally performed max.
|
||||
const T dist = max<T>((distB - m_MinDist) * m_RMax, 0);
|
||||
const T distB = m_Invert != 0 ? std::max<T>(1 - distA, 0) : std::max<T>(distA, 0);//Original called a macro named min, which internally performed max.
|
||||
const T dist = std::max<T>((distB - m_MinDist) * m_RMax, 0);
|
||||
|
||||
switch (int(m_Type))
|
||||
{
|
||||
@ -2484,11 +2484,11 @@ public:
|
||||
break;
|
||||
case 1://Square.
|
||||
default:
|
||||
radius = max(fabs(helper.In.x - m_CenterX), max(fabs(helper.In.y - m_CenterY), (fabs(helper.In.z - m_CenterZ))));//Original called a macro named min, which internally performed max.
|
||||
radius = std::max(fabs(helper.In.x - m_CenterX), std::max(fabs(helper.In.y - m_CenterY), (fabs(helper.In.z - m_CenterZ))));//Original called a macro named min, which internally performed max.
|
||||
break;
|
||||
}
|
||||
|
||||
const T dist = max<T>(((m_InvertDistance != 0 ? max<T>(1 - radius, 0) : max<T>(radius, 0)) - m_MinDistance) * m_RMax, 0);
|
||||
const T dist = std::max<T>(((m_InvertDistance != 0 ? std::max<T>(1 - radius, 0) : std::max<T>(radius, 0)) - m_MinDistance) * m_RMax, 0);
|
||||
|
||||
switch (int(m_BlurType))
|
||||
{
|
||||
|
@ -550,8 +550,8 @@ private:
|
||||
char* attStr;
|
||||
const char* loc = __FUNCTION__;
|
||||
int soloXform = -1;
|
||||
uint i, count, index = 0;
|
||||
double vals[10];
|
||||
uint i, j, count, index = 0;
|
||||
double vals[16];
|
||||
xmlAttrPtr att, curAtt;
|
||||
xmlNodePtr editNode, childNode, motionNode;
|
||||
|
||||
@ -704,6 +704,20 @@ private:
|
||||
Atof(attStr, currentEmber.m_Hue);
|
||||
currentEmber.m_Hue = fmod(currentEmber.m_Hue, T(0.5));//Orig did fmod 1, but want it in the range -0.5 - 0.5.
|
||||
}
|
||||
else if (!Compare(curAtt->name, "curves"))
|
||||
{
|
||||
stringstream ss(attStr);
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
for (j = 0; j < 4; j++)
|
||||
{
|
||||
ss >> currentEmber.m_Curves.m_Points[i][j].x;
|
||||
ss >> currentEmber.m_Curves.m_Points[i][j].y;
|
||||
ss >> currentEmber.m_Curves.m_Weights[i][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
xmlFree(attStr);
|
||||
}
|
||||
|
Reference in New Issue
Block a user