Fix Temporal Anti Aliasing ghosting and improve quality of blending

#1786
This commit is contained in:
Wojtek Figat
2024-02-28 02:05:06 +01:00
parent 0a0bb997e4
commit 901b043909
5 changed files with 45 additions and 20 deletions

BIN
Content/Shaders/TAA.flax (Stored with Git LFS)

Binary file not shown.

View File

@@ -1889,13 +1889,13 @@ API_STRUCT() struct FLAXENGINE_API AntiAliasingSettings : ISerializable
/// The diameter (in texels) inside which jitter samples are spread. Smaller values result in crisper but more aliased output, while larger values result in more stable but blurrier output.
/// </summary>
API_FIELD(Attributes="Limit(0.1f, 1f, 0.001f), EditorOrder(1), PostProcessSetting((int)AntiAliasingSettingsOverride.TAA_JitterSpread), EditorDisplay(null, \"TAA Jitter Spread\")")
float TAA_JitterSpread = 0.75f;
float TAA_JitterSpread = 1.0f;
/// <summary>
/// Controls the amount of sharpening applied to the color buffer. TAA can induce a slight loss of details in high frequency regions. Sharpening alleviates this issue. High values may introduce dark-border artifacts.
/// </summary>
API_FIELD(Attributes="Limit(0, 3f, 0.001f), EditorOrder(2), PostProcessSetting((int)AntiAliasingSettingsOverride.TAA_Sharpness), EditorDisplay(null, \"TAA Sharpness\")")
float TAA_Sharpness = 0.0f;
float TAA_Sharpness = 0.1f;
/// <summary>
/// The blend coefficient for stationary fragments. Controls the percentage of history samples blended into the final color for fragments with minimal active motion.
@@ -1907,7 +1907,7 @@ API_STRUCT() struct FLAXENGINE_API AntiAliasingSettings : ISerializable
/// The blending coefficient for moving fragments. Controls the percentage of history samples blended into the final color for fragments with significant active motion.
/// </summary>
API_FIELD(Attributes="Limit(0, 0.99f, 0.001f), EditorOrder(4), PostProcessSetting((int)AntiAliasingSettingsOverride.TAA_MotionBlending), EditorDisplay(null, \"TAA Motion Blending\")")
float TAA_MotionBlending = 0.7f;
float TAA_MotionBlending = 0.85f;
public:
/// <summary>

View File

@@ -26,7 +26,7 @@ void RenderView::Prepare(RenderContext& renderContext)
TaaFrameIndex = 0;
// Calculate jitter
const float jitterSpread = renderContext.List->Settings.AntiAliasing.TAA_JitterSpread / 0.75f;
const float jitterSpread = renderContext.List->Settings.AntiAliasing.TAA_JitterSpread;
const float jitterX = (RendererUtils::TemporalHalton(TaaFrameIndex + 1, 2) - 0.5f) * jitterSpread;
const float jitterY = (RendererUtils::TemporalHalton(TaaFrameIndex + 1, 3) - 0.5f) * jitterSpread;
taaJitter = Float2(jitterX * 2.0f / width, jitterY * 2.0f / height);

View File

