FXAA白皮书的翻译和理解补充

FXAA

实现

1. 算法前瞻


算法流程参考上图,可以分为以下几步:

  1. 输入一张非线性RGB图(例如:sRGB),并从采样得到的颜色值中得到亮度luminance
  2. 通过亮度,检查当前像素的局部对比度local contrast),来舍弃非边缘像素。检测到的边缘为红色,向黄色渐变表示检测到的亚像素锯齿的程度。(drawn using FXAA_DEBUG_PASSTHROUGHshader define)。
  3. 通过局部对比度检测的像素被分类为金色的水平,和蓝色的垂直。(FXAA_DEBUG_HORZVERT)
  4. 根据当前像素的方向,选出和此方向垂直的(成 9 0 o 90^o 90o),且对比度最高的像素对,图中标记为蓝/绿。(FXAA_DEBUG_PAIR
  5. 沿边缘的负方向和正方向(红/蓝)搜索边缘的末端。检查沿边缘的高对比度像素对的平均亮度是否有明显变化。(FXAA_DEBUG_NEGPOS
  6. 给出边缘的两端边缘上的像素位置进行一个垂直于边缘 9 0 o 90^o 90o的子像素移动,以减少锯齿,红/蓝-/+水平移动金/天蓝-/+垂直移动。(FXAA_DEBUG_OFFSET
  7. 考虑这个子像素偏移量,对输入的纹理进行重新采样
  8. 最后,根据检测到的子像素锯齿的程度,加入一个低通滤波器

2. 亮度转换(过程1

直接使用RG通道进行mad操作——经验上,B通道锯齿很少出现在游戏中:

float FxaaLuma(float3 rgb) 
{
	return rgb.y * (0.587/0.299) + rgb.x;
}

3. 局部对比度检查(过程2

局部对比度检查使用东西南北四个邻域像素。如果局部最大和最小对比度之差小于阈值正比于最大局部对比度),则直接退出(不是边缘像素)。这个阈值应该进行clamp,避免太小,而错误的处理黑色区域

float3 rgbN  = FxaaTextureOffset(tex, pos.xy, FxaaInt2( 0,-1)).xyz; 
float3 rgbW  = FxaaTextureOffset(tex, pos.xy, FxaaInt2(-1, 0)).xyz; 
float3 rgbM  = FxaaTextureOffset(tex, pos.xy, FxaaInt2( 0, 0)).xyz; 
float3 rgbE  = FxaaTextureOffset(tex, pos.xy, FxaaInt2( 1, 0)).xyz; 
float3 rgbS  = FxaaTextureOffset(tex, pos.xy, FxaaInt2( 0, 1)).xyz; 
float lumaN  = FxaaLuma(rgbN); 
float lumaW  = FxaaLuma(rgbW); 
float lumaM  = FxaaLuma(rgbM); 
float lumaE  = FxaaLuma(rgbE); 
float lumaS  = FxaaLuma(rgbS); 
float rangeMin = min(lumaM, min(min(lumaN, lumaW), min(lumaS, lumaE))); 
float rangeMax = max(lumaM, max(max(lumaN, lumaW), max(lumaS, lumaE))); 
float range = rangeMax - rangeMin; 
if(range <  max(FXAA_EDGE_THRESHOLD_MIN, rangeMax * XAA_EDGE_THRESHOLD)) 
{ 
    return FxaaFilterReturn(rgbM); 
}

由此产生的、由艺术家控制的参数:

  • FXAA_EDGE_THRESHOLD:对比度检测阈值,参考值——1/3(too little),1/4(low quality),1/8(high quality),1/16(overkill)
  • FXAA_EDGE_THRESHOLD_MIN:最小阈值,参考值——1/32(visible limit),1/16(high quality),1/12(upper limit,start of visible unfiltered edges)

4. 亚像素锯齿测试(过程2

首先,像素对比度lumaL的计算方法是:通过一个低通滤波器得到平均值,然后减去中间亮度,意义上就是绝对差异像素对比度与局部对比度的比率被用来检测子像素锯齿。这个比率在单像素点存在的情况下接近1.0,而当更多的像素贡献于一个边缘时,开始下降趋于0.0。这个比率后续会拿来(最后一步)作为低通滤波器的强度

float lumaL = (lumaN + lumaW + lumaE + lumaS) * 0.25; 
float rangeL = abs(lumaL - lumaM); 
float blendL = max(0.0, (rangeL / range) - FXAA_SUBPIX_TRIM) * FXAA_SUBPIX_TRIM_SCALE;  
blendL = min(FXAA_SUBPIX_CAP, blendL);

在算法的最后,用于过滤子像素锯齿的低通滤波器是一个完整的**3x3BOX滤波器**。

// 已经采样好的,就直接进行黎曼和
float3 rgbL = rgbN + rgbW + rgbM + rgbE + rgbS; 
// ... 
// 经历了诸多过程,再来采样四个角落,凑出完整的3x3领域
float3 rgbNW = FxaaTextureOffset(tex, pos.xy, FxaaInt2(-1,-1)).xyz; 
float3 rgbNE = FxaaTextureOffset(tex, pos.xy, FxaaInt2( 1,-1)).xyz; 
float3 rgbSW = FxaaTextureOffset(tex, pos.xy, FxaaInt2(-1, 1)).xyz; 
float3 rgbSE = FxaaTextureOffset(tex, pos.xy, FxaaInt2( 1, 1)).xyz; 
rgbL += (rgbNW + rgbNE + rgbSW + rgbSE); 
rgbL *= FxaaToFloat3(1.0/9.0); 

由此产生的、由艺术家控制的参数:(除了关闭完全开启特性外,这些参数不会影响性能。默认情况下,亚像素锯齿的去除程度是有限的。这使得细微的特征得以保留,但对比度足够低,这样它们就不会分散眼睛的注意力。完全开启会使图像模糊。)

  • FXAA_SUBPIX:亚像素过滤的开关,参考值——0(关闭)、1(开启)、2(完全开启,忽略FXAA_SUBPIX_TRICAP
  • FXAA_SUBPIX_TRIM:控制亚像素锯齿的移除,参考值——1/2(low removal),1/3(medium removal),1/4(default removal),1/8(high removal),0(complete removal)
  • FXAA_SUBPIX_CAP:确保精细的细节不被完全删除。这部分覆盖了FXAA_SUBPIX_TRIM。参考值——3/4(default amount of filtering),7/8(high amount of filtering),1( no capping of filtering)

5. 水平/垂直边缘检测(过程3

Sobel这样的边缘检测滤波器在通过像素中心的单像素线上效果很差。FXAA局部3x3邻域行和列的高通值的加权平均幅度,作为局部边缘程度的指示:

float edgeVert =  
    abs((0.25 * lumaNW) + (-0.5 * lumaN) + (0.25 * lumaNE)) + 
    abs((0.50 * lumaW ) + (-1.0 * lumaM) + (0.50 * lumaE )) + 
    abs((0.25 * lumaSW) + (-0.5 * lumaS) + (0.25 * lumaSE)); 
float edgeHorz =  
    abs((0.25 * lumaNW) + (-0.5 * lumaW) + (0.25 * lumaSW)) + 
    abs((0.50 * lumaN ) + (-1.0 * lumaM) + (0.50 * lumaS )) + 
    abs((0.25 * lumaNE) + (-0.5 * lumaE) + (0.25 * lumaSE)); 
bool horzSpan = edgeHorz >= edgeVert;

那个话看似复杂,实际上很简单,这个还是得看示意图:

Todo,灵魂画师用平板画。

6. 边缘末端检测(过程5

给定局部边缘方向FXAA将选出和此方向垂直的(成 9 0 o 90^o 90o),且对比度最高的像素对。算法沿着正方向和负方向进行搜索,直到达到搜索极限,或者沿着边缘移动的pair平均亮度变化足以表示边缘结束。

在水平或垂直方向上平行搜索负方向和正方向单个循环)。这样做是为了避免在着色器中的分支。

搜索加速的原理(预设012):使用各向异性过滤作为盒式过滤器来检查不止一个像素对。

for(uint i = 0; i < FXAA_SEARCH_STEPS; i++) { 
    #if FXAA_SEARCH_ACCELERATION == 1 
        if(!doneN) lumaEndN = FxaaLuma(FxaaTexture(tex, posN.xy).xyz); 
        if(!doneP) lumaEndP = FxaaLuma(FxaaTexture(tex, posP.xy).xyz); 
    #else 
        if(!doneN) lumaEndN = FxaaLuma(FxaaTextureGrad(tex, posN.xy, offNP).xyz); 
        if(!doneP) lumaEndP = FxaaLuma(FxaaTextureGrad(tex, posP.xy, offNP).xyz); 
    #endif 
    doneN = doneN || (abs(lumaEndN - lumaN) >= gradientN); /*负方向*/
    doneP = doneP || (abs(lumaEndP - lumaN) >= gradientN); /*正方向*/
    if(doneN && doneP) break; 
    if(!doneN) posN -= offNP; 
    if(!doneP) posP += offNP; 
} 

由此产生的、由艺术家控制的参数:(这些定义对性能有最大的影响。注意,使用搜索加速可能会在边缘过滤中引起一些抖动。)

  • FXAA_SEARCH_STEPS:控制搜索步数的最大值。乘以FXAA_SEARCH_ACCELERATION过滤半径
  • FXAA_SEARCH_ACCELERATION:各向异性过滤加速搜索的程度,参考值——1(无加速)、2(跳过2个像素)、3(跳过3个)、4
  • FXAA_SEARCH_THRESHOLD:控制什么时候停止搜索,参考值——1/4(似乎是最佳选择)。此外,个人觉得,代码中的gradientN就和这个参数有关

7. 关于后续的过程456

首先,这三个过程其实主要是为了一个目的,那就是获得当前像素的偏移量。这个偏移量又和边缘的末端有什么关系呢?

在我看来,首先这个边缘越长,说明这个边越水平/垂直,因为我们采样就是按照标准方向来的,所以哪怕这个边实际上很长,但是如果它很斜的话,那么我们的循环也走不长。所以,我们可以得出第一个结论:两个端点距离越长,则偏移量越小。

但仅仅这样想,会有一个问题,那就是这个边真的很短,而且有真的很标准(完全垂直或水平),那么,我们此时就不能按照上诉想法,给它分配一个较大的偏移量。这个时候,就引出了第二个评判标准,那就是,这个像素如果离某一个端点很近,那么哪怕这个边不长,也应该给它分配一个小的偏移量

最后,给出每一步的意义:

  • 过程4:仅仅是为了获得最大对比度对,来作为第五步的截止条件。
  • 过程5:搜索得到两个端点。
  • 过程6:利用两个端点和上诉分析得到的两个规则,给当前像素一个偏移量。
  • 过程78:使用偏移量,重新确定当前像素的采样中心,然后根据亚像素锯齿程度来决定box过滤核的大小,进行最后的过滤。

8. 技术总结

就我个人感觉,FXAA的核心就是两个:

  • 重新确定采样核心位置。
  • 确定过滤核的半径。

9. 复现

Todo
Ps: 应付完老师再说吧。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Unity URP(Universal Rendering Pipeline)是Unity引擎中的一种渲染管线,它提供了更高效的渲染方式以及更大的灵活性。URP在渲染效果上有多种选项,其中之一就是FXAA(Fast Approximate Anti-Aliasing)。 FXAA是一种抗锯齿技术,旨在解决图像中出现的锯齿和边缘伪影问题。它通过对场景的渲染图像进行分析,找出锯齿边缘,并通过模糊处理来减少或消除锯齿的出现。FXAA的主要优点是快速且效果较好,适用于移动设备和性能要求较低的设备。 在Unity URP中使用FXAA可以改善游戏或应用程序的图像质量,使其更加平滑和清晰。要在Unity URP中启用FXAA,首先需要进入渲染管线的设置,选择URP渲染管线。在URP的设置中,可以找到与抗锯齿相关的选项,包括FXAA。启用FXAA后,Unity会在渲染图像时应用FXAA算法来减少锯齿和边缘伪影。 使用FXAA还可以减少GPU的工作负载,因为FXAA是在帧缓冲区中进行后处理,而不是在每个对象上进行抗锯齿计算。这样可以降低在渲染大量复杂对象时的性能瓶颈,并提高应用程序的帧率和性能。 综上所述,Unity URP中的FXAA是一种快速且有效的抗锯齿技术,可以显著改善游戏或应用程序的图像质量。通过启用FXAA,用户可以获得更平滑、更清晰的图像,并提高应用程序的性能。 ### 回答2: Unity URP(Universal Render Pipeline)是Unity引擎中的渲染管线,它提供了高性能和高质量的图形渲染能力。URP基于可编程渲染管线(SRP)框架,可以让开发者根据自己的需求定制渲染管线,以实现更好的视觉效果和性能表现。URP采用了轻量级和可配置的方式,适用于多种平台和设备。 FXAA(Fast Approximate Anti-Aliasing)是一种抗锯齿技术,它通过对图像进行模糊处理来减少锯齿和马赛克等锯齿状边缘。 在Unity URP中使用FXAA可以实现更平滑、更真实的图形效果。使用FXAA抗锯齿可以有效减少锯齿状边缘,让图像更加清晰和平滑。 要在Unity URP中使用FXAA,首先需要在项目中启用URP渲染管线,然后在相机设置中启用FXAA抗锯齿。可以通过调整FXAA的参数来控制抗锯齿的效果,例如锯齿的影响范围、锐化的程度等等。 使用FXAA抗锯齿时需要注意一些问题,比如对于特定场景和效果可能会产生一些模糊或失真。因此,在使用时需要根据实际情况进行调整。此外,如果需要更高质量的抗锯齿效果,还可以尝试其他更高级的抗锯齿技术,如SMAA(Subpixel Morphological Anti-Aliasing)或TAA(Temporal Anti-Aliasing)。 总之,Unity URP中的FXAA抗锯齿技术可以帮助开发者在游戏或应用中实现更好的图形效果和视觉质量。 ### 回答3: Unity URP(Universal Render Pipeline)是Unity游戏引擎中的一种渲染管线,它是针对轻量级游戏和应用程序开发的优化渲染解决方案。URP可帮助开发者在保持游戏性能的同时实现高质量的视觉效果。 而FXAA(Fast Approximate Anti-Aliasing)是一种抗锯齿技术,它通过在渲染图像前对像素进行采样和平滑处理,减少锯齿和边缘走样现象,从而提供更加平滑的图像。 在Unity URP中,FXAA可用作一种抗锯齿的解决方案。通过在项目的渲染设置中启用FXAA选项,可以简单地应用FXAA抗锯齿效果。启用FXAA后,Unity URP会在渲染过程中对图像进行采样和平滑处理,以减少锯齿和边缘走样现象,并提供更加平滑的图像输出。 使用FXAA的好处是,它能够在保持较高的帧率和性能的同时提供较好的图像质量。因为FXAA是一种快速近似的抗锯齿技术,相对于一些更复杂的抗锯齿算法(如MSAA或SSAA),它需要较少的计算资源,并可以在大多数较低规格的硬件上运行。 综上所述,Unity URP中的FXAA是一种通过采样和平滑处理来减少锯齿和边缘走样现象的抗锯齿技术。它能够在保持较高性能的同时提供较好的图像质量,是一种适用于轻量级游戏和应用程序开发的简单而有效的抗锯齿解决方案。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值