UE5.32抖动半透明兼容TAA时序超分算法改进
常规的抖动半透明使用噪声(Pattern)抖动Alpha Mask,通过镂空的方式,搭配TAA模拟半透明。
经常会出现一些Artifact:
- 空洞。
- Pattern的Clip破坏了时序抖动序列的空域连续性,速度、深度和颜色都出现了断层,导致后续TAA经常无法识别出边界区域,极易产生拖尾和鬼影。
- 镂空的区域通过快速抖动方式模拟填充,极易出现能量损失,让画面看起来黯淡不少,同时也能看到廉价的抖动模式。
(其实不仅仅是TAA,包括DOF、运动模糊等依赖速度和深度的效果基本都会出错。)
改进的抖动半透明通过缓存历史帧并重建的做法,能完美避免这些问题:
下面是一组对比图:
渲染需要抖动半透的角色时,按照2x2的Pattern做时序抖动(使用2x2的Pattern可以帮助我们在两帧之间就能拿到全部的场景信息)。
float computeOpacity(float2 PixelPos)
{
const int2 PosMod = int2(PixelPos) % 2;
const bool bSpecialPos = (PosMod.x + PosMod.y == 1);
return ((View.FrameNumber & 1u) == 0u) ? bSpecialPos : (!bSpecialPos);
}
然后将上一帧的渲染颜色缓存下来。此时我们有如下两张图:
从中可以提取出前景色和背景色,然后我们将抖动区域的Alpha写到GBuffer或者GPUScene中,在Shader中读取后,就可以模拟出Alpha半透明混合了:
如何判断是chessboard半透明区域?
读取上一帧相同位置的Alpha值,然后与当前帧的Alpha值比较,若存在一帧有Alpha那么就是Chessboard半透区域。
注:通常情况下抖动半透明是逐Primitive的Alpha改变,可以存储当前帧的Alpha和上一帧的Alpha到GPUScene中,再通过GBuffer里的PrimitiveID拿到两帧互补的Alpha做判断。
Texture2D CurrentSceneColorTexture;
Texture2D PrevSceneColorTexture;
shader
{
// ...
const bool bCurrentPixelNoCull = IsJitter(CurrentAlpha);
const bool bPrevPixelNoCull = IsJitter(PrevAlpha);
// No pixel jittering so skip this area.
if((!bCurrentPixelNoCull) && (!bPrevPixelNoCull))
{
out = CurrentColor;
return;
}
// Load prev color.
const float4 PrevColor = PrevSceneColorTexture.load();
// Composition scene color.
if (bCurrentPixelNoCull)
{
const float Alpha = RemapAlpha(CurrentAlpha);
out = lerp(PrevColor, CurrentColor, Alpha);
}
else
{
const float Alpha = RemapAlpha(PrevAlpha);
out = lerp(CurrentColor, PrevColor, Alpha);
}
}
在填充完颜色后,也不要忘记对深度和速度做孔洞填充防止TAA鬼影(此处我们不使用上一帧的速度和速度来填充空洞,而是直接拿当前帧的速度和速度图,扩张到隔壁空洞去)。
由于Pattern固定,可以知道应该采样像素的位置:
float2 ChessboardOffsetPos(float2 PixelPos)
{
const int2 PosMod = int2(PixelPos) % 2;
// (0, 0) -> (1, 0) = ( 1, 0) 0.
// (1, 0) -> (0, 0) = (-1, 0) 1.
// (0, 1) -> (1, 1) = ( 1, 0) 2.
// (1, 1) -> (0, 1) = (-1, 0) 3.
static const float2 kOffset2x2[4] =
{
float2( 1, 0),
float2(-1, 0),
float2( 1, 0),
float2(-1, 0),
};
return kOffset2x2[PosMod.x + PosMod.y * 2];
}
速度和深度的孔洞填充如下:
虽然有些拉伸,但提供给TAA或者其他时域超分算法,用来防止拖尾,已经是足够了。
此时,由于重建了完整的速度和速度,颜色也支持混合在了一起,角色抖动的行为类似于绘制了速度的半透明物体,能正常在超分、DOF和运动模糊中渲染。
PS: 这是一个Dead Tech。在发现了抖动半透明的问题后,我在一个空闲的业余时间,在太阳落山的傍晚完成了开发,但是因为种种原因,现在(并且未来也)没有任何人,任何地方会有使用上它,XD。