Files
FlaxEngine/Source/Shaders/TAA.shader

109 lines
3.7 KiB
GLSL

// 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;
float2 JitterInv;
float Sharpness;
float StationaryBlending;
float MotionBlending;
float Dummy0;
GBufferData GBuffer;
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
{
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 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(Input, sampleUV);
neighborhoodMin = min(neighborhoodMin, neighbor);
neighborhoodMax = max(neighborhoodMax, neighbor);
neighborhoodSum += neighbor;
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)
{
current = neighbor;
currentDepth = neighborDepth;
}
}
}
// Apply sharpening
float4 neighborhoodAvg = neighborhoodSum / 9.0;
current += (current - neighborhoodAvg) * Sharpness;
// Sample history by clamp it to the nearby colors range to reduce artifacts
float4 history = SAMPLE_RT_LINEAR(InputHistory, prevUV);
float lumaOffset = abs(Luminance(neighborhoodAvg.rgb) - Luminance(current.rgb));
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 * 1000.0f);
float blendfactor = lerp(StationaryBlending, MotionBlending, motion);
// Perform linear accumulation of the previous samples with a current one
float4 color = lerp(current, history, blendfactor);
// 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;
}