Adjust the sharpness of TAA's Catmull-Rom filter based on local area luminance (#3400)
* Adjust the sharpness of TAA's Catmull-Rom filter based on local area luminance. This helps prevent ringing artifacts in high contrast areas. Due to Catmull-Rom's use of negative weights, it's possible to have a very high value neighbor sample with a negative weight completely overwhelm the result leading to a negative output. This change reduces the sharpness of the catmull rom filter in high contrast areas so the negative weights contribute less to the final result. Signed-off-by: Ken Pruiksma <pruiksma@amazon.com> * PR review feedback fixes - more comments and updated the way the sharpness equation was written for clarity. Signed-off-by: Ken Pruiksma <pruiksma@amazon.com>
This commit is contained in:
@@ -99,11 +99,23 @@ float3 YCoCgToRgb(float3 yCoCg)
|
||||
// This function samples a 4x4 neighborhood around the uv. By taking advantage of bilinear filtering this can be
|
||||
// done with only 9 taps on the edges between pixels. The cost is further reduced by dropping the 4 diagonal
|
||||
// samples as their influence is negligible.
|
||||
float4 SampleCatmullRom5Tap(Texture2D<float4> texture, SamplerState linearSampler, float2 uv, float2 textureSize, float2 rcpTextureSize, float sharpness)
|
||||
float3 SampleCatmullRom5Tap(Texture2D<float4> texture, SamplerState linearSampler, float2 uv, float2 textureSize, float2 rcpTextureSize, float sharpness)
|
||||
{
|
||||
// Think of sample locations in the 4x4 neighborhood as having a top left coordinate of 0,0 and
|
||||
// a bottom right coordinate of 3,3.
|
||||
|
||||
// Due to Catmull-Rom's use of negative weights, it's possible to have a very high value
|
||||
// neighbor sample that turns into a large negative value when multiplied by its weight.
|
||||
// This can completely overwhelm the final result leading to negative output. To fix this,
|
||||
// use the local area green value as a measurement of luminance, and tweak the sharpness
|
||||
// based on the difference between the max and min values. This will reduce the contribution
|
||||
// of negative weights in high contrast areas and help prevent ringing artifacts.
|
||||
float4 gatheredGreen = texture.GatherGreen(linearSampler, uv, int2(0, 0));
|
||||
float minGreen = min(gatheredGreen.x, min(gatheredGreen.y, min(gatheredGreen.z, gatheredGreen.w)));
|
||||
float maxGreen = max(gatheredGreen.x, max(gatheredGreen.y, max(gatheredGreen.z, gatheredGreen.w)));
|
||||
float diff = maxGreen - minGreen;
|
||||
sharpness = sharpness / (diff + 1.0);
|
||||
|
||||
// Find the position in texture space then round it to get the center of the 1,1 pixel (tc1)
|
||||
float2 texelPos = uv * textureSize;
|
||||
float2 tc1= floor(texelPos - 0.5) + 0.5;
|
||||
@@ -117,7 +129,6 @@ float4 SampleCatmullRom5Tap(Texture2D<float4> texture, SamplerState linearSample
|
||||
float2 w1 = 1.0 + f * f * (c -3.0 + (2.0 - c) * f);
|
||||
float2 w2 = f * (c + f * ((3.0 - 2.0 * c) - (2.0 - c) * f));
|
||||
float2 w3 = f * f * (c * f - c);
|
||||
|
||||
float2 w12 = w1 + w2;
|
||||
|
||||
// Compute uv coordinates for sampling the texture
|
||||
@@ -135,12 +146,12 @@ float4 SampleCatmullRom5Tap(Texture2D<float4> texture, SamplerState linearSample
|
||||
// total weight of samples to normalize result.
|
||||
float totalWeight = sw0 + sw1 + sw2 + sw3 + sw4;
|
||||
|
||||
float4 result = 0.0f;
|
||||
result += texture.SampleLevel(linearSampler, float2(tc12.x, tc0.y), 0.0) * sw0;
|
||||
result += texture.SampleLevel(linearSampler, float2( tc0.x, tc12.y), 0.0) * sw1;
|
||||
result += texture.SampleLevel(linearSampler, float2(tc12.x, tc12.y), 0.0) * sw2;
|
||||
result += texture.SampleLevel(linearSampler, float2( tc3.x, tc12.y), 0.0) * sw3;
|
||||
result += texture.SampleLevel(linearSampler, float2(tc12.x, tc3.y), 0.0) * sw4;
|
||||
float3 result = 0.0;
|
||||
result += texture.SampleLevel(linearSampler, float2(tc12.x, tc0.y), 0.0).rgb * sw0;
|
||||
result += texture.SampleLevel(linearSampler, float2( tc0.x, tc12.y), 0.0).rgb * sw1;
|
||||
result += texture.SampleLevel(linearSampler, float2(tc12.x, tc12.y), 0.0).rgb * sw2;
|
||||
result += texture.SampleLevel(linearSampler, float2( tc3.x, tc12.y), 0.0).rgb * sw3;
|
||||
result += texture.SampleLevel(linearSampler, float2(tc12.x, tc3.y), 0.0).rgb * sw4;
|
||||
|
||||
return result / totalWeight;
|
||||
}
|
||||
@@ -217,7 +228,7 @@ void MainCS(
|
||||
float2 previousPositionOffsetInPixels = float2(PassSrg::m_constantData.m_inputColorSize) * previousPositionOffset;
|
||||
|
||||
// Sample the last frame using a 5-tap Catmull-Rom
|
||||
float3 lastFrameColor = SampleCatmullRom5Tap(PassSrg::m_lastFrameAccumulation, PassSrg::LinearSampler, uvOld, PassSrg::m_constantData.m_inputColorSize, PassSrg::m_constantData.m_inputColorRcpSize, 0.5).rgb;
|
||||
float3 lastFrameColor = SampleCatmullRom5Tap(PassSrg::m_lastFrameAccumulation, PassSrg::LinearSampler, uvOld, PassSrg::m_constantData.m_inputColorSize, PassSrg::m_constantData.m_inputColorRcpSize, 0.5);
|
||||
lastFrameColor = RgbToYCoCg(lastFrameColor);
|
||||
|
||||
// Last frame color relative to mean
|
||||
@@ -250,8 +261,8 @@ void MainCS(
|
||||
|
||||
// Blend should be in perceptual space, so tonemap first
|
||||
float luminance = GetLuminance(thisFrameColor);
|
||||
thisFrameColor = thisFrameColor / (1 + luminance);
|
||||
lastFrameClampedColor = lastFrameClampedColor / (1 + luminance);
|
||||
thisFrameColor = thisFrameColor / (1.0 + luminance);
|
||||
lastFrameClampedColor = lastFrameClampedColor / (1.0 + luminance);
|
||||
|
||||
// Blend color with history
|
||||
float3 color = lerp(lastFrameClampedColor, thisFrameColor, currentFrameWeight);
|
||||
|
||||
Reference in New Issue
Block a user