You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
1011 lines
40 KiB
Plaintext
1011 lines
40 KiB
Plaintext
// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
|
|
|
|
/*=============================================================================
|
|
PostProcessTemporalCommon.usf: Common Temporal AA Functionality
|
|
-------------------------------------------------------------------------------
|
|
This is the common bits of the temporal AA shader
|
|
which can be configured via defines for various temporal AA passes.
|
|
=============================================================================*/
|
|
|
|
// 1 = Use tighter AABB clamp for history.
|
|
// 0 = Use simple min/max clamp.
|
|
#ifndef AA_AABB
|
|
#define AA_AABB 1
|
|
#endif
|
|
|
|
// 0 = Anti-alias the alpha channel also (not getting used currently).
|
|
// 1 = Use alpha channel to improve quality (required for primary AA).
|
|
// Leverages dead code removal to work in RGB instead of RGBA.
|
|
#ifndef AA_ALPHA
|
|
#define AA_ALPHA 1
|
|
#endif
|
|
|
|
// Cross distance in pixels used in depth search X pattern.
|
|
// 0 = Turn this feature off.
|
|
// 2 = Is required for standard temporal AA pass.
|
|
#ifndef AA_CROSS
|
|
#define AA_CROSS 2
|
|
#endif
|
|
|
|
// 1 = Render in blue, with green = diff between frames, red = alpha channel.
|
|
// 0 = Non-debug.
|
|
#ifndef AA_DEBUG
|
|
#define AA_DEBUG 0
|
|
#endif
|
|
|
|
// 2 = Dilate in cross pattern by 2 pixels in distance (this can be larger than 2 if required).
|
|
// 1 = Dilate history alpha using maximum of neighborhood.
|
|
// This increases thin edge quality in motion.
|
|
// This is only valid for AA_ALPHA == 1
|
|
// 0 = Turn off.
|
|
#ifndef AA_DILATE
|
|
#define AA_DILATE AA_ALPHA
|
|
#endif
|
|
|
|
// 1 = Use dynamic motion.
|
|
// 0 = Skip dynamic motion, currently required for half resolution passes.
|
|
#ifndef AA_DYNAMIC
|
|
#define AA_DYNAMIC 1
|
|
#endif
|
|
|
|
// 1 = Use filtered sample.
|
|
// 0 = Use center sample.
|
|
#ifndef AA_FILTERED
|
|
#define AA_FILTERED 1
|
|
#endif
|
|
|
|
// 1 = Improve quality on converged edges.
|
|
// 0 = Default.
|
|
#ifndef AA_GRAIN
|
|
#define AA_GRAIN 0
|
|
#endif
|
|
|
|
// 1 = Use higher quality HDR weighted filtering.
|
|
// 0 = Don't use.
|
|
#ifndef AA_HDR
|
|
#define AA_HDR 1
|
|
#endif
|
|
|
|
// 1 = Use manual HDR weighted bilinear filtering for history.
|
|
#if AA_HDR_HISTORY
|
|
#define AA_HDR_HISTORY 1
|
|
#endif
|
|
|
|
// 0 = Dynamic motion based lerp value (default).
|
|
// non-zero = Use 1/LERP fixed lerp value (used for reflections).
|
|
#ifndef AA_LERP
|
|
#define AA_LERP 0
|
|
#endif
|
|
|
|
// 1 = Use extra lowpass filter for quality bump.
|
|
// 0 = Don't use.
|
|
#ifndef AA_LOWPASS
|
|
#define AA_LOWPASS 1
|
|
#endif
|
|
|
|
// 1 = Use higher quality round clamp.
|
|
// 0 = Use lower quality but faster box clamp.
|
|
#ifndef AA_ROUND
|
|
#define AA_ROUND 1
|
|
#endif
|
|
|
|
// 1 = Use extra clamp to avoid NANs
|
|
// 0 = Don't use.
|
|
#ifndef AA_NAN
|
|
#define AA_NAN 1
|
|
#endif
|
|
|
|
// Fix for lack of borders during current frame filter.
|
|
#ifndef AA_BORDER
|
|
#define AA_BORDER 0
|
|
#endif
|
|
|
|
// Force clamp on alpha.
|
|
#ifndef AA_FORCE_ALPHA_CLAMP
|
|
#define AA_FORCE_ALPHA_CLAMP 0
|
|
#endif
|
|
|
|
// Optimized path to avoid a second dynamic motion sample.
|
|
#ifndef AA_ONE_DYNAMIC_SAMPLE
|
|
#define AA_ONE_DYNAMIC_SAMPLE 0
|
|
#endif
|
|
|
|
// Use green as luma.
|
|
#ifndef AA_GREEN_AS_LUMA
|
|
#define AA_GREEN_AS_LUMA 0
|
|
#endif
|
|
|
|
// Try the new AA algorithm.
|
|
#ifndef AA_NEW
|
|
#define AA_NEW 0
|
|
#endif
|
|
|
|
|
|
|
|
#if AA_NEW
|
|
|
|
// FIND MOTION OF PIXEL AND NEAREST IN NEIGHBORHOOD
|
|
// ------------------------------------------------
|
|
float3 PosP; // Position of this pixel.
|
|
PosP.xy = UVAndScreenPos.zw * float2(0.5, -0.5) + 0.5; // View position [0 to 1] flipped in Y.
|
|
PosP.z = SceneDepthTexture.SampleLevel(SceneDepthTextureSampler, UVAndScreenPos.xy, 0).r;
|
|
float3 PosN; // Position of closest pixel in neighborhood.
|
|
PosN = PosP;
|
|
// Screen position of minimum depth.
|
|
float2 VelocityOffset = float2(0.0, 0.0);
|
|
#if AA_CROSS
|
|
// For motion vector, use camera/dynamic motion from min depth pixel in pattern around pixel.
|
|
// This enables better quality outline on foreground against different motion background.
|
|
// Larger 2 pixel distance "x" works best (because AA diolates surface).
|
|
float4 Depths;
|
|
#if FEATURE_LEVEL >= FEATURE_LEVEL_SM5
|
|
Depths = SceneDepthTexture.GatherRed(SceneDepthTextureSampler, UVAndScreenPos.xy, int2(-AA_CROSS, -AA_CROSS), int2(AA_CROSS, -AA_CROSS), int2(-AA_CROSS, AA_CROSS), int2(AA_CROSS, AA_CROSS));
|
|
#else
|
|
Depths.x = SceneDepthTexture.SampleLevel(SceneDepthTextureSampler, UVAndScreenPos.xy, 0, int2(-AA_CROSS, -AA_CROSS)).r;
|
|
Depths.y = SceneDepthTexture.SampleLevel(SceneDepthTextureSampler, UVAndScreenPos.xy, 0, int2( AA_CROSS, -AA_CROSS)).r;
|
|
Depths.z = SceneDepthTexture.SampleLevel(SceneDepthTextureSampler, UVAndScreenPos.xy, 0, int2(-AA_CROSS, AA_CROSS)).r;
|
|
Depths.w = SceneDepthTexture.SampleLevel(SceneDepthTextureSampler, UVAndScreenPos.xy, 0, int2( AA_CROSS, AA_CROSS)).r;
|
|
#endif
|
|
float2 DepthOffset = float2(AA_CROSS, AA_CROSS);
|
|
float DepthOffsetXx = float(AA_CROSS);
|
|
// Nearest depth is the largest depth (depth surface 0=far, 1=near).
|
|
if(Depths.x > Depths.y)
|
|
{
|
|
DepthOffsetXx = -AA_CROSS;
|
|
}
|
|
if(Depths.z > Depths.w)
|
|
{
|
|
DepthOffset.x = -AA_CROSS;
|
|
}
|
|
float DepthsXY = max(Depths.x, Depths.y);
|
|
float DepthsZW = max(Depths.z, Depths.w);
|
|
if(DepthsXY > DepthsZW)
|
|
{
|
|
DepthOffset.y = -AA_CROSS;
|
|
DepthOffset.x = DepthOffsetXx;
|
|
}
|
|
float DepthsXYZW = max(DepthsXY, DepthsZW);
|
|
if(DepthsXYZW > PosP.z)
|
|
{
|
|
// This is offset for reading from velocity texture.
|
|
// This supports half or fractional resolution velocity textures.
|
|
// With the assumption that UV position scales between velocity and color.
|
|
VelocityOffset = DepthOffset * PostprocessInput0Size.zw;
|
|
// This is [0 to 1] flipped in Y.
|
|
PosN.xy = (UVAndScreenPos.zw + DepthOffset * ViewportSize.zw * 2.0) * float2(0.5, -0.5) + 0.5;
|
|
PosN.z = DepthsXYZW;
|
|
}
|
|
#endif
|
|
// Camera motion for pixel (in ScreenPos space).
|
|
float ScaleM = 1.0 / (dot(PosP, CameraMotion[0].xyz) + CameraMotion[0].w);
|
|
float2 BackP;
|
|
BackP.x = -2.0 * ((PosP.x * ((CameraMotion[1].x * PosP.y) + (CameraMotion[1].y * PosP.z) + CameraMotion[1].z)) + (CameraMotion[1].w * PosP.y) + (CameraMotion[2].x * PosP.x * PosP.x) + (CameraMotion[2].y * PosP.z) + CameraMotion[2].z) * ScaleM;
|
|
BackP.y = 2.0 * ((PosP.y * ((CameraMotion[3].x * PosP.x) + (CameraMotion[3].y * PosP.z) + CameraMotion[3].z)) + (CameraMotion[3].w * PosP.x) + (CameraMotion[4].x * PosP.y * PosP.y) + (CameraMotion[4].y * PosP.z) + CameraMotion[4].z) * ScaleM;
|
|
// Convert BackP back into pixels.
|
|
float2 BackTemp = BackP * ViewportSize.xy;
|
|
// Save the amount of pixel offset of just camera motion, used later as the amount of blur introduced by history.
|
|
float HistoryBlurAmp = 2.0;
|
|
float HistoryBlur = saturate(abs(BackTemp.x) * HistoryBlurAmp + abs(BackTemp.y) * HistoryBlurAmp);
|
|
#if AA_CROSS
|
|
// Camera motion for neighborhood.
|
|
ScaleM = 1.0 / (dot(PosN, CameraMotion[0].xyz) + CameraMotion[0].w);
|
|
float2 BackN;
|
|
BackN.x = -2.0 * ((PosN.x * ((CameraMotion[1].x * PosN.y) + (CameraMotion[1].y * PosN.z) + CameraMotion[1].z)) + (CameraMotion[1].w * PosN.y) + (CameraMotion[2].x * PosN.x * PosN.x) + (CameraMotion[2].y * PosN.z) + CameraMotion[2].z) * ScaleM;
|
|
BackN.y = 2.0 * ((PosN.y * ((CameraMotion[3].x * PosN.x) + (CameraMotion[3].y * PosN.z) + CameraMotion[3].z)) + (CameraMotion[3].w * PosN.x) + (CameraMotion[4].x * PosN.y * PosN.y) + (CameraMotion[4].y * PosN.z) + CameraMotion[4].z) * ScaleM;
|
|
BackTemp = BackN * ViewportSize.xy;
|
|
#endif
|
|
#if AA_DYNAMIC
|
|
// Dynamic motion of pixel.
|
|
#if (AA_CROSS == 0) || (AA_ONE_DYNAMIC_SAMPLE == 0)
|
|
float2 VelocityP = PostprocessInput3.SampleLevel(PostprocessInput3Sampler, UVAndScreenPos.xy, 0).xy;
|
|
bool DynamicP = VelocityP.x > 0.0;
|
|
if(DynamicP)
|
|
{
|
|
BackP = DecodeVelocityFromTexture(VelocityP);
|
|
}
|
|
#endif
|
|
BackTemp = BackP * ViewportSize.xy;
|
|
// At this point DoublePixelMotionDynamic is kept squared until later.
|
|
float DoublePixelMotionDynamic = dot(BackTemp, BackTemp);
|
|
#if AA_CROSS
|
|
// Dynamic motion of nearest in neighborhood.
|
|
float2 VelocityN = PostprocessInput3.SampleLevel(PostprocessInput3Sampler, UVAndScreenPos.xy + VelocityOffset, 0).xy;
|
|
bool DynamicN = VelocityN.x > 0.0;
|
|
if(DynamicN)
|
|
{
|
|
BackN = DecodeVelocityFromTexture(VelocityN);
|
|
}
|
|
#if (AA_ONE_DYNAMIC_SAMPLE == 0)
|
|
bool DynamicMotion = DynamicN || DynamicP;
|
|
#else
|
|
bool DynamicMotion = DynamicN;
|
|
#endif
|
|
BackTemp = BackN * ViewportSize.xy;
|
|
DoublePixelMotionDynamic = max(DoublePixelMotionDynamic, dot(BackTemp, BackTemp));
|
|
#else
|
|
bool DynamicMotion = DynamicP;
|
|
#endif
|
|
DoublePixelMotionDynamic = sqrt(DoublePixelMotionDynamic);
|
|
#else
|
|
bool DynamicMotion = false;
|
|
float DoublePixelMotionDynamic = 0.0;
|
|
#endif
|
|
#if (AA_CROSS == 0)
|
|
float2 BackN = BackP;
|
|
#endif
|
|
// Easier to do off screen check before conversion.
|
|
// BackN is in units of 2pixels/viewportWidthInPixels
|
|
// This converts back projection vector to [-1 to 1] offset in viewport.
|
|
BackN = UVAndScreenPos.zw - BackN;
|
|
bool OffScreen = max(abs(BackN.x), abs(BackN.y)) >= 1.0;
|
|
// Also clamp to be on screen (fixes problem with DOF).
|
|
// The .z and .w is the 1/width and 1/height.
|
|
// This clamps to be a pixel inside the viewport.
|
|
BackN.x = clamp(BackN.x, -1.0 + ViewportSize.z, 1.0 - ViewportSize.z);
|
|
BackN.y = clamp(BackN.y, -1.0 + ViewportSize.w, 1.0 - ViewportSize.w);
|
|
// Convert from [-1 to 1] to view rectangle which is somewhere in [0 to 1].
|
|
// The extra +0.5 factor is because ScreenPosToPixel.zw is incorrectly computed
|
|
// as the upper left of the pixel instead of the center of the pixel.
|
|
BackN = (BackN * ScreenPosToPixel.xy + ScreenPosToPixel.zw + 0.5) * PostprocessInput0Size.zw;
|
|
|
|
// FILTER PIXEL (RESAMPLE TO REMOVE JITTER OFFSET) AND GET NEIGHBORHOOD
|
|
// --------------------------------------------------------------------
|
|
// 012
|
|
// 345
|
|
// 678
|
|
float4 Neighbor0 = PostprocessInput0.SampleLevel(PostprocessInput0Sampler, UVAndScreenPos.xy, 0, int2(-1, -1));
|
|
float4 Neighbor1 = PostprocessInput0.SampleLevel(PostprocessInput0Sampler, UVAndScreenPos.xy, 0, int2( 0, -1));
|
|
float4 Neighbor2 = PostprocessInput0.SampleLevel(PostprocessInput0Sampler, UVAndScreenPos.xy, 0, int2( 1, -1));
|
|
float4 Neighbor3 = PostprocessInput0.SampleLevel(PostprocessInput0Sampler, UVAndScreenPos.xy, 0, int2(-1, 0));
|
|
float4 Neighbor4 = PostprocessInput0.SampleLevel(PostprocessInput0Sampler, UVAndScreenPos.xy, 0);
|
|
float4 Neighbor5 = PostprocessInput0.SampleLevel(PostprocessInput0Sampler, UVAndScreenPos.xy, 0, int2( 1, 0));
|
|
float4 Neighbor6 = PostprocessInput0.SampleLevel(PostprocessInput0Sampler, UVAndScreenPos.xy, 0, int2(-1, 1));
|
|
float4 Neighbor7 = PostprocessInput0.SampleLevel(PostprocessInput0Sampler, UVAndScreenPos.xy, 0, int2( 0, 1));
|
|
float4 Neighbor8 = PostprocessInput0.SampleLevel(PostprocessInput0Sampler, UVAndScreenPos.xy, 0, int2( 1, 1));
|
|
#if AA_GREEN_AS_LUMA
|
|
Neighbor0.rgb *= HdrWeightG(Neighbor0.rgb, InExposureScale);
|
|
Neighbor1.rgb *= HdrWeightG(Neighbor1.rgb, InExposureScale);
|
|
Neighbor2.rgb *= HdrWeightG(Neighbor2.rgb, InExposureScale);
|
|
Neighbor3.rgb *= HdrWeightG(Neighbor3.rgb, InExposureScale);
|
|
Neighbor4.rgb *= HdrWeightG(Neighbor4.rgb, InExposureScale);
|
|
Neighbor5.rgb *= HdrWeightG(Neighbor5.rgb, InExposureScale);
|
|
Neighbor6.rgb *= HdrWeightG(Neighbor6.rgb, InExposureScale);
|
|
Neighbor7.rgb *= HdrWeightG(Neighbor7.rgb, InExposureScale);
|
|
Neighbor8.rgb *= HdrWeightG(Neighbor8.rgb, InExposureScale);
|
|
#else
|
|
Neighbor0.rgb *= HdrWeight4(Neighbor0.rgb, InExposureScale);
|
|
Neighbor1.rgb *= HdrWeight4(Neighbor1.rgb, InExposureScale);
|
|
Neighbor2.rgb *= HdrWeight4(Neighbor2.rgb, InExposureScale);
|
|
Neighbor3.rgb *= HdrWeight4(Neighbor3.rgb, InExposureScale);
|
|
Neighbor4.rgb *= HdrWeight4(Neighbor4.rgb, InExposureScale);
|
|
Neighbor5.rgb *= HdrWeight4(Neighbor5.rgb, InExposureScale);
|
|
Neighbor6.rgb *= HdrWeight4(Neighbor6.rgb, InExposureScale);
|
|
Neighbor7.rgb *= HdrWeight4(Neighbor7.rgb, InExposureScale);
|
|
Neighbor8.rgb *= HdrWeight4(Neighbor8.rgb, InExposureScale);
|
|
#endif
|
|
#if AA_FILTERED
|
|
float4 Filtered =
|
|
Neighbor0 * SampleWeights[0] +
|
|
Neighbor1 * SampleWeights[1] +
|
|
Neighbor2 * SampleWeights[2] +
|
|
Neighbor3 * SampleWeights[3] +
|
|
Neighbor4 * SampleWeights[4] +
|
|
Neighbor5 * SampleWeights[5] +
|
|
Neighbor6 * SampleWeights[6] +
|
|
Neighbor7 * SampleWeights[7] +
|
|
Neighbor8 * SampleWeights[8];
|
|
#if AA_LOWPASS
|
|
float4 FilteredLow =
|
|
Neighbor0 * LowpassWeights[0] +
|
|
Neighbor1 * LowpassWeights[1] +
|
|
Neighbor2 * LowpassWeights[2] +
|
|
Neighbor3 * LowpassWeights[3] +
|
|
Neighbor4 * LowpassWeights[4] +
|
|
Neighbor5 * LowpassWeights[5] +
|
|
Neighbor6 * LowpassWeights[6] +
|
|
Neighbor7 * LowpassWeights[7] +
|
|
Neighbor8 * LowpassWeights[8];
|
|
#else
|
|
float4 FilteredLow = Filtered;
|
|
#endif
|
|
#if AA_BORDER
|
|
// Use unfiltered for 1 pixel border.
|
|
float2 TestPos = abs(UVAndScreenPos.zw);
|
|
// Add 1 pixel and check if off screen.
|
|
TestPos += ViewportSize.zw * 2.0;
|
|
bool FilteredOffScreen = max(TestPos.x, TestPos.y) >= 1.0;
|
|
if(FilteredOffScreen)
|
|
{
|
|
Filtered = Neighbor4;
|
|
FilteredLow = Neighbor4;
|
|
}
|
|
#endif
|
|
#else
|
|
// Unfiltered.
|
|
float4 Filtered = Neighbor4;
|
|
float4 FilteredLow = Neighbor4;
|
|
#endif
|
|
#if AA_ROUND
|
|
float4 NeighborMin2 = min(min(Neighbor0, Neighbor2), min(Neighbor6, Neighbor8));
|
|
float4 NeighborMax2 = max(max(Neighbor0, Neighbor2), max(Neighbor6, Neighbor8));
|
|
float4 NeighborMin = min(min(min(Neighbor1, Neighbor3), min(Neighbor4, Neighbor5)), Neighbor7);
|
|
float4 NeighborMax = max(max(max(Neighbor1, Neighbor3), max(Neighbor4, Neighbor5)), Neighbor7);
|
|
NeighborMin2 = min(NeighborMin2, NeighborMin);
|
|
NeighborMax2 = max(NeighborMax2, NeighborMax);
|
|
NeighborMin = NeighborMin * 0.5 + NeighborMin2 * 0.5;
|
|
NeighborMax = NeighborMax * 0.5 + NeighborMax2 * 0.5;
|
|
#else
|
|
float4 NeighborMin = min(min(
|
|
min(min(Neighbor0, Neighbor1), min(Neighbor2, Neighbor3)),
|
|
min(min(Neighbor4, Neighbor5), min(Neighbor6, Neighbor7))), Neighbor8);
|
|
float4 NeighborMax = max(max(
|
|
max(max(Neighbor0, Neighbor1), max(Neighbor2, Neighbor3)),
|
|
max(max(Neighbor4, Neighbor5), max(Neighbor6, Neighbor7))), Neighbor8);
|
|
#endif
|
|
|
|
// FETCH HISTORY
|
|
// -------------
|
|
OutColor = PostprocessInput1.SampleLevel(PostprocessInput1Sampler, BackN.xy, 0);
|
|
#if AA_DEBUG
|
|
Neighbor4.rg = float2(0.0, 0.0);
|
|
NeighborMin.rg = float2(0.0, 0.0);
|
|
NeighborMax.rg = float2(0.0, 0.0);
|
|
Filtered.rg = float2(0.0, 0.0);
|
|
FilteredLow.rg = float2(0.0, 0.0);
|
|
float DebugDiffCurrent = Filtered.b;
|
|
#endif
|
|
#if AA_GREEN_AS_LUMA
|
|
OutColor.rgb *= HdrWeightG(OutColor.rgb, InExposureScale);
|
|
#else
|
|
OutColor.rgb *= HdrWeight4(OutColor.rgb, InExposureScale);
|
|
#endif
|
|
#if AA_DEBUG
|
|
OutColor.rg = float2(0.0, 0.0);
|
|
float DebugDiffPrior = OutColor.b;
|
|
#endif
|
|
|
|
// FIND LUMA OF CLAMPED HISTORY
|
|
// ----------------------------
|
|
// Save off luma of history before the clamp.
|
|
#if AA_GREEN_AS_LUMA
|
|
float LumaMin = NeighborMin.g;
|
|
float LumaMax = NeighborMax.g;
|
|
float LumaHistory = OutColor.g;
|
|
#else
|
|
float LumaMin = Luma4(NeighborMin.rgb);
|
|
float LumaMax = Luma4(NeighborMax.rgb);
|
|
float LumaHistory = Luma4(OutColor.rgb);
|
|
#endif
|
|
float LumaContrast = LumaMax - LumaMin;
|
|
#if AA_AABB
|
|
// Clamp history, this uses color AABB intersection for tighter fit.
|
|
// Clamping works with the low pass (if available) to reduce flicker.
|
|
float ClampBlend = HistoryClamp(OutColor.rgb, FilteredLow.rgb, NeighborMin.rgb, NeighborMax.rgb);
|
|
#if AA_ALPHA
|
|
OutColor.rgb = lerp(OutColor.rgb, FilteredLow.rgb, ClampBlend);
|
|
#else
|
|
OutColor.rgba = lerp(OutColor.rgba, FilteredLow.rgba, ClampBlend);
|
|
#endif
|
|
#else
|
|
OutColor = clamp(OutColor, NeighborMin, NeighborMax);
|
|
#endif
|
|
#if AA_DEBUG
|
|
OutColor.rg = float2(0.0, 0.0);
|
|
#endif
|
|
|
|
// ADD BACK IN ALIASING TO SHARPEN
|
|
// -------------------------------
|
|
#if AA_FILTERED
|
|
// Blend in non-filtered based on the amount of sub-pixel motion.
|
|
float AddAliasing = saturate(HistoryBlur) * 0.5;
|
|
float LumaContrastFactor = 32.0;
|
|
#if AA_GREEN_AS_LUMA
|
|
// GREEN_AS_LUMA is 1/4 as bright.
|
|
LumaContrastFactor *= 4.0;
|
|
#endif
|
|
AddAliasing = saturate(AddAliasing + rcp(1.0 + LumaContrast * LumaContrastFactor));
|
|
Filtered.rgb = lerp(Filtered.rgb, Neighbor4.rgb, AddAliasing);
|
|
#endif
|
|
#if AA_GREEN_AS_LUMA
|
|
float LumaFiltered = Filtered.g;
|
|
#else
|
|
float LumaFiltered = Luma4(Filtered.rgb);
|
|
#endif
|
|
|
|
// COMPUTE DYNAMIC MOTION BLEND FACTOR
|
|
// -----------------------------------
|
|
#if (AA_DYNAMIC == 1)
|
|
float BlendDynamicMotion = 0.0;
|
|
#if (AA_ALPHA == 1)
|
|
// Decay prior value.
|
|
OutColor.a *= 0.5;
|
|
BlendDynamicMotion = OutColor.a;
|
|
#endif
|
|
if(DynamicMotion)
|
|
{
|
|
float BlendDynamicMotionNew = (1.0/2.0) * saturate(DoublePixelMotionDynamic * (1.0/1.0));
|
|
// Reduce dynamic motion factor if camera is also moving.
|
|
BlendDynamicMotionNew *= 1.0 - HistoryBlur;
|
|
#if (AA_ALPHA == 1)
|
|
// Feedback of 2.0 provides 1 extra frame of fast reconvergence.
|
|
// Feedback of 4.0 provides 2 extra frames of fast reconvergence.
|
|
// This enables fast reconverge after dynamic motion.
|
|
float FeedbackDynamic = 4.0;
|
|
OutColor.a = max(OutColor.a, BlendDynamicMotionNew * FeedbackDynamic);
|
|
BlendDynamicMotion = max(BlendDynamicMotion, BlendDynamicMotionNew);
|
|
#else
|
|
BlendDynamicMotion = BlendDynamicMotionNew;
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
// COMPUTE BLEND AMOUNT
|
|
// --------------------
|
|
float HistoryAmount = (1.0/8.0) + HistoryBlur * (1.0/8.0);
|
|
float HistoryFactor = LumaHistory * HistoryAmount * (1.0 + HistoryBlur * HistoryAmount * 4.0);
|
|
float BlendFinal = saturate(HistoryFactor * rcp(LumaHistory + LumaContrast));
|
|
#if RESPONSIVE
|
|
// Responsive forces 1/4 of new frame.
|
|
BlendFinal = 1.0/4.0;
|
|
#endif
|
|
#if (AA_DYNAMIC == 1)
|
|
BlendFinal = max(BlendFinal, BlendDynamicMotion);
|
|
BlendFinal = min(1.0/2.0, BlendFinal);
|
|
#endif
|
|
// Offscreen feedback resets.
|
|
#if AA_LERP
|
|
float FixedLerp = 1.0/float(AA_LERP);
|
|
#endif
|
|
if(OffScreen)
|
|
{
|
|
OutColor = Filtered;
|
|
#if AA_ALPHA
|
|
OutColor.a = 0.0;
|
|
#endif
|
|
#if AA_LERP
|
|
FixedLerp = 1.0;
|
|
#endif
|
|
}
|
|
|
|
// DO FINAL BLEND BETWEEN HISTORY AND FILTERED COLOR
|
|
// -------------------------------------------------
|
|
#if (AA_LERP == 0)
|
|
#if AA_ALPHA
|
|
// Blend in linear to hit luma target.
|
|
OutColor.rgb = lerp(OutColor.rgb, Filtered.rgb, BlendFinal);
|
|
#if RESPONSIVE
|
|
OutColor.a = max(OutColor.a, 1.0/2.0);
|
|
#endif
|
|
#else
|
|
OutColor = lerp(OutColor, Filtered, BlendFinal);
|
|
#if AA_FORCE_ALPHA_CLAMP
|
|
OutColor.a = clamp(OutColor.a, NeighborMin.a, NeighborMax.a);
|
|
#endif
|
|
#endif
|
|
// Convert back into linear.
|
|
#if AA_GREEN_AS_LUMA
|
|
OutColor.rgb *= HdrWeightInvG(OutColor.rgb, InExposureScale);
|
|
#else
|
|
OutColor.rgb *= HdrWeightInv4(OutColor.rgb, InExposureScale);
|
|
#endif
|
|
#else
|
|
OutColor = lerp(OutColor, Filtered, FixedLerp);
|
|
#if AA_GREEN_AS_LUMA
|
|
OutColor.rgb *= HdrWeightInvG(OutColor.rgb, InExposureScale);
|
|
#else
|
|
OutColor.rgb *= HdrWeightInv4(OutColor.rgb, InExposureScale);
|
|
#endif
|
|
#endif
|
|
#if AA_NAN
|
|
// Transform NaNs to black, transform negative colors to black.
|
|
OutColor.rgb = -min(-OutColor.rgb, 0.0);
|
|
#endif
|
|
#if AA_DEBUG
|
|
OutColor.g = abs(DebugDiffPrior - DebugDiffCurrent);
|
|
OutColor.r = OutColor.a;
|
|
#endif
|
|
|
|
#else
|
|
|
|
// FIND MOTION OF PIXEL AND NEAREST IN NEIGHBORHOOD
|
|
// ------------------------------------------------
|
|
float3 PosP; // Position of this pixel.
|
|
PosP.xy = UVAndScreenPos.zw * float2(0.5, -0.5) + 0.5; // View position [0 to 1] flipped in Y.
|
|
PosP.z = SceneDepthTexture.SampleLevel(SceneDepthTextureSampler, UVAndScreenPos.xy, 0).r;
|
|
float3 PosN; // Position of closest pixel in neighborhood.
|
|
PosN = PosP;
|
|
// Screen position of minimum depth.
|
|
float2 VelocityOffset = float2(0.0, 0.0);
|
|
#if AA_CROSS
|
|
// For motion vector, use camera/dynamic motion from min depth pixel in pattern around pixel.
|
|
// This enables better quality outline on foreground against different motion background.
|
|
// Larger 2 pixel distance "x" works best (because AA diolates surface).
|
|
float4 Depths;
|
|
#if FEATURE_LEVEL >= FEATURE_LEVEL_SM5
|
|
Depths = SceneDepthTexture.GatherRed(SceneDepthTextureSampler, UVAndScreenPos.xy, int2(-AA_CROSS, -AA_CROSS), int2(AA_CROSS, -AA_CROSS), int2(-AA_CROSS, AA_CROSS), int2(AA_CROSS, AA_CROSS));
|
|
#else
|
|
Depths.x = SceneDepthTexture.SampleLevel(SceneDepthTextureSampler, UVAndScreenPos.xy, 0, int2(-AA_CROSS, -AA_CROSS)).r;
|
|
Depths.y = SceneDepthTexture.SampleLevel(SceneDepthTextureSampler, UVAndScreenPos.xy, 0, int2( AA_CROSS, -AA_CROSS)).r;
|
|
Depths.z = SceneDepthTexture.SampleLevel(SceneDepthTextureSampler, UVAndScreenPos.xy, 0, int2(-AA_CROSS, AA_CROSS)).r;
|
|
Depths.w = SceneDepthTexture.SampleLevel(SceneDepthTextureSampler, UVAndScreenPos.xy, 0, int2( AA_CROSS, AA_CROSS)).r;
|
|
#endif
|
|
float2 DepthOffset = float2(AA_CROSS, AA_CROSS);
|
|
float DepthOffsetXx = float(AA_CROSS);
|
|
// Nearest depth is the largest depth (depth surface 0=far, 1=near).
|
|
if(Depths.x > Depths.y)
|
|
{
|
|
DepthOffsetXx = -AA_CROSS;
|
|
}
|
|
if(Depths.z > Depths.w)
|
|
{
|
|
DepthOffset.x = -AA_CROSS;
|
|
}
|
|
float DepthsXY = max(Depths.x, Depths.y);
|
|
float DepthsZW = max(Depths.z, Depths.w);
|
|
if(DepthsXY > DepthsZW)
|
|
{
|
|
DepthOffset.y = -AA_CROSS;
|
|
DepthOffset.x = DepthOffsetXx;
|
|
}
|
|
float DepthsXYZW = max(DepthsXY, DepthsZW);
|
|
if(DepthsXYZW > PosP.z)
|
|
{
|
|
// This is offset for reading from velocity texture.
|
|
// This supports half or fractional resolution velocity textures.
|
|
// With the assumption that UV position scales between velocity and color.
|
|
VelocityOffset = DepthOffset * PostprocessInput0Size.zw;
|
|
// This is [0 to 1] flipped in Y.
|
|
PosN.xy = (UVAndScreenPos.zw + DepthOffset * ViewportSize.zw * 2.0) * float2(0.5, -0.5) + 0.5;
|
|
PosN.z = DepthsXYZW;
|
|
}
|
|
#endif
|
|
// Camera motion for pixel (in ScreenPos space).
|
|
float ScaleM = 1.0 / (dot(PosP, CameraMotion[0].xyz) + CameraMotion[0].w);
|
|
float2 BackP;
|
|
BackP.x = -2.0 * ((PosP.x * ((CameraMotion[1].x * PosP.y) + (CameraMotion[1].y * PosP.z) + CameraMotion[1].z)) + (CameraMotion[1].w * PosP.y) + (CameraMotion[2].x * PosP.x * PosP.x) + (CameraMotion[2].y * PosP.z) + CameraMotion[2].z) * ScaleM;
|
|
BackP.y = 2.0 * ((PosP.y * ((CameraMotion[3].x * PosP.x) + (CameraMotion[3].y * PosP.z) + CameraMotion[3].z)) + (CameraMotion[3].w * PosP.x) + (CameraMotion[4].x * PosP.y * PosP.y) + (CameraMotion[4].y * PosP.z) + CameraMotion[4].z) * ScaleM;
|
|
// Convert BackP back into pixels.
|
|
float2 BackTemp = BackP * ViewportSize.xy;
|
|
// At this point DoublePixelMotionCamera is kept squared until later.
|
|
float DoublePixelMotionCamera = dot(BackTemp, BackTemp);
|
|
#if AA_CROSS
|
|
// Camera motion for neighborhood.
|
|
ScaleM = 1.0 / (dot(PosN, CameraMotion[0].xyz) + CameraMotion[0].w);
|
|
float2 BackN;
|
|
BackN.x = -2.0 * ((PosN.x * ((CameraMotion[1].x * PosN.y) + (CameraMotion[1].y * PosN.z) + CameraMotion[1].z)) + (CameraMotion[1].w * PosN.y) + (CameraMotion[2].x * PosN.x * PosN.x) + (CameraMotion[2].y * PosN.z) + CameraMotion[2].z) * ScaleM;
|
|
BackN.y = 2.0 * ((PosN.y * ((CameraMotion[3].x * PosN.x) + (CameraMotion[3].y * PosN.z) + CameraMotion[3].z)) + (CameraMotion[3].w * PosN.x) + (CameraMotion[4].x * PosN.y * PosN.y) + (CameraMotion[4].y * PosN.z) + CameraMotion[4].z) * ScaleM;
|
|
BackTemp = BackN * ViewportSize.xy;
|
|
DoublePixelMotionCamera = max(DoublePixelMotionCamera, dot(BackTemp, BackTemp));
|
|
#endif
|
|
DoublePixelMotionCamera = sqrt(DoublePixelMotionCamera);
|
|
#if AA_DYNAMIC
|
|
// Dynamic motion of pixel.
|
|
#if (AA_CROSS == 0) || (AA_ONE_DYNAMIC_SAMPLE == 0)
|
|
float2 VelocityP = PostprocessInput3.SampleLevel(PostprocessInput3Sampler, UVAndScreenPos.xy, 0).xy;
|
|
bool DynamicP = VelocityP.x > 0.0;
|
|
if(DynamicP)
|
|
{
|
|
BackP = DecodeVelocityFromTexture(VelocityP);
|
|
}
|
|
#endif
|
|
BackTemp = BackP * ViewportSize.xy;
|
|
// At this point DoublePixelMotionDynamic is kept squared until later.
|
|
float DoublePixelMotionDynamic = dot(BackTemp, BackTemp);
|
|
#if AA_CROSS
|
|
// Dynamic motion of nearest in neighborhood.
|
|
float2 VelocityN = PostprocessInput3.SampleLevel(PostprocessInput3Sampler, UVAndScreenPos.xy + VelocityOffset, 0).xy;
|
|
bool DynamicN = VelocityN.x > 0.0;
|
|
if(DynamicN)
|
|
{
|
|
BackN = DecodeVelocityFromTexture(VelocityN);
|
|
}
|
|
#if (AA_ONE_DYNAMIC_SAMPLE == 0)
|
|
bool DynamicMotion = DynamicN || DynamicP;
|
|
#else
|
|
bool DynamicMotion = DynamicN;
|
|
#endif
|
|
BackTemp = BackN * ViewportSize.xy;
|
|
DoublePixelMotionDynamic = max(DoublePixelMotionDynamic, dot(BackTemp, BackTemp));
|
|
#else
|
|
bool DynamicMotion = DynamicP;
|
|
#endif
|
|
DoublePixelMotionDynamic = sqrt(DoublePixelMotionDynamic);
|
|
#else
|
|
bool DynamicMotion = false;
|
|
float DoublePixelMotionDynamic = 0.0;
|
|
#endif
|
|
// Find motion edges.
|
|
#if AA_CROSS
|
|
float2 BackPN = (BackP - BackN) * ViewportSize.xy;
|
|
float DoublePixelMotionDiff = sqrt(dot(BackPN, BackPN));
|
|
#else
|
|
float2 BackN = BackP;
|
|
float DoublePixelMotionDiff = 0.0;
|
|
#endif
|
|
// Easier to do off screen check before conversion.
|
|
// BackN is in units of 2pixels/viewportWidthInPixels
|
|
// This converts back projection vector to [-1 to 1] offset in viewport.
|
|
BackN = UVAndScreenPos.zw - BackN;
|
|
bool OffScreen = max(abs(BackN.x), abs(BackN.y)) >= 1.0;
|
|
// Also clamp to be on screen (fixes problem with DOF).
|
|
// The .z and .w is the 1/width and 1/height.
|
|
// This clamps to be a pixel inside the viewport.
|
|
BackN.x = clamp(BackN.x, -1.0 + ViewportSize.z, 1.0 - ViewportSize.z);
|
|
BackN.y = clamp(BackN.y, -1.0 + ViewportSize.w, 1.0 - ViewportSize.w);
|
|
// Convert from [-1 to 1] to view rectangle which is somewhere in [0 to 1].
|
|
// The extra +0.5 factor is because ScreenPosToPixel.zw is incorrectly computed
|
|
// as the upper left of the pixel instead of the center of the pixel.
|
|
BackN = (BackN * ScreenPosToPixel.xy + ScreenPosToPixel.zw + 0.5) * PostprocessInput0Size.zw;
|
|
|
|
// FILTER PIXEL (RESAMPLE TO REMOVE JITTER OFFSET) AND GET NEIGHBORHOOD
|
|
// --------------------------------------------------------------------
|
|
// 012
|
|
// 345
|
|
// 678
|
|
float4 Neighbor0 = PostprocessInput0.SampleLevel(PostprocessInput0Sampler, UVAndScreenPos.xy, 0, int2(-1, -1));
|
|
float4 Neighbor1 = PostprocessInput0.SampleLevel(PostprocessInput0Sampler, UVAndScreenPos.xy, 0, int2( 0, -1));
|
|
float4 Neighbor2 = PostprocessInput0.SampleLevel(PostprocessInput0Sampler, UVAndScreenPos.xy, 0, int2( 1, -1));
|
|
float4 Neighbor3 = PostprocessInput0.SampleLevel(PostprocessInput0Sampler, UVAndScreenPos.xy, 0, int2(-1, 0));
|
|
float4 Neighbor4 = PostprocessInput0.SampleLevel(PostprocessInput0Sampler, UVAndScreenPos.xy, 0);
|
|
float4 Neighbor5 = PostprocessInput0.SampleLevel(PostprocessInput0Sampler, UVAndScreenPos.xy, 0, int2( 1, 0));
|
|
float4 Neighbor6 = PostprocessInput0.SampleLevel(PostprocessInput0Sampler, UVAndScreenPos.xy, 0, int2(-1, 1));
|
|
float4 Neighbor7 = PostprocessInput0.SampleLevel(PostprocessInput0Sampler, UVAndScreenPos.xy, 0, int2( 0, 1));
|
|
float4 Neighbor8 = PostprocessInput0.SampleLevel(PostprocessInput0Sampler, UVAndScreenPos.xy, 0, int2( 1, 1));
|
|
#if AA_FILTERED
|
|
#if AA_HDR
|
|
float SampleHdrWeight0 = HdrWeight(Neighbor0.rgb, InExposureScale);
|
|
float SampleHdrWeight1 = HdrWeight(Neighbor1.rgb, InExposureScale);
|
|
float SampleHdrWeight2 = HdrWeight(Neighbor2.rgb, InExposureScale);
|
|
float SampleHdrWeight3 = HdrWeight(Neighbor3.rgb, InExposureScale);
|
|
float SampleHdrWeight4 = HdrWeight(Neighbor4.rgb, InExposureScale);
|
|
float SampleHdrWeight5 = HdrWeight(Neighbor5.rgb, InExposureScale);
|
|
float SampleHdrWeight6 = HdrWeight(Neighbor6.rgb, InExposureScale);
|
|
float SampleHdrWeight7 = HdrWeight(Neighbor7.rgb, InExposureScale);
|
|
float SampleHdrWeight8 = HdrWeight(Neighbor8.rgb, InExposureScale);
|
|
float4 Filtered = (
|
|
Neighbor0 * (SampleWeights[0] * SampleHdrWeight0) +
|
|
Neighbor1 * (SampleWeights[1] * SampleHdrWeight1) +
|
|
Neighbor2 * (SampleWeights[2] * SampleHdrWeight2) +
|
|
Neighbor3 * (SampleWeights[3] * SampleHdrWeight3) +
|
|
Neighbor4 * (SampleWeights[4] * SampleHdrWeight4) +
|
|
Neighbor5 * (SampleWeights[5] * SampleHdrWeight5) +
|
|
Neighbor6 * (SampleWeights[6] * SampleHdrWeight6) +
|
|
Neighbor7 * (SampleWeights[7] * SampleHdrWeight7) +
|
|
Neighbor8 * (SampleWeights[8] * SampleHdrWeight8)) * rcp(
|
|
SampleWeights[0] * SampleHdrWeight0 +
|
|
SampleWeights[1] * SampleHdrWeight1 +
|
|
SampleWeights[2] * SampleHdrWeight2 +
|
|
SampleWeights[3] * SampleHdrWeight3 +
|
|
SampleWeights[4] * SampleHdrWeight4 +
|
|
SampleWeights[5] * SampleHdrWeight5 +
|
|
SampleWeights[6] * SampleHdrWeight6 +
|
|
SampleWeights[7] * SampleHdrWeight7 +
|
|
SampleWeights[8] * SampleHdrWeight8);
|
|
#if AA_LOWPASS
|
|
float4 FilteredLow = (
|
|
Neighbor0 * (LowpassWeights[0] * SampleHdrWeight0) +
|
|
Neighbor1 * (LowpassWeights[1] * SampleHdrWeight1) +
|
|
Neighbor2 * (LowpassWeights[2] * SampleHdrWeight2) +
|
|
Neighbor3 * (LowpassWeights[3] * SampleHdrWeight3) +
|
|
Neighbor4 * (LowpassWeights[4] * SampleHdrWeight4) +
|
|
Neighbor5 * (LowpassWeights[5] * SampleHdrWeight5) +
|
|
Neighbor6 * (LowpassWeights[6] * SampleHdrWeight6) +
|
|
Neighbor7 * (LowpassWeights[7] * SampleHdrWeight7) +
|
|
Neighbor8 * (LowpassWeights[8] * SampleHdrWeight8)) * rcp(
|
|
LowpassWeights[0] * SampleHdrWeight0 +
|
|
LowpassWeights[1] * SampleHdrWeight1 +
|
|
LowpassWeights[2] * SampleHdrWeight2 +
|
|
LowpassWeights[3] * SampleHdrWeight3 +
|
|
LowpassWeights[4] * SampleHdrWeight4 +
|
|
LowpassWeights[5] * SampleHdrWeight5 +
|
|
LowpassWeights[6] * SampleHdrWeight6 +
|
|
LowpassWeights[7] * SampleHdrWeight7 +
|
|
LowpassWeights[8] * SampleHdrWeight8);
|
|
#else
|
|
float4 FilteredLow = Filtered;
|
|
#endif
|
|
#else
|
|
float4 Filtered =
|
|
Neighbor0 * SampleWeights[0] +
|
|
Neighbor1 * SampleWeights[1] +
|
|
Neighbor2 * SampleWeights[2] +
|
|
Neighbor3 * SampleWeights[3] +
|
|
Neighbor4 * SampleWeights[4] +
|
|
Neighbor5 * SampleWeights[5] +
|
|
Neighbor6 * SampleWeights[6] +
|
|
Neighbor7 * SampleWeights[7] +
|
|
Neighbor8 * SampleWeights[8];
|
|
#if AA_LOWPASS
|
|
float4 FilteredLow =
|
|
Neighbor0 * LowpassWeights[0] +
|
|
Neighbor1 * LowpassWeights[1] +
|
|
Neighbor2 * LowpassWeights[2] +
|
|
Neighbor3 * LowpassWeights[3] +
|
|
Neighbor4 * LowpassWeights[4] +
|
|
Neighbor5 * LowpassWeights[5] +
|
|
Neighbor6 * LowpassWeights[6] +
|
|
Neighbor7 * LowpassWeights[7] +
|
|
Neighbor8 * LowpassWeights[8];
|
|
#else
|
|
float4 FilteredLow = Filtered;
|
|
#endif
|
|
#endif
|
|
#if AA_BORDER
|
|
// Use unfiltered for 1 pixel border.
|
|
float2 TestPos = abs(UVAndScreenPos.zw);
|
|
// Add 1 pixel and check if off screen.
|
|
TestPos += ViewportSize.zw * 2.0;
|
|
bool FilteredOffScreen = max(TestPos.x, TestPos.y) >= 1.0;
|
|
if(FilteredOffScreen)
|
|
{
|
|
Filtered = Neighbor4;
|
|
FilteredLow = Filtered;
|
|
}
|
|
#endif
|
|
#else
|
|
// Unfiltered.
|
|
float4 Filtered = Neighbor4;
|
|
float4 FilteredLow = Filtered;
|
|
#endif
|
|
#if AA_ROUND
|
|
float4 NeighborMin2 = min(min(Neighbor0, Neighbor2), min(Neighbor6, Neighbor8));
|
|
float4 NeighborMax2 = max(max(Neighbor0, Neighbor2), max(Neighbor6, Neighbor8));
|
|
float4 NeighborMin = min(min(min(Neighbor1, Neighbor3), min(Neighbor4, Neighbor5)), Neighbor7);
|
|
float4 NeighborMax = max(max(max(Neighbor1, Neighbor3), max(Neighbor4, Neighbor5)), Neighbor7);
|
|
NeighborMin2 = min(NeighborMin2, NeighborMin);
|
|
NeighborMax2 = max(NeighborMax2, NeighborMax);
|
|
#if AA_HDR
|
|
NeighborMin = HdrLerp(NeighborMin, NeighborMin2, 0.5, InExposureScale);
|
|
NeighborMax = HdrLerp(NeighborMax, NeighborMax2, 0.5, InExposureScale);
|
|
#else
|
|
NeighborMin = lerp(NeighborMin, NeighborMin2, 0.5);
|
|
NeighborMax = lerp(NeighborMax, NeighborMax2, 0.5);
|
|
#endif
|
|
#else
|
|
float4 NeighborMin = min(min(
|
|
min(min(Neighbor0, Neighbor1), min(Neighbor2, Neighbor3)),
|
|
min(min(Neighbor4, Neighbor5), min(Neighbor6, Neighbor7))), Neighbor8);
|
|
float4 NeighborMax = max(max(
|
|
max(max(Neighbor0, Neighbor1), max(Neighbor2, Neighbor3)),
|
|
max(max(Neighbor4, Neighbor5), max(Neighbor6, Neighbor7))), Neighbor8);
|
|
#endif
|
|
#if AA_DEBUG
|
|
NeighborMin.rg = float2(0.0, 0.0);
|
|
NeighborMax.rg = float2(0.0, 0.0);
|
|
Filtered.rg = float2(0.0, 0.0);
|
|
FilteredLow.rg = float2(0.0, 0.0);
|
|
float DebugDiffCurrent = Filtered.b;
|
|
#endif
|
|
|
|
// FETCH HISTORY AND MANUALLY INTERPOLATE WITH WEIGHT FILTER FOR PROPER HDR
|
|
// ------------------------------------------------------------------------
|
|
#if AA_HDR_HISTORY
|
|
// a c
|
|
// EF
|
|
// gHI
|
|
float4 OutColorE = PostprocessInput2.SampleLevel(PostprocessInput2Sampler, BackN.xy, 0, int2(0, 0));
|
|
float4 OutColorF = PostprocessInput2.SampleLevel(PostprocessInput2Sampler, BackN.xy, 0, int2(1, 0));
|
|
float4 OutColorH = PostprocessInput2.SampleLevel(PostprocessInput2Sampler, BackN.xy, 0, int2(0, 1));
|
|
float4 OutColorI = PostprocessInput2.SampleLevel(PostprocessInput2Sampler, BackN.xy, 0, int2(1, 1));
|
|
float WeightE = HdrWeight(OutColorE.rgb, InExposureScale);
|
|
float WeightF = HdrWeight(OutColorF.rgb, InExposureScale);
|
|
float WeightH = HdrWeight(OutColorH.rgb, InExposureScale);
|
|
float WeightI = HdrWeight(OutColorI.rgb, InExposureScale);
|
|
float2 Subpix = frac(BackN.xy * PostprocessInput1Size.zw);
|
|
WeightE *= (1.0 - Subpix.x) * (1.0 - Subpix.y);
|
|
WeightF *= ( Subpix.x) * (1.0 - Subpix.y);
|
|
WeightH *= (1.0 - Subpix.x) * ( Subpix.y);
|
|
WeightI *= ( Subpix.x) * ( Subpix.y);
|
|
float RcpWeightEFHI = rcp(WeightE + WeightF + WeightH + WeightI);
|
|
WeightE *= RcpWeightEFHI;
|
|
WeightF *= RcpWeightEFHI;
|
|
WeightH *= RcpWeightEFHI;
|
|
WeightI *= RcpWeightEFHI;
|
|
OutColor = (OutColorE * WeightE) + (OutColorF * WeightF) + (OutColorH * WeightH) + (OutColorI * WeightI);
|
|
#else
|
|
OutColor = PostprocessInput1.SampleLevel(PostprocessInput1Sampler, BackN.xy, 0);
|
|
#endif
|
|
#if AA_DILATE
|
|
// Grab alphas from cross pattern and take maximum, dilate feedback control.
|
|
float4 Alphas;
|
|
#if FEATURE_LEVEL >= FEATURE_LEVEL_SM5
|
|
// Samples acgI
|
|
Alphas = PostprocessInput2.GatherAlpha(PostprocessInput2Sampler, BackN.xy, int2(-AA_DILATE, -AA_DILATE), int2(AA_DILATE, -AA_DILATE), int2(-AA_DILATE, AA_DILATE), int2(AA_DILATE, AA_DILATE));
|
|
#else
|
|
Alphas.x = PostprocessInput2.SampleLevel(PostprocessInput2Sampler, BackN.xy, 0, int2(-AA_DILATE, -AA_DILATE)).w;
|
|
Alphas.y = PostprocessInput2.SampleLevel(PostprocessInput2Sampler, BackN.xy, 0, int2( AA_DILATE, -AA_DILATE)).w;
|
|
Alphas.z = PostprocessInput2.SampleLevel(PostprocessInput2Sampler, BackN.xy, 0, int2(-AA_DILATE, AA_DILATE)).w;
|
|
#if (AA_HDR_HISTORY == 1) && (AA_DILATE == 1)
|
|
Alphas.w = OutColorI.w;
|
|
#else
|
|
Alphas.w = PostprocessInput2.SampleLevel(PostprocessInput2Sampler, BackN.xy, 0, int2(AA_DILATE, AA_DILATE)).w;
|
|
#endif
|
|
#endif
|
|
OutColor.w = max(OutColor.w, max(max(Alphas.x, Alphas.y), max(Alphas.z, Alphas.w)));
|
|
#endif
|
|
#if AA_DEBUG
|
|
OutColor.rg = float2(0.0, 0.0);
|
|
float DebugDiffPrior = OutColor.b;
|
|
#endif
|
|
|
|
// FIND LUMA OF CLAMPED HISTORY
|
|
// ----------------------------
|
|
// Save luma converted into a perceptual space.
|
|
float LumaHistory = PerceptualLuma(OutColor.rgb, InExposureScale);
|
|
#if AA_AABB
|
|
// Clamp history, this uses color AABB intersection for tighter fit.
|
|
// Clamping works with the low pass (if available) to reduce flicker.
|
|
float ClampBlend = HistoryClamp(OutColor.rgb, FilteredLow.rgb, NeighborMin.rgb, NeighborMax.rgb);
|
|
float4 Clamped = lerp(OutColor, FilteredLow, ClampBlend);
|
|
#else
|
|
float4 Clamped = clamp(OutColor, NeighborMin, NeighborMax);
|
|
#endif
|
|
|
|
// FIND PERCEPTUAL LUMAS
|
|
// ---------------------
|
|
float LumaClamped = PerceptualLuma(Clamped.rgb, InExposureScale);
|
|
float LumaFiltered = PerceptualLuma(Filtered.rgb, InExposureScale);
|
|
float LumaFilteredLow = PerceptualLuma(FilteredLow.rgb, InExposureScale);
|
|
float LumaMin = PerceptualLuma(NeighborMin.rgb, InExposureScale);
|
|
float LumaMax = PerceptualLuma(NeighborMax.rgb, InExposureScale);
|
|
|
|
// CONVERT MOTION AMOUNTS TO ALLOWED HISTORY TO FILTERED CHANGE AMOUNTS
|
|
// --------------------------------------------------------------------
|
|
float3 Change;
|
|
// Small camera motion.
|
|
Change.x = (1.0/8.0) * saturate(DoublePixelMotionCamera * 128.0);
|
|
// Motion edges.
|
|
Change.y = (1.0/8.0) * saturate(DoublePixelMotionDiff * 8.0);
|
|
// Dynamic motion.
|
|
Change.z = 0.0;
|
|
if(DynamicMotion)
|
|
{
|
|
Change.z = (1.0/2.0) * saturate(DoublePixelMotionDynamic * (1.0/8.0));
|
|
}
|
|
|
|
// FEEDBACK CHANGE AMOUNT THEN CONVERT TO LUMA LIMITS FOR THIS FRAME
|
|
// -----------------------------------------------------------------
|
|
float ChangeLimit = 1.0/2.0;
|
|
#if AA_ALPHA
|
|
#if (AA_DYNAMIC == 0) && (AA_CROSS == 0)
|
|
ChangeLimit = 0.0;
|
|
#else
|
|
// Feedback half of change from last frame.
|
|
float ChangePrior = OutColor.a * (1.0/2.0);
|
|
#endif
|
|
// Feedback of 2.0 provides 1 extra frame of fast reconvergence.
|
|
// Feedback of 4.0 provides 2 extra frames of fast reconvergence.
|
|
// This enables fast reconverge after dynamic motion.
|
|
float FeedbackMotionEdge = 4.0;
|
|
float FeedbackDynamic = 4.0;
|
|
// Change limit on this frame.
|
|
#if (AA_DYNAMIC == 1) && (AA_CROSS == 0)
|
|
OutColor.a = max(ChangePrior, Change.z * FeedbackDynamic);
|
|
ChangeLimit = min(ChangeLimit, max(ChangePrior, Change.z));
|
|
#endif
|
|
#if (AA_DYNAMIC == 0) && (AA_CROSS != 0)
|
|
OutColor.a = max(ChangePrior, Change.y * FeedbackMotionEdge);
|
|
ChangeLimit = min(ChangeLimit, max(ChangePrior, Change.y));
|
|
#endif
|
|
#if (AA_DYNAMIC == 1) && (AA_CROSS != 0)
|
|
OutColor.a = max(ChangePrior, max(Change.y * FeedbackMotionEdge, Change.z * FeedbackDynamic));
|
|
ChangeLimit = min(ChangeLimit, max(ChangePrior, max(Change.y, Change.z)));
|
|
#endif
|
|
#else
|
|
#if (AA_DYNAMIC == 0) && (AA_CROSS == 0)
|
|
ChangeLimit = 0.0;
|
|
#endif
|
|
#if (AA_DYNAMIC == 1) && (AA_CROSS == 0)
|
|
ChangeLimit = min(ChangeLimit, Change.z);
|
|
#endif
|
|
#if (AA_DYNAMIC == 0) && (AA_CROSS != 0)
|
|
ChangeLimit = min(ChangeLimit, Change.y);
|
|
#endif
|
|
#if (AA_DYNAMIC == 1) && (AA_CROSS != 0)
|
|
ChangeLimit = min(1.0/2.0, max(Change.y, Change.z));
|
|
#endif
|
|
#endif
|
|
#if AA_GRAIN
|
|
// Introduce noise to the change limit.
|
|
// This reduces the convergence to a non-AA look on some edges.
|
|
float2 GrainUV = (UVAndScreenPos.zw * float2(0.5, -0.5) + 0.5) + RandomOffset.xy;
|
|
float Grain = frac(sin(GrainUV.x + GrainUV.y * 543.31) * 493013.0);
|
|
ChangeLimit += Grain * (1.0/16.0);
|
|
#endif
|
|
// Change limit is related to change in luma between history and current frame.
|
|
float ChangeLimitLuma = ChangeLimit * abs(LumaHistory - LumaFiltered);
|
|
// Remove the blur of small motion.
|
|
float EdgeFactor = min(abs(LumaMax - LumaFiltered), abs(LumaMin - LumaFiltered));
|
|
// Add in a constant factor to improve convergence speed with a small amount of noise.
|
|
Change.x = max(Change.x, 1.0/32.0);
|
|
#if (AA_DYNAMIC == 0) && (AA_CROSS == 0)
|
|
ChangeLimitLuma = EdgeFactor * Change.x;
|
|
#else
|
|
ChangeLimitLuma = max(ChangeLimitLuma, EdgeFactor * Change.x);
|
|
#endif
|
|
// Allow a minimal amount of change on still and motion.
|
|
ChangeLimitLuma = max(ChangeLimitLuma, 1.0/512.0);
|
|
// Target is filtered value clamped to +/- change limit.
|
|
float LumaTarget = clamp(LumaFiltered, LumaHistory - ChangeLimitLuma, LumaHistory + ChangeLimitLuma);
|
|
// Now perform the neighborhood clamp.
|
|
LumaTarget = clamp(LumaTarget, min(LumaFiltered, LumaClamped), max(LumaFiltered, LumaClamped));
|
|
// Offscreen feedback resets.
|
|
#if AA_LERP
|
|
float FixedLerp = 1.0/float(AA_LERP);
|
|
#endif
|
|
if(OffScreen)
|
|
{
|
|
OutColor = Filtered;
|
|
#if AA_ALPHA
|
|
OutColor.a = 1.0;
|
|
#endif
|
|
#if AA_LERP
|
|
FixedLerp = 1.0;
|
|
#endif
|
|
}
|
|
|
|
// DO FINAL BLEND BETWEEN HISTORY AND FILTERED COLOR
|
|
// -------------------------------------------------
|
|
#if (AA_LERP == 0)
|
|
// Switch luma back to linear.
|
|
LumaTarget = LinearLuma(LumaTarget);
|
|
LumaHistory = LinearLuma(LumaHistory);
|
|
LumaFiltered = LinearLuma(LumaFiltered);
|
|
float LumaDiff = LumaHistory - LumaFiltered;
|
|
float RcpLumaDiff = rcp(LumaDiff);
|
|
if(abs(LumaDiff) < (1.0/1024.0))
|
|
{
|
|
// If diff between frames is under a set limit, use new frame 100%.
|
|
RcpLumaDiff = 0.0;
|
|
}
|
|
float BlendFinal = saturate((LumaTarget - LumaFiltered) * RcpLumaDiff);
|
|
#if AA_ALPHA
|
|
// Blend in linear to hit luma target.
|
|
OutColor.rgb = lerp(Filtered.rgb, OutColor.rgb, BlendFinal);
|
|
#if RESPONSIVE
|
|
OutColor.a = max(OutColor.a, 1.0);
|
|
#endif
|
|
#else
|
|
OutColor = lerp(Filtered, OutColor, BlendFinal);
|
|
#if AA_FORCE_ALPHA_CLAMP
|
|
OutColor.a = clamp(OutColor.a, NeighborMin.a, NeighborMax.a);
|
|
#endif
|
|
#endif
|
|
#else
|
|
#if AA_HDR
|
|
OutColor = HdrLerp(Clamped, Filtered, FixedLerp, InExposureScale);
|
|
#else
|
|
OutColor = lerp(Clamped, Filtered, FixedLerp);
|
|
#endif
|
|
#endif
|
|
#if AA_NAN
|
|
// Transform NaNs to black, transform negative colors to black.
|
|
OutColor.rgb = -min(-OutColor.rgb, 0.0);
|
|
#endif
|
|
#if AA_DEBUG
|
|
OutColor.g = abs(DebugDiffPrior - DebugDiffCurrent);
|
|
OutColor.r = OutColor.a;
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
#undef AA_AABB
|
|
#undef AA_ALPHA
|
|
#undef AA_CROSS
|
|
#undef AA_DEBUG
|
|
#undef AA_DILATE
|
|
#undef AA_DYNAMIC
|
|
#undef AA_FILTERED
|
|
#undef AA_GRAIN
|
|
#undef AA_HDR
|
|
#undef AA_HDR_HISTORY
|
|
#undef AA_LERP
|
|
#undef AA_LOWPASS
|
|
#undef AA_ROUND
|
|
#undef AA_NAN
|
|
#undef AA_BORDER
|
|
#undef AA_FORCE_ALPHA_CLAMP
|
|
#undef AA_ONE_DYNAMIC_SAMPLE
|
|
#undef AA_GREEN_AS_LUMA
|
|
#undef AA_NEW |