文章目录
FSR技术分析
1. SIGGRAPH课程分析
1.1 源码介绍
FSR源码核心是两个文件:
ffx_a.h
:Portability header(可移植头文件)ffx_fsr1.h
:核心算法集合:EASU
:边缘自适应空间升频(Edge Adaptive Spatial Upsampling )RCAS
:鲁棒的对比度自适应锐化(Robust Contrast Adaptive Sharpening)LFGA
:Linear Film Grain ApplicatorSRTM
:Simple Reversible TonemapperTEPD
:Temporal Energy Preserving Dither
1.2 EASU简单介绍
1️⃣EASU最好的描述是:一个局部自适应的椭圆Lanczos式滤波器。Lanczos
是一种重采样算法,是一个sinc函数。这是一个理论上最佳的重建滤波器。
EASU需要有良好的抗锯齿输入作为基础——它本身并不是一个抗锯齿的解决方案,因此它需要良好的抗锯齿来作为输入。
2️⃣EASU
使用的是一个如下所示的**12-tap
的窗口来进行空间升频**:
使用
12 tap
的好处之一,就是如果我们使用Float32
,那么我们只需要32
个寄存器来存储: 1 6 r e g i s t e r = 1 2 t a p ∗ 3 r g b 16_{register}=12_{tap}*3_{rgb} 16register=12tap∗3rgb。由后续算法可知, 窗口内的数据都要使用两次,与其每次都读取纹理,不如直接存储下来使用,这也是一种==以空间换取时间==了。
读取得到RGB数据,和很多DLAA
算法类似,需要转换成luma
,这里的转换算法非常简单:
float RGB2Luma(float3 rgb)
{
return rgb.x + 2.0 * rgb.y + rgb.z;
}
对于这个**12-tap
的窗口**,算法会进行四次+
类型的分析,如下所示:
3️⃣每次+
类型分析,其实就是边缘检测和特征长度计算(Feature length
),关于后者:EASU
会忽略单像素特征,也就是完全翻转(例如:黑-白-黑,滤波器核变得对称和无方向性),而关心部分翻转和不翻转。如下图所示:
此外,EASU
最好运行在感知空间(例如:sRGB
、Gamma 2.0
),线性空间虽然也可以,但效果会变差。
4️⃣正如之前所说,+
分析之后,我们会得到两个结果:
direction
:每个+
类型分析的结果有三个类型:水平、垂直、对角线。我们要进行四次分析,对四次分析结果进行双线性插值,得到最终的方向。我们根据这个方向来旋转过滤核。length
:长度用于在X
和Y
轴上对旋转后的内核进行缩放。
对于缩放,更加准确的算法解释是:
X
轴:缩放区间是 [ 1 , 2 ] [1,\sqrt{2}] [1,2],对应的是从轴对齐到对角线。这意味着:对角线情况下使用更大的内核,来避免带状伪影(band
)。Y
轴:缩放区间是 [ 1 , 2 ] [1,2] [1,2],对应的是从small feature
到large feature
。这意味着:对小的特征使用无比例,这样就得到了一个小的对称核,它不会在特征本身之外采样。而当特征变大时,使用一个较长的核,这样我们可以更好地还原边缘。
5️⃣正如一开始所说,算法使用的过滤核是lanczos
核,但是原始内核的计算太过昂贵,所以EASU
使用了多项式近似:
[
25
16
(
2
5
x
2
−
1
)
2
−
(
25
16
−
1
)
]
(
w
x
2
−
1
)
2
[\frac{25}{16}(\frac{2}{5}x^2-1)^2-(\frac{25}{16}-1)](wx^2-1)^2
[1625(52x2−1)2−(1625−1)](wx2−1)2
其中,w
可以看作是控制窗口的基数。以下图为例子:当
w
=
1
/
4
w=1/4
w=1/4时,内核是宽的,范围是
[
−
2
,
2
]
[-2,2]
[−2,2];当
w
=
1
/
2
w=1/2
w=1/2时,内核是窄的,范围是
[
−
2
,
2
]
[-\sqrt{2},\sqrt{2}]
[−2,2]。
2. EASU源码分析
2.1 FsrEasuCon分析
根据官方信息,我们可以知道:在CPU
端,我们需要调用此函数,来设置EASU
算法所需的四个const vec4
常量,我们接下来分析这些常量都填充了什么。
1️⃣首先是con0
,它的第一个和第二个成员,填充的是输入/输出的分辨率比:
c
o
n
0.
x
y
=
i
n
p
u
t
V
i
e
w
p
o
r
t
I
n
P
i
x
e
l
s
o
u
t
p
u
t
S
i
z
e
I
n
P
i
x
e
l
s
con0.xy=\frac{inputViewportInPixels}{outputSizeInPixels}
con0.xy=outputSizeInPixelsinputViewportInPixels
它的第三个和第四个成员,则是使用了类似
[
0
,
1
]
→
[
−
1
,
1
]
[0,1]\rightarrow [-1,1]
[0,1]→[−1,1] 的映射公式:
c
o
n
0.
z
w
=
0.5
∗
i
n
p
u
t
V
i
e
w
p
o
r
t
I
n
P
i
x
e
l
s
o
u
t
p
u
t
S
i
z
e
I
n
P
i
x
e
l
s
−
0.5
con0.zw=0.5 *\frac{inputViewportInPixels}{outputSizeInPixels}-0.5
con0.zw=0.5∗outputSizeInPixelsinputViewportInPixels−0.5
2️⃣在分析其他常量之前,我们需要参考如下图片:
然后直接看看代码(分析也在代码处给了):
// Note :
// ARcpF1(x) : 1/x
// AU1_AF1 : 打包函数
// AF1_ : 类型转换函数
// This is used to get upper-left of 'F' tap.
// 所以采样原点是`F`tap的左上方那个空白处
con1[0] = AU1_AF1(ARcpF1(inputSizeInPixelsX));
con1[1] = AU1_AF1(ARcpF1(inputSizeInPixelsY));
// Centers of gather4, first offset from upper-left of 'F'
// 根据后续代码可以知道,con1.zw是用来以`F`为原点,来定位`(0)`处
con1[2] = AU1_AF1(AF1_( 1.0) * ARcpF1(inputSizeInPixelsX));
con1[3] = AU1_AF1(AF1_(-1.0) * ARcpF1(inputSizeInPixelsY));
// These are from (0) instead of 'F'.
// 依据注释,以上四个偏移是以(0)为原点的
// 下面依次是 : (1) (2) (3) (0)的位置
con2[0] = AU1_AF1(AF1_(-1.0) * ARcpF1(inputSizeInPixelsX));
con2[1] = AU1_AF1(AF1_( 2.0) * ARcpF1(inputSizeInPixelsY));
con2[2] = AU1_AF1(AF1_( 1.0) * ARcpF1(inputSizeInPixelsX));
con2[3] = AU1_AF1(AF1_( 2.0) * ARcpF1(inputSizeInPixelsY));
con3[0] = AU1_AF1(AF1_( 0.0) * ARcpF1(inputSizeInPixelsX));
con3[1] = AU1_AF1(AF1_( 4.0) * ARcpF1(inputSizeInPixelsY));
con3[2] = con3[3] = 0;
ToDo:暂时先不管了,先来看之后的源码吧
2.2 FidelityFXSuperResolution分析
根据PostProcessFFX_FSR.usf
的MainPs
函数,我们可以知道EASU
算法的实现是在FidelityFXSuperResolution()
中的,现在让我们来分析它。
AF4 FidelityFXSuperResolution(AU2 gxy)
{
// FSR / EASU()
AF3 Gamma2Color = AF3(0,0,0);
FsrEasuF(Gamma2Color, gxy, Const0, Const1, Const2, Const3);
AF3 OutColor = AF3(Gamma2Color);
// Grain()
// if RCAS & ChromAb are not running, we will apply Grain Intensity post-upscale here.
// App should disable this path based on pass inputs on the CPU-side;
#if USE_GRAIN_INTENSITY
AF3 LinearColor = Gamma2ToLinear(AF3(Gamma2Color));
ApplyUE4Grain(LinearColor, gxy, VPColor_ExtentInverse);
#endif // USE_GRAIN_INTENSITY
// 一些颜色空间的变换操作
...
return AF4(OutColor, 1);
}
仔细一看,其实核心也是调用的函数:FsrEasuF
。我们暂且不管ApplyUE4Grain
,而直接去分析FsrEasuF
。
3. 之后
主要就是分析FsrEasuF
,希望秋招一切顺利,然后有时间我会尽快更新之后的 [源码分析(二)] 和 [源码分析(三)]