diff --git a/Content/Shaders/TAA.flax b/Content/Shaders/TAA.flax
index f14be47f6..0a359eca5 100644
--- a/Content/Shaders/TAA.flax
+++ b/Content/Shaders/TAA.flax
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:18053d25260b9636af403508225c1a7934ddf92a60c52c426b70c057ac53e559
-size 712
+oid sha256:5ac1ebf8bf16576e25b2dfd8e063c788f4b6a160786a97038405c6b01bd001a2
+size 3371
diff --git a/Source/Engine/Graphics/RenderView.cpp b/Source/Engine/Graphics/RenderView.cpp
index b270ba69a..c9da7a1b4 100644
--- a/Source/Engine/Graphics/RenderView.cpp
+++ b/Source/Engine/Graphics/RenderView.cpp
@@ -31,8 +31,15 @@ void RenderView::Prepare(RenderContext& renderContext)
taaJitter = Vector2(jitterX * 2.0f / width, jitterY * 2.0f / height);
// Modify projection matrix
- Projection.Values[2][0] += taaJitter.X;
- Projection.Values[2][1] += taaJitter.Y;
+ if (IsOrthographicProjection())
+ {
+ // TODO: jitter otho matrix in a proper way
+ }
+ else
+ {
+ Projection.Values[2][0] += taaJitter.X;
+ Projection.Values[2][1] += taaJitter.Y;
+ }
// Update matrices
Matrix::Invert(Projection, IP);
diff --git a/Source/Engine/Graphics/RenderView.h b/Source/Engine/Graphics/RenderView.h
index 22acbfb52..88cbe07bd 100644
--- a/Source/Engine/Graphics/RenderView.h
+++ b/Source/Engine/Graphics/RenderView.h
@@ -202,7 +202,6 @@ public:
///
/// Determines whether view is perspective projection or orthographic.
///
- /// True if view is perspective, otherwise false if view is orthographic.
FORCE_INLINE bool IsPerspectiveProjection() const
{
return Projection.M44 < 1.0f;
@@ -211,7 +210,6 @@ public:
///
/// Determines whether view is orthographic projection or perspective.
///
- /// True if view is orthographic, otherwise false if view is perspective.
FORCE_INLINE bool IsOrthographicProjection() const
{
return Projection.M44 >= 1.0f;
diff --git a/Source/Engine/Renderer/AntiAliasing/TAA.cpp b/Source/Engine/Renderer/AntiAliasing/TAA.cpp
index e61132b42..c3121d5be 100644
--- a/Source/Engine/Renderer/AntiAliasing/TAA.cpp
+++ b/Source/Engine/Renderer/AntiAliasing/TAA.cpp
@@ -11,31 +11,47 @@
#include "Engine/Renderer/RenderList.h"
#include "Engine/Engine/Engine.h"
+PACK_STRUCT(struct Data
+ {
+ Vector2 ScreenSizeInv;
+ Vector2 JitterInv;
+ float Sharpness;
+ float StationaryBlending;
+ float MotionBlending;
+ float Dummy0;
+ });
+
bool TAA::Init()
{
- // Create pipeline state
- //_psTAA.CreatePipelineStates();
-
- // Load shader
_shader = Content::LoadAsyncInternal(TEXT("Shaders/TAA"));
if (_shader == nullptr)
return true;
#if COMPILE_WITH_DEV_ENV
_shader.Get()->OnReloading.Bind(this);
#endif
-
return false;
}
bool TAA::setupResources()
{
- // Check shader
if (!_shader->IsLoaded())
+ return true;
+ const auto shader = _shader->GetShader();
+ if (shader->GetCB(0)->GetSize() != sizeof(Data))
{
+ REPORT_INVALID_SHADER_PASS_CB_SIZE(shader, 0, Data);
return true;
}
- const auto shader = _shader->GetShader();
-
+ if (!_psTAA)
+ _psTAA = GPUDevice::Instance->CreatePipelineState();
+ GPUPipelineState::Description psDesc;
+ if (!_psTAA->IsValid())
+ {
+ psDesc = GPUPipelineState::Description::DefaultFullscreenTriangle;
+ psDesc.PS = shader->GetPS("PS");
+ if (_psTAA->Init(psDesc))
+ return true;
+ }
return false;
}
@@ -44,8 +60,7 @@ void TAA::Dispose()
// Base
RendererPass::Dispose();
- // Cleanup
- _psTAA = nullptr;
+ SAFE_DELETE_GPU_RESOURCE(_psTAA);
_shader = nullptr;
}
@@ -59,7 +74,7 @@ void TAA::Render(RenderContext& renderContext, GPUTexture* input, GPUTextureView
auto context = GPUDevice::Instance->GetMainContext();
// Ensure to have valid data
- //if (checkIfSkipPass())
+ if (checkIfSkipPass())
{
// Resources are missing. Do not perform rendering, just copy source frame.
context->SetRenderTarget(output);
@@ -73,7 +88,7 @@ void TAA::Render(RenderContext& renderContext, GPUTexture* input, GPUTextureView
// Get history buffers
bool resetHistory = renderContext.Task->IsCameraCut;
renderContext.Buffers->LastFrameTemporalAA = Engine::FrameCount;
- const auto tempDesc = GPUTextureDescription::New2D((int32)renderContext.View.ScreenSize.X, (int32)renderContext.View.ScreenSize.Y, input->Format());
+ const auto tempDesc = GPUTextureDescription::New2D(input->Width(), input->Height(), input->Format());
if (renderContext.Buffers->TemporalAA == nullptr)
{
// Missing temporal buffer
@@ -94,14 +109,44 @@ void TAA::Render(RenderContext& renderContext, GPUTexture* input, GPUTextureView
float blendStrength = 1.0f;
if (resetHistory)
{
- PROFILE_GPU_CPU("Reset History");
-
+#if 0
+ context->CopyTexture(inputHistory, 0, 0, 0, 0, input, 0);
+#else
context->SetRenderTarget(inputHistory->View());
context->Draw(input);
context->ResetRenderTarget();
-
+#endif
blendStrength = 0.0f;
}
- // ...
+ // Bind input
+ Data data;
+ data.ScreenSizeInv.X = renderContext.View.ScreenSize.Z;
+ data.ScreenSizeInv.Y = renderContext.View.ScreenSize.W;
+ data.JitterInv.X = renderContext.View.TemporalAAJitter.X / (float)tempDesc.Width;
+ data.JitterInv.Y = renderContext.View.TemporalAAJitter.Y / (float)tempDesc.Height;
+ data.Sharpness = settings.TAA_Sharpness;
+ data.StationaryBlending = settings.TAA_StationaryBlending * blendStrength;
+ data.MotionBlending = settings.TAA_MotionBlending * blendStrength;
+ const auto cb = _shader->GetShader()->GetCB(0);
+ context->UpdateCB(cb, &data);
+ context->BindCB(0, cb);
+ context->BindSR(0, input);
+ context->BindSR(1, inputHistory);
+ context->BindSR(2, renderContext.Buffers->MotionVectors);
+ context->BindSR(3, renderContext.Buffers->DepthBuffer);
+
+ // Render
+ context->SetRenderTarget(output);
+ context->SetState(_psTAA);
+ context->DrawFullscreenTriangle();
+
+ // Update the history
+ {
+ RenderTargetPool::Release(inputHistory);
+ context->ResetRenderTarget();
+ context->SetRenderTarget(outputHistory->View());
+ context->Draw(output);
+ renderContext.Buffers->TemporalAA = outputHistory;
+ }
}
diff --git a/Source/Engine/Renderer/AntiAliasing/TAA.h b/Source/Engine/Renderer/AntiAliasing/TAA.h
index 904153f7c..c4022a147 100644
--- a/Source/Engine/Renderer/AntiAliasing/TAA.h
+++ b/Source/Engine/Renderer/AntiAliasing/TAA.h
@@ -37,7 +37,7 @@ private:
#if COMPILE_WITH_DEV_ENV
void OnShaderReloading(Asset* obj)
{
- _psTAA = nullptr;
+ _psTAA->ReleaseGPU();
invalidateResources();
}
#endif
@@ -49,7 +49,6 @@ public:
{
return TEXT("TAA");
}
-
bool Init() override;
void Dispose() override;
diff --git a/Source/Engine/Renderer/Renderer.cpp b/Source/Engine/Renderer/Renderer.cpp
index f2abe867a..8c82f9792 100644
--- a/Source/Engine/Renderer/Renderer.cpp
+++ b/Source/Engine/Renderer/Renderer.cpp
@@ -294,13 +294,8 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext)
task->CollectPostFxVolumes(renderContext);
renderContext.List->BlendSettings();
auto aaMode = (renderContext.View.Flags & ViewFlags::AntiAliasing) != 0 ? renderContext.List->Settings.AntiAliasing.Mode : AntialiasingMode::None;
- if (view.IsOrthographicProjection() && aaMode == AntialiasingMode::TemporalAntialiasing)
- aaMode = AntialiasingMode::None; // TODO: support TAA in ortho projection
-#if USE_EDITOR
- // Disable temporal AA effect in editor without play mode enabled to hide minor artifacts on objects moving and lack of valid motion vectors
- if (!Editor::IsPlayMode && aaMode == AntialiasingMode::TemporalAntialiasing)
- aaMode = AntialiasingMode::FastApproximateAntialiasing;
-#endif
+ if (aaMode == AntialiasingMode::TemporalAntialiasing && view.IsOrthographicProjection())
+ aaMode = AntialiasingMode::None; // TODO: support TAA in ortho projection (see RenderView::Prepare to jitter projection matrix better)
renderContext.List->Settings.AntiAliasing.Mode = aaMode;
// Prepare
diff --git a/Source/Shaders/TAA.shader b/Source/Shaders/TAA.shader
index de78707ac..509ca5d76 100644
--- a/Source/Shaders/TAA.shader
+++ b/Source/Shaders/TAA.shader
@@ -2,9 +2,86 @@
#include "./Flax/Common.hlsl"
+META_CB_BEGIN(0, Data)
+float2 ScreenSizeInv;
+float2 JitterInv;
+float Sharpness;
+float StationaryBlending;
+float MotionBlending;
+float Dummy0;
+META_CB_END
+
+Texture2D Input : register(t0);
+Texture2D InputHistory : register(t1);
+Texture2D MotionVectors : register(t2);
+Texture2D Depth : register(t3);
+
+// [Pedersen, 2016, "Temporal Reprojection Anti-Aliasing in INSIDE"]
+float4 ClipToAABB(float4 color, float4 minimum, float4 maximum)
+{
+ float4 center = (maximum + minimum) * 0.5;
+ float4 extents = (maximum - minimum) * 0.5;
+ float4 shift = color - center;
+ float4 absUnit = abs(shift / max(extents, 0.0001));
+ float maxUnit = max(max(absUnit.x, absUnit.y), absUnit.z);
+ return maxUnit > 1.0 ? center + (shift / maxUnit) : color;
+}
+
// Pixel Shader for Temporal Anti-Aliasing
META_PS(true, FEATURE_LEVEL_ES2)
float4 PS(Quad_VS2PS input) : SV_Target0
{
- return float4(0, 0, 0, 0);
+ // Find the closest pixel in 3x3 neighborhood
+ float bestDepth = 1;
+ float2 bestUV = float2(0, 0);
+ float4 neighborhoodMin = 100000;
+ float4 neighborhoodMax = -10000;
+ float4 current;
+ float4 neighborhoodSum = 0;
+ 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);
+ 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)
+ {
+ bestDepth = depth;
+ bestUV = sampleUV;
+ }
+ }
+ }
+ float2 velocity = SAMPLE_RT_LINEAR(MotionVectors, bestUV).xy;
+ float velocityLength = length(velocity);
+ float2 prevUV = input.TexCoord + velocity;
+
+ // Apply sharpening
+ float4 neighborhoodAvg = neighborhoodSum / 9.0;
+ current += (current - neighborhoodAvg) * Sharpness;
+
+ // Sample history by clamp it to the nearby colros range to reduce artifacts
+ float4 history = SAMPLE_RT_LINEAR(InputHistory, prevUV);
+ float lumaOffset = abs(Luminance(neighborhoodAvg) - Luminance(current));
+ float aabbMargin = lerp(4.0, 0.25, saturate(velocityLength * 100.0)) * lumaOffset;
+ history = ClipToAABB(history, neighborhoodMin - aabbMargin, neighborhoodMax + aabbMargin);
+ //history = clamp(history, neighborhoodMin, neighborhoodMax);
+
+ // Calculate history blending factor
+ float motion = saturate(velocityLength * 600.0f);
+ float blendfactor = lerp(StationaryBlending, MotionBlending, motion);
+ blendfactor = any(abs(prevUV * 2 - 1) >= 1.0f) ? 0.0f : blendfactor;
+
+ // Perform linear accumulation of the previous samples with a current one
+ float4 color = lerp(current, history, blendfactor);
+ color = clamp(color, 0, HDR_CLAMP_MAX);
+
+ return color;
}