I've been experimenting with full-screen AA techniques, and DLAA looked like a promising technique from what the developers said and the results in TFU2.
I had been looking for details on the this technique for a while but there isn't much out there, so I've set out to uncover the technique. Here are my results:
The technique is based on detecting short and long - vertical and horizontal edges.
1) The first pass does a simple color average space edge detection.
2) The second pass does localized edge detection and blurs directionally (vertically and horizontally).
Next it determines if the current edge is a long one, in which case it does a wider vertical and horizontal direction blur
that's about it.
The following pixel shader produces pretty much the same output as the 360 version of TFU2.
Performance is around ~3.2 ms in a X360 class GPU.
#define PIXEL_SIZE float2(1.0/1280, 1.0/720)
float4 sampleOffseted(const in sampler2D tex, const in float2 texCoord, const float2 pixelOffset )
{
return tex2D(tex, texCoord + pixelOffset * PIXEL_SIZE);
}
float3 avg(const in float3 value)
{
static const float oneThird = 1.0 / 3.0;
return dot(value.xyz, float3(oneThird, oneThird, oneThird) );
}
float4 firsPassEdgeDetect( float2 texCoord : TEXCOORD0 ) : COLOR
{
float4 sCenter = sampleOffseted(Texture0, texCoord, float2( 0.0, 0.0) );
float4 sUpLeft = sampleOffseted(Texture0, texCoord, float2(-0.5, -0.5) );
float4 sUpRight = sampleOffseted(Texture0, texCoord, float2( 0.5, -0.5) );
float4 sDownLeft = sampleOffseted(Texture0, texCoord, float2(-0.5, 0.5) );
float4 sDownRight = sampleOffseted(Texture0, texCoord, float2( 0.5, 0.5) );
float4 diff = abs( ((sUpLeft + sUpRight + sDownLeft + sDownRight) * 4.0) - (sCenter * 16.0) );
float edgeMask = avg(diff.xyz);
return float4(sCenter.rgb, edgeMask);
}
float4 secondPassEdgeDetectAndBlur( float2 texCoord : TEXCOORD0 ) : COLOR
{
// short edges
float4 sampleCenter = sampleOffseted(Texture0, texCoord.xy, float2( 0.0, 0.0) );
float4 sampleHorizNeg0 = sampleOffseted(Texture0, texCoord.xy, float2(-1.5, 0.0) );
float4 sampleHorizPos0 = sampleOffseted(Texture0, texCoord.xy, float2( 1.5, 0.0) );
float4 sampleVertNeg0 = sampleOffseted(Texture0, texCoord.xy, float2( 0.0, -1.5) );
float4 sampleVertPos0 = sampleOffseted(Texture0, texCoord.xy, float2( 0.0, 1.5) );
float4 sumHoriz = sampleHorizNeg0 + sampleHorizPos0;
float4 sumVert = sampleVertNeg0 + sampleVertPos0;
float4 diffToCenterHoriz = abs( sumHoriz - (2.0 * sampleCenter) ) / 2.0;
float4 diffToCenterVert = abs( sumHoriz - (2.0 * sampleCenter) ) / 2.0;
float valueEdgeHoriz = avg( diffToCenterHoriz.xyz );
float valueEdgeVert = avg( diffToCenterVert.xyz );
float edgeDetectHoriz = saturate( (3.0 * valueEdgeHoriz) - 0.1);
float edgeDetectVert = saturate( (3.0 * valueEdgeVert) - 0.1);
float4 avgHoriz = ( sumHoriz + sampleCenter) / 3.0;
float4 avgVert = ( sumVert + sampleCenter) / 3.0;
float valueHoriz = avg( avgHoriz.xyz );
float valueVert = avg( avgVert.xyz );
float blurAmountHoriz = saturate( edgeDetectHoriz / valueHoriz );
float blurAmountVert = saturate( edgeDetectVert / valueVert );
float4 aaResult = lerp( sampleCenter, avgHoriz, blurAmountHoriz );
aaResult = lerp( aaResult, avgVert, blurAmountVert );
// long edges
float4 sampleVertNeg1 = sampleOffseted(Texture0, texCoord.xy, float2(0.0, -3.5) );
float4 sampleVertNeg2 = sampleOffseted(Texture0, texCoord.xy, float2(0.0, -7.5) );
float4 sampleVertPos1 = sampleOffseted(Texture0, texCoord.xy, float2(0.0, 3.5) );
float4 sampleVertPos2 = sampleOffseted(Texture0, texCoord.xy, float2(0.0, 7.5) );
float4 sampleHorizNeg1 = sampleOffseted(Texture0, texCoord.xy, float2(-3.5, 0.0) );
float4 sampleHorizNeg2 = sampleOffseted(Texture0, texCoord.xy, float2(-7.5, 0.0) );
float4 sampleHorizPos1 = sampleOffseted(Texture0, texCoord.xy, float2( 3.5, 0.0) );
float4 sampleHorizPos2 = sampleOffseted(Texture0, texCoord.xy, float2( 7.5, 0.0) );
float pass1EdgeAvgHoriz = ( sampleHorizNeg2.a + sampleHorizNeg1.a + sampleCenter.a + sampleHorizPos1.a + sampleHorizPos2.a ) / 5.0;
float pass1EdgeAvgVert = ( sampleVertNeg2.a + sampleVertNeg1.a + sampleCenter.a + sampleVertPos1.a + sampleVertPos2.a ) / 5.0;
pass1EdgeAvgHoriz = saturate( pass1EdgeAvgHoriz * 2.0f - 1.0f );
pass1EdgeAvgVert = saturate( pass1EdgeAvgVert * 2.0f - 1.0f );
float longEdge = max( pass1EdgeAvgHoriz, pass1EdgeAvgVert);
if ( longEdge > 0 )
{
float4 avgHorizLong = ( sampleHorizNeg2 + sampleHorizNeg1 + sampleCenter + sampleHorizPos1 + sampleHorizPos2 ) / 5.0;
float4 avgVertLong = ( sampleVertNeg2 + sampleVertNeg1 + sampleCenter + sampleVertPos1 + sampleVertPos2 ) / 5.0;
float valueHorizLong = avg(avgHorizLong.xyz);
float valueVertLong = avg(avgVertLong.xyz);
float4 sampleLeft = sampleOffseted(Texture0, texCoord.xy, float2(-1.0, 0.0) );
float4 sampleRight = sampleOffseted(Texture0, texCoord.xy, float2( 1.0, 0.0) );
float4 sampleUp = sampleOffseted(Texture0, texCoord.xy, float2( 0.0, -1.0) );
float4 sampleDown = sampleOffseted(Texture0, texCoord.xy, float2( 0.0, 1.0) );
float valueCenter = avg(sampleCenter.xyz);
float valueLeft = avg(sampleLeft.xyz);
float valueRight = avg(sampleRight.xyz);
float valueTop = avg(sampleUp.xyz);
float valueBottom = avg(sampleDown.xyz);
float4 diffToCenter = valueCenter - float4(valueLeft, valueTop, valueRight, valueBottom);
float blurAmountLeft = saturate( 0.0 + ( valueVertLong - valueLeft ) / diffToCenter.x );
float blurAmountUp = saturate( 0.0 + ( valueHorizLong - valueTop ) / diffToCenter.y );
float blurAmountRight= saturate( 1.0 + ( valueVertLong - valueCenter ) / diffToCenter.z );
float blurAmountDown = saturate( 1.0 + ( valueHorizLong - valueCenter ) / diffToCenter.w );
float4 blurAmounts = float4( blurAmountLeft, blurAmountRight, blurAmountUp, blurAmountDown );
blurAmounts = (blurAmounts == float4(0.0, 0.0, 0.0, 0.0)) ? float4(1.0, 1.0, 1.0, 1.0) : blurAmounts;
float4 longBlurHoriz = lerp( sampleLeft, sampleCenter, blurAmounts.x );
longBlurHoriz = lerp( sampleRight, longBlurHoriz, blurAmounts.y );
float4 longBlurVert = lerp( sampleUp, sampleCenter, blurAmounts.z );
longBlurVert = lerp( sampleDown, longBlurVert, blurAmounts.w );
aaResult = lerp( aaResult, longBlurHoriz, pass1EdgeAvgVert);
aaResult = lerp( aaResult, longBlurVert, pass1EdgeAvgHoriz);
}
return float4(aaResult.rgb, 1.0f);
}
Hope it helps.