|
@@ -18,7 +18,8 @@ mixin PPTemporalResolve
|
|
|
#endif
|
|
#endif
|
|
|
|
|
|
|
|
// 0 - System will use the velocity of the current pixel
|
|
// 0 - System will use the velocity of the current pixel
|
|
|
- // 1 - System will search 4 neighbor pixels in + pattern, and choose the velocity of the pixel nearest // to the camera
|
|
|
|
|
|
|
+ // 1 - System will search 4 neighbor pixels in + pattern, and choose the velocity of the pixel nearest
|
|
|
|
|
+ // to the camera
|
|
|
// 2 - System will search 8 surrounding pixels and choose the velocity of the pixel nearest to the camera
|
|
// 2 - System will search 8 surrounding pixels and choose the velocity of the pixel nearest to the camera
|
|
|
//
|
|
//
|
|
|
// Searching the neighborhod instead of just using current velocity yields nicer edges for objects in
|
|
// Searching the neighborhod instead of just using current velocity yields nicer edges for objects in
|
|
@@ -76,6 +77,32 @@ mixin PPTemporalResolve
|
|
|
#ifndef TEMPORAL_SMOOTH_NEIGHBORHOOD
|
|
#ifndef TEMPORAL_SMOOTH_NEIGHBORHOOD
|
|
|
#define TEMPORAL_SMOOTH_NEIGHBORHOOD 1
|
|
#define TEMPORAL_SMOOTH_NEIGHBORHOOD 1
|
|
|
#endif
|
|
#endif
|
|
|
|
|
+
|
|
|
|
|
+ // When enabled, neighborhood clipping will use an AABB intersection to clip the history value. When disabled
|
|
|
|
|
+ // just a clamp will be used instead. Not relevant when TEMPORAL_YCOCG is enabled because it always uses a clamp.
|
|
|
|
|
+ #ifndef TEMPORAL_CLIP_AABB
|
|
|
|
|
+ #define TEMPORAL_CLIP_AABB 1
|
|
|
|
|
+ #endif
|
|
|
|
|
+
|
|
|
|
|
+ // Determines how is the history value blended with the current value.
|
|
|
|
|
+ // 0 - The system will calculate the optimal blend value automatically
|
|
|
|
|
+ // >0 - A fixed blend factor will be used, equal to the multiplicative inverse of the provided value.
|
|
|
|
|
+ // (i.e. a value of 8 will result in blend factor of 1/8, meaning 12.5% of the history value will be used)
|
|
|
|
|
+ #ifndef TEMPORAL_BLEND_FACTOR
|
|
|
|
|
+ #define TEMPORAL_BLEND_FACTOR 0
|
|
|
|
|
+ #endif
|
|
|
|
|
+
|
|
|
|
|
+ // Determines how many frames should pixels deemed as "bad" (too different from current pixel) contribute to the
|
|
|
|
|
+ // current frame.
|
|
|
|
|
+ #ifndef TEMPORAL_BAD_RETENTION
|
|
|
|
|
+ #define TEMPORAL_BAD_RETENTION 3
|
|
|
|
|
+ #endif
|
|
|
|
|
+
|
|
|
|
|
+ // Determines how many frames should pixels deemed as "good" (similar to the current pixel) contribute to the
|
|
|
|
|
+ // current frame.
|
|
|
|
|
+ #ifndef TEMPORAL_GOOD_RETENTION
|
|
|
|
|
+ #define TEMPORAL_GOOD_RETENTION 10
|
|
|
|
|
+ #endif
|
|
|
|
|
|
|
|
////////////////////////// HELPER MACROS /////////////////////////
|
|
////////////////////////// HELPER MACROS /////////////////////////
|
|
|
#if MSAA
|
|
#if MSAA
|
|
@@ -123,27 +150,47 @@ mixin PPTemporalResolve
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
{
|
|
{
|
|
|
- float depth = _SAMPLEOFF(sceneDepth, uv, -r).x;
|
|
|
|
|
|
|
+ float depth = _SAMPLEOFF(sceneDepth, uv, int2(-r, 0)).x;
|
|
|
dmin = depth < dmin.z ? float3(-r, 0, depth) : dmin;
|
|
dmin = depth < dmin.z ? float3(-r, 0, depth) : dmin;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
{
|
|
{
|
|
|
- float depth = _SAMPLEOFF(sceneDepth, uv, -r).x;
|
|
|
|
|
|
|
+ float depth = _SAMPLEOFF(sceneDepth, uv, int2(r, 0)).x;
|
|
|
dmin = depth < dmin.z ? float3(r, 0, depth) : dmin;
|
|
dmin = depth < dmin.z ? float3(r, 0, depth) : dmin;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
{
|
|
{
|
|
|
- float depth = _SAMPLEOFF(sceneDepth, uv, -r).x;
|
|
|
|
|
|
|
+ float depth = _SAMPLEOFF(sceneDepth, uv, int2(0, -r)).x;
|
|
|
dmin = depth < dmin.z ? float3(0, -r, depth) : dmin;
|
|
dmin = depth < dmin.z ? float3(0, -r, depth) : dmin;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
{
|
|
{
|
|
|
- float depth = _SAMPLEOFF(sceneDepth, uv, -r).x;
|
|
|
|
|
|
|
+ float depth = _SAMPLEOFF(sceneDepth, uv, int2(0, r)).x;
|
|
|
dmin = depth < dmin.z ? float3(0, r, depth) : dmin;
|
|
dmin = depth < dmin.z ? float3(0, r, depth) : dmin;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return float3(uv + dmin.xy * _PIXSIZE(sceneDepth), dmin.z);
|
|
return float3(uv + dmin.xy * _PIXSIZE(sceneDepth), dmin.z);
|
|
|
- }
|
|
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ float3 clipAABB(float3 boxMin, float3 boxMax, float3 history, float3 current)
|
|
|
|
|
+ {
|
|
|
|
|
+ // Note: Is this necessary? Will "current" always be in the box?
|
|
|
|
|
+ boxMin = min(current, boxMin);
|
|
|
|
|
+ boxMax = max(current, boxMax);
|
|
|
|
|
+
|
|
|
|
|
+ float3 center = (boxMax + boxMin) * 0.5f;
|
|
|
|
|
+ float3 extents = boxMax - center;
|
|
|
|
|
+
|
|
|
|
|
+ float3 origin = history - center; // Relative to box
|
|
|
|
|
+ float3 dir = current - history;
|
|
|
|
|
+
|
|
|
|
|
+ float3 rDir = rcp(dir);
|
|
|
|
|
+ float3 tNeg = (extents - origin) * rDir;
|
|
|
|
|
+ float3 tPos = (-extents - origin) * rDir;
|
|
|
|
|
+
|
|
|
|
|
+ float t = saturate(max(max(min(tNeg.x, tPos.x), min(tNeg.y, tPos.y)), min(tNeg.z, tPos.z)));
|
|
|
|
|
+ return history + t * dir;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
// Encodes velocity into a format suitable for storing in a 16-bit SNORM texture.
|
|
// Encodes velocity into a format suitable for storing in a 16-bit SNORM texture.
|
|
|
// Velocity range of [-2, 2] is supported (full NDC).
|
|
// Velocity range of [-2, 2] is supported (full NDC).
|
|
@@ -229,31 +276,13 @@ mixin PPTemporalResolve
|
|
|
#define _TONEMAP_COLOR(v) v
|
|
#define _TONEMAP_COLOR(v) v
|
|
|
#endif // TEMPORAL_TONEMAP
|
|
#endif // TEMPORAL_TONEMAP
|
|
|
|
|
|
|
|
- #if TEMPORAL_TONEMAP
|
|
|
|
|
- #if TEMPORAL_YCOCG
|
|
|
|
|
- #define _TONEMAP_COLOR_INV(v) HDRScaleYInv(v, exposureScale)
|
|
|
|
|
- #elif TEMPORAL_GREEN_AS_LUMA
|
|
|
|
|
- #define _TONEMAP_COLOR_INV(v) HDRScaleGInv(v, exposureScale)
|
|
|
|
|
- #else
|
|
|
|
|
- #define _TONEMAP_COLOR_INV(v) HDRScaleRGBInv(v, exposureScale)
|
|
|
|
|
- #endif
|
|
|
|
|
- #else // TEMPORAL_TONEMAP
|
|
|
|
|
- #define _TONEMAP_COLOR_INV(v) v
|
|
|
|
|
- #endif // TEMPORAL_TONEMAP
|
|
|
|
|
-
|
|
|
|
|
// Automatically convert from/to YCoCg space, if enabled
|
|
// Automatically convert from/to YCoCg space, if enabled
|
|
|
#if TEMPORAL_YCOCG
|
|
#if TEMPORAL_YCOCG
|
|
|
#define _SAMPLE_COLOR(n, uv, offset) _TONEMAP_COLOR(RGBToYCoCg(_SAMPLEOFF(n, uv, offset)))
|
|
#define _SAMPLE_COLOR(n, uv, offset) _TONEMAP_COLOR(RGBToYCoCg(_SAMPLEOFF(n, uv, offset)))
|
|
|
#else // TEMPORAL_YCOCG
|
|
#else // TEMPORAL_YCOCG
|
|
|
#define _SAMPLE_COLOR(n, uv, offset) _TONEMAP_COLOR(_SAMPLEOFF(n, uv, offset))
|
|
#define _SAMPLE_COLOR(n, uv, offset) _TONEMAP_COLOR(_SAMPLEOFF(n, uv, offset))
|
|
|
#endif // TEMPORAL_YCOCG
|
|
#endif // TEMPORAL_YCOCG
|
|
|
-
|
|
|
|
|
- #if TEMPORAL_YCOCG
|
|
|
|
|
- #define _RESOLVE_COLOR(v) _TONEMAP_COLOR_INV(YCoCgToRGB(v))
|
|
|
|
|
- #else // TEMPORAL_YCOCG
|
|
|
|
|
- #define _RESOLVE_COLOR(v) _TONEMAP_COLOR_INV(v)
|
|
|
|
|
- #endif // TEMPORAL_YCOCG
|
|
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
///////////////////////////// MAIN /////////////////////////////////
|
|
///////////////////////////// MAIN /////////////////////////////////
|
|
|
[internal]
|
|
[internal]
|
|
|
cbuffer TemporalInput
|
|
cbuffer TemporalInput
|
|
@@ -320,13 +349,14 @@ mixin PPTemporalResolve
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#if MSAA
|
|
#if MSAA
|
|
|
- float prevUV = NDCToScreen(prevNdcPos);
|
|
|
|
|
|
|
+ float2 prevUV = NDCToScreen(prevNdcPos);
|
|
|
#else
|
|
#else
|
|
|
float2 prevUV = NDCToUV(prevNdcPos);
|
|
float2 prevUV = NDCToUV(prevNdcPos);
|
|
|
#endif
|
|
#endif
|
|
|
|
|
|
|
|
/////////////// GET FILTERED COLOR VALUE AND NEIGHBORHOOD MIN/MAX /////////////
|
|
/////////////// GET FILTERED COLOR VALUE AND NEIGHBORHOOD MIN/MAX /////////////
|
|
|
#if TEMPORAL_YCOCG
|
|
#if TEMPORAL_YCOCG
|
|
|
|
|
+ // YCOCG only requires a + pattern for good quality
|
|
|
float3 neighbor[5];
|
|
float3 neighbor[5];
|
|
|
neighbor[0] = _SAMPLE_COLOR(sceneColor, uv, int2(-1, 0));
|
|
neighbor[0] = _SAMPLE_COLOR(sceneColor, uv, int2(-1, 0));
|
|
|
neighbor[1] = _SAMPLE_COLOR(sceneColor, uv, int2( 0, -1));
|
|
neighbor[1] = _SAMPLE_COLOR(sceneColor, uv, int2( 0, -1));
|
|
@@ -374,7 +404,7 @@ mixin PPTemporalResolve
|
|
|
#endif // TEMPORAL_LOWPASS
|
|
#endif // TEMPORAL_LOWPASS
|
|
|
|
|
|
|
|
#if TEMPORAL_SMOOTH_NEIGHBORHOOD
|
|
#if TEMPORAL_SMOOTH_NEIGHBORHOOD
|
|
|
-
|
|
|
|
|
|
|
+ // Calculate standard deviation and determine neighborhood min/max based on it
|
|
|
float3 mean = 0;
|
|
float3 mean = 0;
|
|
|
[unroll]
|
|
[unroll]
|
|
|
for(uint i = 0; i < 9; ++i)
|
|
for(uint i = 0; i < 9; ++i)
|
|
@@ -410,22 +440,71 @@ mixin PPTemporalResolve
|
|
|
/////////////////// GET PREVIOUS FRAME COLOR ///////////////////////
|
|
/////////////////// GET PREVIOUS FRAME COLOR ///////////////////////
|
|
|
float3 prevColorVal = _SAMPLE_COLOR(prevColor, prevUV, int2(0, 0));
|
|
float3 prevColorVal = _SAMPLE_COLOR(prevColor, prevUV, int2(0, 0));
|
|
|
|
|
|
|
|
- // TODO - Do clamp
|
|
|
|
|
- // - Either basic or AABB based
|
|
|
|
|
|
|
+ ///////////////////// CLAMP TO NEIGHBORHOOD ////////////////////////
|
|
|
|
|
+ // Clamping to neighborhood ensures we don't blend with values that are too
|
|
|
|
|
+ // different, which can happen when history data becomes invalid.
|
|
|
|
|
+ #if TEMPORAL_YCOCG
|
|
|
|
|
+ prevColorVal = clamp(prevColorVal, neighborMin, neighborMax);
|
|
|
|
|
+ #else // TEMPORAL_YCOCG
|
|
|
|
|
+ // Uses low-pass to reduce flickering
|
|
|
|
|
+ #if TEMPORAL_CLIP_AABB
|
|
|
|
|
+ prevColorVal = clipAABB(neighborMin, neighborMax, prevColorVal, filteredLow);
|
|
|
|
|
+ #else // TEMPORAL_CLIP_AABB
|
|
|
|
|
+ prevColorVal = clamp(prevColorVal, neighborMin, neighborMax);
|
|
|
|
|
+ #endif // TEMPORAL_CLIP_AABB
|
|
|
|
|
+ #endif // TEMPORAL_YCOCG
|
|
|
|
|
|
|
|
- // TODO - Sharpen
|
|
|
|
|
- // - Needs more research
|
|
|
|
|
|
|
+ //////////////// BLEND BETWEEN CURRENT AND HISTORY //////////////////
|
|
|
|
|
+ // Find out how much impact should the previous frame's color have
|
|
|
|
|
+ #if TEMPORAL_BLEND_FACTOR // Fixed blend factor
|
|
|
|
|
+ float blendAmount = 1.0f / TEMPORAL_BLEND_FACTOR;
|
|
|
|
|
+ float3 output = lerp(prevColorVal, filtered, blendAmount);
|
|
|
|
|
+ #else // TEMPORAL_BLEND_FACTOR
|
|
|
|
|
+ #if TEMPORAL_YCOCG
|
|
|
|
|
+ float lumaCurrent = filtered.r;
|
|
|
|
|
+ float lumaHistory = prevColorVal.r;
|
|
|
|
|
+ #else // TEMPORAL_YCOCG
|
|
|
|
|
+ #if TEMPORAL_GREEN_AS_LUMA
|
|
|
|
|
+ float lumaCurrent = filtered.g;
|
|
|
|
|
+ float lumaHistory = prevColorVal.g;
|
|
|
|
|
+ #else // TEMPORAL_GREEN_AS_LUMA
|
|
|
|
|
+ float lumaCurrent = LuminanceRGB(filtered);
|
|
|
|
|
+ float lumaHistory = LuminanceRGB(prevColorVal);
|
|
|
|
|
+ #endif // TEMPORAL_GREEN_AS_LUMA
|
|
|
|
|
+ #endif // TEMPORAL_YCOCG
|
|
|
|
|
+
|
|
|
|
|
+ // Based on T. Lottes: https://www.youtube.com/watch?v=WzpLWzGvFK4&t=18m
|
|
|
|
|
+ float blendWeight = 1.0f - abs(lumaCurrent - lumaHistory) / max(max(lumaCurrent, lumaHistory), 0.001f);
|
|
|
|
|
+
|
|
|
|
|
+ float weightBad = 1.0f - 1.0f / TEMPORAL_BAD_RETENTION;
|
|
|
|
|
+ float weightGood = 1.0f - 1.0f / TEMPORAL_GOOD_RETENTION;
|
|
|
|
|
+
|
|
|
|
|
+ float blendAmount = lerp(weightBad, weightGood, blendWeight * blendWeight);
|
|
|
|
|
+ float3 output = lerp(filtered, prevColorVal, blendAmount);
|
|
|
|
|
+ #endif // TEMPORAL_BLEND_FACTOR
|
|
|
|
|
|
|
|
- // TODO - Blend
|
|
|
|
|
- // - Determine blend value based on colors, or use a fixed amount
|
|
|
|
|
- // - Reverse YCOCG and tonemap as required
|
|
|
|
|
|
|
+ //////// UNDO TONEMAP & MOVE BACK TO RGB SPACE //////////////////////
|
|
|
|
|
+ #if TEMPORAL_TONEMAP
|
|
|
|
|
+ #if TEMPORAL_YCOCG
|
|
|
|
|
+ output = HDRScaleYInv(output, exposureScale);
|
|
|
|
|
+ #elif TEMPORAL_GREEN_AS_LUMA
|
|
|
|
|
+ output = HDRScaleGInv(output, exposureScale);
|
|
|
|
|
+ #else
|
|
|
|
|
+ output = HDRScaleRGBInv(output, exposureScale);
|
|
|
|
|
+ #endif
|
|
|
|
|
+ #endif // TEMPORAL_TONEMAP
|
|
|
|
|
+
|
|
|
|
|
+ #if TEMPORAL_YCOCG
|
|
|
|
|
+ output = YCoCgToRGB(output);
|
|
|
|
|
+ #endif // TEMPORAL_YCOCG
|
|
|
|
|
|
|
|
- // TODO - Limbo implementation does unjitter operation in specific places, do I need to as well?
|
|
|
|
|
- // - This seems to be applied during sample generation
|
|
|
|
|
|
|
+ // Note: Potential improvements:
|
|
|
|
|
+ // - Add a sharpen step
|
|
|
|
|
+ // - Use filtering when sampling history
|
|
|
|
|
+ // - Properly handle borders when sampling neighbors
|
|
|
|
|
+ // - Better blend amount calculation? (Needs experimentation)
|
|
|
|
|
|
|
|
- // TODO - Not doing (investigate if needed):
|
|
|
|
|
- // - Sharpen
|
|
|
|
|
- // - AA border clamp
|
|
|
|
|
|
|
+ return output;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#undef _TEX2D
|
|
#undef _TEX2D
|