@@ -8,6 +8,7 @@
#include "Engine/Graphics/RenderBuffers.h"
#include "Engine/Graphics/RenderTask.h"
#include "Engine/Renderer/RenderList.h"
#include "Engine/Renderer/GBufferPass.h"
#include "Engine/Engine/Engine.h"
PACK_STRUCT(struct Data
@@ -18,6 +19,7 @@ PACK_STRUCT(struct Data
float StationaryBlending;
float MotionBlending;
float Dummy0;
GBufferData GBuffer;
});
bool TAA::Init()
@@ -125,6 +127,7 @@ void TAA::Render(const RenderContext& renderContext, GPUTexture* input, GPUTextu
data.Sharpness = settings.TAA_Sharpness;
data.StationaryBlending = settings.TAA_StationaryBlending * blendStrength;
data.MotionBlending = settings.TAA_MotionBlending * blendStrength;
GBufferPass::SetInputs(renderContext.View, data.GBuffer);
const auto cb = _shader->GetShader()->GetCB(0);
context->UpdateCB(cb, &data);
context->BindCB(0, cb);

View File

@@ -1,6 +1,10 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
#define DEBUG_HISTORY_REJECTION 0
#define NO_GBUFFER_SAMPLING
#include "./Flax/Common.hlsl"
#include "./Flax/GBuffer.hlsl"
META_CB_BEGIN(0, Data)
float2 ScreenSizeInv;
@@ -9,6 +13,7 @@ float Sharpness;
float StationaryBlending;
float MotionBlending;
float Dummy0;
GBufferData GBuffer;
META_CB_END
Texture2D Input : register(t0);
@@ -31,37 +36,42 @@ float4 ClipToAABB(float4 color, float4 minimum, float4 maximum)
META_PS(true, FEATURE_LEVEL_ES2)
float4 PS(Quad_VS2PS input) : SV_Target0
{
float2 tanHalfFOV = float2(GBuffer.InvProjectionMatrix[0][0], GBuffer.InvProjectionMatrix[1][1]);
// Calculate previous frame UVs based on per-pixel velocity
float2 velocity = SAMPLE_RT_LINEAR(MotionVectors, input.TexCoord).xy;
float velocityLength = length(velocity);
float2 prevUV = input.TexCoord - velocity;
float prevDepth = LinearizeZ(GBuffer, SAMPLE_RT(Depth, prevUV).r);
// Find the closest pixel in 3x3 neighborhood
float bestDepth = 1;
float2 bestUV = float2(0, 0);
float currentDepth = 1;
float4 neighborhoodMin = 100000;
float4 neighborhoodMax = -10000;
float4 current;
float4 neighborhoodSum = 0;
float minDepthDiff = 100000;
for (int x = -1; x <= 1; ++x)
{
for (int y = -1; y <= 1; ++y)
{
float2 sampleUV = input.TexCoord + float2(x, y) * ScreenSizeInv;
float4 neighbor = SAMPLE_RT_LINEAR(Input, sampleUV);
float4 neighbor = SAMPLE_RT(Input, sampleUV);
neighborhoodMin = min(neighborhoodMin, neighbor);
neighborhoodMax = max(neighborhoodMax, neighbor);
if (x == 0 && y == 0)
current = neighbor;
neighborhoodSum += neighbor;
float depth = SAMPLE_RT(Depth, sampleUV).r;
if (depth < bestDepth)
float neighborDepth = LinearizeZ(GBuffer, SAMPLE_RT(Depth, sampleUV).r);
float depthDiff = abs(max(neighborDepth - prevDepth, 0));
minDepthDiff = min(minDepthDiff, depthDiff);
if (x == 0 && y == 0)
{
bestDepth = depth;
bestUV = sampleUV;
current = neighbor;
currentDepth = neighborDepth;
}
}
}
float2 velocity = SAMPLE_RT_LINEAR(MotionVectors, bestUV).xy;
float velocityLength = length(velocity);
float2 prevUV = input.TexCoord - velocity;
// Apply sharpening
float4 neighborhoodAvg = neighborhoodSum / 9.0;
@@ -76,11 +86,23 @@ float4 PS(Quad_VS2PS input) : SV_Target0
// Calculate history blending factor
float motion = saturate(velocityLength * 1000.0f);
float blendfactor = any(abs(prevUV * 2 - 1) >= 1.0f) ? 0.0f : lerp(StationaryBlending, MotionBlending, motion);
float blendfactor = lerp(StationaryBlending, MotionBlending, motion);
// Perform linear accumulation of the previous samples with a current one
float4 color = lerp(current, history, blendfactor);
color = clamp(color, 0, HDR_CLAMP_MAX);
// Reduce history blend in favor of neighborhood blend when sample has no valid prevous frame data
float miss = any(abs(prevUV * 2 - 1) >= 1.0f) ? 1 : 0;
float currentDepthWorld = currentDepth * GBuffer.ViewFar;
float minDepthDiffWorld = minDepthDiff * GBuffer.ViewFar;
float depthError = tanHalfFOV.x * ScreenSizeInv.x * 200.0f * (currentDepthWorld + 10.0f);
miss += minDepthDiffWorld > depthError ? 1 : 0;
float4 neighborhoodSharp = lerp(neighborhoodAvg, current, 0.5f);
#if DEBUG_HISTORY_REJECTION
neighborhoodSharp = float4(1, 0, 0, 1);
#endif
color = lerp(color, neighborhoodSharp, saturate(miss));
color = clamp(color, 0, HDR_CLAMP_MAX);
return color;
}