目录算法 |
1 设置场景数组 1.1 测试场景微信 1.2 超采样函数 1.3 后处理性能 2 亮度测试 2.1 计算亮度优化 2.2 支持亮度数据url 2.3 采样亮度spa 3 混合高对比度像素.net 3.1 肯定与相邻像素的对比度 3.2 跳太低对比度像素 3.3 计算混合因子 3.4 混合方向 3.5 融合 4 沿着边混合 4.1 边亮度 4.2 沿着边走 4.3 沿着两个方向 4.4 肯定混合因子 4.5 质量 4.6 性能 5 颜色空间 5.1 LDR 5.2 Gamma 收起 |
本文重点:
一、计算图像亮度
二、查找高对比度像素
三、识别对比度边缘
四、选择性混合
五、搜索边的终点
本教程介绍了如何建立FXAA后期效果。它是接着“景深”教程以后发布的。
本教程使用Unity 2017.3.0p3制做。
(掌握FXAA的艺术以对抗锯齿和萤火虫)
1 设置场景
由于显示的分辨率有限。结果,与像素网格不对齐的图像特征会出现锯齿。对角线和弯曲线显示为梯度,一般称为锯齿。细线可能会断开链接并变成虚线。小于或小于一个像素的高对比度功能有时会出现,有时却不会出现,致使物体移动时闪烁,一般称为萤火虫。实际上,已经开发了一系列抗锯齿技术来缓解这些问题。本教程介绍了经典的FXAA解决方案。
(细线及其光栅化后的表现)
1.1 测试场景
在本教程中,我建立了一个相似于“景深”中的测试场景。它包含高对比度和低对比度区域,较亮和较暗的区域,多个笔直和弯曲的边缘以及一些小的特征。和往常同样,咱们使用HDR和线性色彩空间。全部场景屏幕截图都会放大,以使单个像素更易于区分。
(测试场景 放大四倍 没有任何抗锯齿)
1.2 超采样
消除混叠的最直接方法是以高于显示器的分辨率进行渲染并对其进行下采样。这是一种空间抗锯齿的方法,它能够捕获和平滑对于显示器而言较高的子像素特征。
(在双倍的分辨率下采样,而后平均2X2的块)
超级采样抗锯齿(SSAA)就是这样作的。至少,场景要以最终分辨率的两倍渲染到缓冲区,并平均四个像素的块以生成最终图像。甚至可使用更高的分辨率和不一样的采样模式来进一步改善效果。这种方法消除了锯齿,但也会稍微模糊整个图像。
(SSAA 2倍)
尽管SSAA有效,但它是一种暴力的方法,很是昂贵。将分辨率加倍,内存和阴影中的像素数量的存储须要同时增长三倍。尤为是填充率将会成为瓶颈。为了减轻这种状况,引入了多样本抗混叠(MSAA)。它还能够实现更高的分辨率和更高的下采样率,可是会改变片断的渲染方式。它不是简单地渲染高分辨率块的全部片断,而是每一个三角形渲染一个覆盖该块的单个片断,从而有效地将结果复制到高分辨率像素。这样可使填充率易于管理。这也意味着仅三角形的边缘会受到影响,其余全部内容均保持不变。这也是为何MSAA不会平滑经过cutout 材质建立的透明边的缘由。
(MSAA 2倍和8倍)
MSAA表现良好而且使用较多,可是它须要大量内存,而且不会与依赖深度缓冲区的效果(例如延迟渲染)结合在一块儿。这就是为何许多游戏选择不一样的抗锯齿技术的缘由。
那CSAA呢?
CSAA是指覆盖采样抗锯齿(coverage sampling anti-aliasing)。它是MSAA的变体,但我在这里不作详细介绍。
1.3 后处理
执行抗锯齿的第三种方法是经过后期效果处理。这些是与其余效果同样的全屏Pass,所以它们不须要更高的分辨率,但可能依赖于临时渲染纹理。这些技术必须以最终分辨率工做,所以它们没法访问实际的子像素数据。相反,他们必须分析图像并根据该结果进行选择性地模糊。
选择须要应用多种后处理技术了。第一个是形态抗锯齿(morphological anti-aliasing )(MLAA)。在本教程中,咱们将建立咱们本身的快速近似抗锯齿(FXAA)版本。它是由NVIDIA的Timothy Lottes开发的,其功能恰如其名。与MLAA相比,它以质量换取速度。尽管FXAA的一个常见问题是它模糊的太多,但这取决于使用哪一种变体以及如何对其进行调整。咱们将建立最新版本FXAA 3.11,针对PC的高质量版本。
对于新的FXAA着色器,咱们将使用与DepthOfField着色器相同的设置。你能够复制它并将其减小为仅执行一次blit的单个通道。
再次使用与景深效果相同的方法,建立一个最小的FXAAEffect组件。
设置组件脚本的默认着色器引用。
(默认的着色器引用)
将咱们的新效果做为惟一效果附加到相机上。再次假设咱们在线性HDR空间中进行渲染,所以请相应地配置项目和摄影机。另外,因为咱们执行本身的抗锯齿功能,所以请确保已禁用MSAA。
(HDR摄像机 不使用MASS 而使用FXAA)
我仍然在场景视图中看到MSAA吗?
场景观察相机使用质量设置中的MSAA设置,如今这种状况下,它不会模仿主相机。
2 亮度
FXAA经过选择性下降图像对比度,平滑视觉上明显的锯齿和孤立的像素来起做用。经过比较像素的光强度来肯定对比度。像素的确切颜色可有可无,重要的是它们的亮度。实际上,FXAA可在仅包含像素亮度的灰度图像上工做。这意味着,当它们的亮度类似时,不一样颜色之间的硬过渡不会被平滑不少。仅在视觉上明显的过渡会受到严重影响。
2.1 计算亮度
让咱们从检查此单色亮度图像的外观开始。因为绿色份量对像素的亮度影响最大,所以能够经过简单地使用它来建立快速预览,而无需丢弃红色和蓝色数据。
(使用绿色通道做为亮度)
这是亮度的粗略近似。若是可以适当地计算亮度就最好了,为此,咱们可使用UnityCG的LinearRgbToLuminance函数。
FXAA但愿亮度值在0–1范围内,可是在使用HDR颜色时不能保证这一点。一般,抗锯齿是在色调映射和颜色分级以后进行的,应该消除大多数(即便不是所有)HDR颜色。可是咱们在本教程中不使用这些效果,而是使用钳位的颜色来计算亮度。
(亮度)
LinearRgbToLuminance是什么样子的?
它是颜色通道的简单加权总和,绿色是最重要的。
2.2 支持亮度数据
FXAA不会自行计算亮度。这是很是昂贵的,由于每一个像素须要多个亮度样本。取而代之的是,必须经过更早的Pass将亮度数据放入alpha通道中。另外,当因为某种缘由没法使用Alpha通道时,FXAA可使用绿色做为亮度。使用FXAA时,Unity的后期效果堆栈v2两种方法都支持。
咱们也支持这两个选项,可是因为咱们不使用后期效果堆栈,所以也须要支持本身计算亮度。在FXAAEffect中添加枚举字段以控制此字段,而后在检查器中将其设置为Calculate。
(luminance Source ,设置为calculate)
当咱们必须本身计算亮度时,咱们将单独进行一次处理,将原始RGB加亮度数据存储在临时纹理中。而后,实际的FXAA通道将使用该纹理而不是原始源。此外,FXAA通道须要知道是使用绿色仍是Alpha通道来得到亮度。咱们将经过LUMINANCE_GREEN shader关键字进行指示。
咱们能够将现有的通道用于亮度通道。惟一的变化是亮度应存储在alpha通道中,并保留原始RGB数据。新的FXAA通道以简单的blit通道开始,并带有LUMINANCE_GREEN的多编译选项。
2.3 采样亮度
要应用FXAA效果,咱们必须对亮度数据进行采样。这是经过对主要纹理进行采样并选择其绿色或Alpha通道来完成的。为此,咱们将建立一些便捷的功能,并将其所有放在着色器文件顶部的CGINCLUDE块中。
如今,咱们的FXAA通道能够仅将片断的纹理坐标做为参数来调用ApplyFXAA函数。
3 混合高对比度像素
FXAA经过混合高对比度像素来完成工做。但这不是直接模糊图像。首先,必须计算局部对比度。其次,若是有足够的对比度,则必须根据对比度选择混合因子。第三,必须研究局部对比度梯度以肯定混合方向。最后,在原始像素与其相邻像素之间执行混合。
3.1 肯定与相邻像素的对比度
经过将当前像素的亮度与其相邻像素的亮度进行比较,能够找到局部对比度。为了使对邻居像素的采样变得容易,添加一个SampleLuminance函数变量,该变量具备以像素为单位的U和V坐标的偏移参数。这些应经过纹理像素大小进行缩放,并在采样前添加到uv中。
FXAA使用直接的水平和垂直邻居以及中间像素自己来肯定对比度。由于咱们将屡次使用此亮度数据,因此将其放在LuminanceData结构中。咱们将使用罗盘方向来引用邻居数据,使用North表示正V,使用East表示位置U,使用South表示负V,使用West表示负U。对这些像素进行采样并在单独的函数中初始化亮度数据,而后调用 在ApplyFXAA中。
(NESW十字和中间像素)
南北不该该互换吗?
我使用的是OpenGL约定,UV坐标从左到右,从下到上。FXAA算法虽然并不关心相对方向,但必须保持一致。
这些像素之间的局部对比度只是它们最高和最低亮度值之间的差别。因为亮度在0-1范围内定义,所以对比度也是如此。咱们在取样后当即计算出最低,最高和对比度值。将它们添加到结构中,以便咱们稍后能够在ApplyFXAA中访问它们。对比度是最重要的,因此咱们看看它是什么样的。
(局部对比)
结果就像一个粗糙的边缘检测滤波器。由于对比度不关心方向,因此对比度不一样的两侧的像素最终都具备相同的值。所以,咱们获得的边缘至少有两个像素厚,由南北像素或东西向像素对造成。
3.2 跳太低对比度像素
让咱们经过对比度阈值滑块进行配置哪些区域不用执行抗锯齿。原始的FXAA算法也具备此阈值,而且具备如下代码文档:
尽管文档中提到它会修剪深色区域,但其实是根据对比度而不是亮度进行修剪,所以不管它是亮仍是暗。咱们将使用与文档中指示的范围相同的范围,但默认使用低阈值。
(对比度阈值)
在着色器内部,若是对比度低于阈值,则只需在对邻域采样后返回便可。为了使跳过的像素在视觉上显而易见,我将它们设置为红色。
(红色像素被跳过)
除了绝对对比度阈值之外,FXAA还具备相对阈值。这是它的代码文档:
这听起来像咱们刚刚介绍的阈值,可是在这种状况下,它是基于邻域的最大亮度的。邻域越亮,对比度就必须越高。咱们还将为该相对阈值添加一个配置滑块,使用指示的范围,一样将最小值做为默认值。
(相对对比度阈值)
阈值是相对的,由于它由对比度缩放得出来的。使用该值而不是先前的阈值来查看差别。此次,我用绿色表示跳过的像素。
(绿色像素被跳过)
整体而言,“Contrast Threshold”最积极地跳过像素,可是“Relative Threshold ”能够跳过较亮区域中的较高对比度像素。例如,在下面的屏幕截图中,我将两种颜色都与两种阈值进行了最大组合。黄色表示同时使用两个标准跳过的像素。在此场景中,只有一些白色阴影区域和白色球体仅受相对阈值的影响。
(两种阈值 最大值)
要应用两个阈值,只需将对比度与两个最大值进行比较便可。为了清楚起见,将此比较放在单独的函数中。目前,若是跳过像素,只需返回零便可将其变为黑色。
(对比度,跳过0像素)
3.3 计算混合因子
如今咱们有了所需像素的对比度值,能够继续肯定混合因子。为此建立一个单独的函数,以亮度数据做为参数,并使用该函数肯定最终结果。
咱们应该混合多少取决于中间像素及其整个邻域之间的对比度。尽管咱们已经使用NEWS十字来肯定局部对比度,但这不足以表示邻域。咱们还须要四个对角邻居。所以,将它们添加到亮度数据。即便咱们最终可能会跳过像素,也能够在SampleLuminanceNeighborhood和其余邻居中直接对它们进行采样。着色器编译器负责优化代码,所以仅在须要时才进行额外采样。
(所有邻居)
如今咱们能够肯定全部相邻邻居的平均亮度。可是,因为对角邻点在空间上距离中间较远,所以它们的做用应该较小。咱们经过将NESW邻居的权重加倍,将总数除以12而不是8来将其计入平均值。结果相似于账篷(Tent)滤波器,并充当low-pass滤波器。
(邻居权重)
(高对比度区域上的低通滤波器)
接下来,经过它们的绝对差找到中间值与该平均值之间的对比度。结果如今变成了high-pass滤波器。
(高通滤波器)
接下来,经过除法相对于NESW十字的对比度对滤镜进行归一化。将结果限制为最大值1,由于滤镜覆盖的像素比十字形要多,咱们可能会获得更大的值。
(归一化后的滤波器)
结果是用做混合因子的过渡至关艰难。使用smoothstep函数对其进行平滑处理,而后对结果求平方以使其变慢。
(线性 VS 平方 平滑步长)
(混合因子)
3.4 混合方向
如今咱们有了混合因子,下一步就是肯定要混合的两个像素。FXAA将中间像素与NESW十字中的一个相邻像素混合。选择这四个像素中的哪一个取决于对比度梯度的方向。在最简单的状况下,中间像素触及两个对比区域之间的水平或垂直边缘。在水平边缘的状况下,它应该是北邻仍是南邻,这取决于中间是在边缘之下仍是之上。不然,它应该是东边的邻居或者西边的邻居,这取决于中间位置是在边缘的左侧仍是右侧。
(混合方向。红色表明亮度差别,深色或浅色)
边缘一般不是彻底水平或垂直,可是咱们会选择最佳近似值。为了肯定这一点,咱们比较邻域中的水平和垂直对比度。当存在水平边缘时,在中间上方或下方会产生强烈的垂直对比度。咱们经过加北和南,减去中间两次并取其绝对值来进行测量,所以| n + s-2m |。相同的逻辑适用于垂直边缘,但使用东西向代替。
这仅给咱们指示了NESW十字内部的垂直对比度。咱们也能够经过包括对角线邻居来提升边缘方向检测的质量。对于水平边缘,咱们对东边的三个像素和西边的三个像素执行相同的计算,将结果相加。一样,这些附加值离中间值很远,所以咱们将它们的相对重要性减半。得出最终公式 2 | n + s-2m | + | ne + se-2e | + | nw + sw-2w | 用于水平边缘对比度,而用于垂直边缘对比度。咱们不须要对结果进行归一化,由于咱们只关心哪一个更大,它们都使用相同的比例。
若是水平边缘的对比度大于或等于垂直边缘,则咱们有一个水平边缘。建立一个结构以保存此边缘数据,并将其计算结果放在单独的函数中。而后让ApplyFXAA调用它。这使咱们能够可视化检测到的边缘方向,例如经过将水平边缘设为红色。
(红色像素在水平边缘)
知道边缘方向能够告诉咱们须要混合的尺寸。若是它是水平的,那么咱们将在边缘垂直融合。到UV空间中下一个像素的距离取决于纹理像素大小,而且取决于混合方向。所以,咱们也将此步长添加到边缘数据中。
接下来,咱们必须肯定是否应在正向或负向融合。经过在适当尺寸的中间任一侧比较对比度(亮度梯度)来作到这一点。若是咱们有一条水平边,那么北是正邻,南是负邻。若是咱们有一条垂直边,那么东是正邻,西是负邻。
比较渐变。若是正面具备最高的对比度,则可使用适当的纹理像素大小不变。不然,咱们必须朝相反的方向走,须要取反。
为了实现这一目标,我将全部带有负步长的像素设为红色。由于像素应该在边缘上融合,因此这意味着边缘右侧或顶部的全部像素都变为红色。
(红色像素沿负方向融合)
3.5 融合
此时,咱们既有混合因子,又知道混合的方向。经过使用混合因子在适当方向上在中间像素及其相邻像素之间线性插值,能够得到最终结果。咱们能够经过简单地以等于由混合因子缩放的像素步长的偏移量采样图像来实现此目的。另外,若是咱们决定不混合原始像素,请确保返回原始像素。我将原始亮度保留在Alpha通道中,以防你想将其用于其余用途,但这不是必需的。
请注意,最终样本最终会在四个可能的方向上发生偏移,而且距离可变,而每一个像素之间的距离可能会有很大差别。这弄乱了各向异性纹理过滤和mipmap选择。虽然咱们不使用mipmaps做为临时纹理,而且一般也没有其余后效果来作这个,但咱们也没有明确禁用各向异性过滤,所以可能会使最终样本走样失真。为确保不该用大量透视过滤,请使用tex2Dlod并不使用Sample调整进行纹理访问,取代使用tex2D。
(有和没有混合)
结果是使用FXAA子像素混合的抗锯齿图像。它会影响高对比度边缘,但也会影响纹理中的许多低对比度细节。虽然这有助于减轻萤火虫的影响,但模糊程度过多。该效果的强度能够经过0-1范围因子进行调整,以调制最终偏移。原始的FXAA实现也容许这样作,并带有如下代码文档:
为子像素融合添加一个滑块以达到咱们的效果。咱们将使用全强度做为默认值,Unity的后期效果堆栈v2也将使用此默认值,而且它不容许你进行调整。
(子像素融合滑动条)
使用_SubpixelBlending调制混合因子,而后再将其返回到DecisionPixelBlendFactor中。如今,咱们能够控制FXAA效果的强度。
(调整混合数量)
4 沿着边混合
因为像素混合因子是在3×3块内肯定的,所以只能平滑该比例的特征。可是边缘能够比这更长。像素可能会终止于成角度的边楼梯的长台阶上的某个位置。虽然局部边缘是水平或者垂直的,但真正的边是带有角度的。若是咱们知道此真实边,则能够更好地匹配相邻像素的混合因子,从而在整个边上进行平滑。
(没有,当前以及所需的边融合)
4.1 边亮度
为了弄清楚咱们正在处理哪一种边,必需要追踪更多信息。咱们知道3×3块的中间像素在边的一侧,其余像素之一在另外一侧。为了进一步识别边,咱们须要知道其梯度,即其任一侧区域之间的对比度差别。咱们已经在DefineEdge中弄清楚了这一点。让咱们跟踪此渐变以及另外一侧的亮度。
咱们将使用一个单独的函数来肯定边的新混合因子。如今,在肯定边后当即返回它,跳过着色器的其他部分。还将跳过的像素设置回零。首先,咱们将输出边缘渐变。
(边渐变)
4.2 沿着边走
咱们必须找出沿水平或垂直边缘段的像素的相对位置。为此,咱们将向两个方向沿着边走,直到找到终点为止。能够经过沿边采样像素对并检查其对比度梯度是否仍与原始边缘的对比度梯度想匹配来实现。
(搜索边缘的末端)
可是咱们实际上并不须要每一个步骤都对两个像素进行采样。能够用它们之间的单个样原本作。这给了咱们刚好在边缘的像素的平均亮度,咱们能够将其与第一个边缘交叉进行比较。
(搜索时纹理样本(黄色)和3×3样本(黑色))
所以,咱们首先肯定边缘上的UV坐标,该距离距离原始UV坐标只有半个步长。
接下来,沿着边缘的单个步骤的UV偏移量取决于其方向。它能够是水平的或垂直的。
咱们将经过比较沿边行走时采样的亮度与原始边缘位置的亮度(这是咱们已经拥有的亮度对的平均值)来找到终点。若是发现的亮度与原始亮度足够类似,那么仍然处于边缘,必须继续前进。若是相差太大,说明已经到了尽头。
咱们将经过沿边缘获取亮度增量(采样亮度减去原始边缘亮度)并检查其是否知足阈值来执行此比较。做为阈值,FXAA使用原始梯度的四分之一。在正方向上执行此步骤,明确追踪亮度增量以及是否到达边缘末端。经过显示白色展现哪些像素与其正边缘相邻,其余则为黑色。
(一步步走向正端点)
咱们能够看到,孤立的像素如今大部分都是白色的,可是沿较长角度线的一些像素仍然是黑色的。它们距离局部水平或垂直边缘的正端点比一步还远。对于这些像素,必须继续沿着边缘走。所以,请在第一个搜索步骤以后添加一个循环,最多执行九次,每一个像素最多能够执行十个步长。
(升级到10个步长)
如今,咱们能够找到距十个像素远的正端点,而且在示例屏幕快照中几乎全部像素都变为白色。经过获取相关的UV增量并将其放大10倍来可视化UV空间到端点的距离。
(正端距离,最多十个像素)
4.3 沿着两个方向
沿边缘还有一个负方向的端点,所以也请使用示例方法搜索该端点。而后,最终距离变为正距离和负距离中的最短距离。
(到最近边缘的距离)
4.4 肯定混合因子
如今,咱们知道到边缘最近端点的距离(若是它在范围内),能够用来肯定混合因子。咱们将在距离终点越近的地方融合更多,从而使阶梯变得平滑。可是,咱们只会在边缘向包含中间像素的区域倾斜的方向上执行此操做。经过比较沿边缘的亮度增量和沿边缘的亮度增量的符号来发现这一点。
(选择正确的一面)
若是增量方向相反,那么咱们会远离边,应使用零混合因子来跳过混合。这样能够确保咱们仅在缘的一侧混合像素。
(只有正确的一边才有像素)
若是咱们有一个有效的像素可用于混合,则咱们将混合0.5倍减去到沿边缘到最近端点的相对距离。这意味着咱们越靠近端点就进行更多的混合,而根本不会在边缘的中间进行混合。
(边缘融合因子)
要了解经过此方法发现了哪些边缘(仅考虑3×3区域时会丢失的),请从边混合因子中减去像素混合因子。
(经过边因子添加混合)
FXAA的最终混合因子只是两个混合因子的最大值。所以,它始终使用边缘混合因子,而且能够经过滑块控制像素混合因子的强度。
(边混合VS原始图像)
(带有子像素混合 0 vs 1)
4.5 质量
如今,咱们老是在搜索多达十次迭代以找到边的终点。这在许多状况下就足够了,但对于阶梯步长超过十个像素宽的那些边缘来讲,还不够。若是最终找不到边缘,那么终点在更远的地方。若是不收集更多样本,咱们所能作的就是猜想末端到底有多远。这至少要相距一步,所以在这种状况下,咱们能够再增长一次UV偏移。会永远更加准确。
除此以外,咱们能够改变咱们要采起的步骤。还能够更改步长大小,以精确度为代价跳过像素来检测更长的边。也能够不使用恒定的步长,经过在数组中定义步长来增长步长。最后,咱们能够调整用于猜想太大距离的偏移量。让咱们使用宏定义这些设置,以使着色器变体可以支持。
原始FXAA算法包含质量定义列表。Unity的后期效果堆栈v2使用默认质量等级28。它的步数为10,第二步为1.5,而不是1,全部后续步数为2,最后一步为4,若是还不够找到终点,最后的猜想会加上8。
经过一次包含一个半像素偏移,咱们最终从那开始在相邻像素对之间进行采样,一次处理四个像素而不是两个像素的平均值。这不是很准确,可是可使用步长2而不跳过像素。
(边缘融合因子,可实现28像素质量步距与单像素图像步距)
与使用单像素搜索步骤相比,混合因子可能会更阻塞而且质量有所降低,可是做为回报,短边须要较少的搜索迭代,而长边也能够被检测到。
(边缘融合的结果是质量为28仍是单像素为步长)
在FXAA通道中添加一个多编译选项。
和一个Toggle来控制它。
(低质量,仅边缘融合)
4.6 性能
最后,让咱们考虑一下性能。循环并不理想。原始的FXAA代码显式展开了循环,其中包含一长串嵌套的if语句。幸运的是,咱们不须要这样作。咱们能够简单地经过UNITY_UNROLL属性告诉着色器编译器为咱们执行此操做。就我所知,展开循环能够显着提升性能,即便没有这种优化,FXAA仍然很是快。
除此以外,原始的FXAA代码还将两个循环组合为一个循环,并在两个方向上以锁步方式进行搜索。每次迭代,只有还没有完成的方向前进并再次采样。在某些状况下,这可能会更快,但在咱们的例子里,单独的循环要比融合的循环执行得更好。
若是检查原始的FXAA代码(版本3.11),你会发现它主要由积极的低级优化主导。除了使代码难以阅读以外,这些优化现在可能再也不有意义。在咱们的案例中,着色器编译器实际上为咱们处理了全部此类优化,这比咱们本身能作的更好。激进的手动优化可能会使状况更糟。
Unity的后期效果堆栈v2几乎逐字的使用了FXAA 3.11代码,但就咱们的例子来讲,本教程中介绍的更清晰的版本实际上表现更好。与之前同样,若是要得到绝对最佳的性能,请针对每一个项目,每一个目标平台本身进行测试。
5 颜色空间
在此测试场景中,咱们在线性空间中使用HDR照明,并将渲染的图像直接馈送到FXAA,而无需进行任何颜色调整。这可能不会产生最佳结果。
5.1 LDR
尽管咱们在计算用于肯定混合因子的亮度时已经对颜色进行了钳位,可是实际上并无对在混合时所使用的RGB通道进行钳位。这意味着咱们最终能够混合HDR颜色。当HDR颜色成分混合在一块儿时,咱们看不到差别,由于结果仍然超出最终显示范围。这不是问题,由于替代方案将是1到1之间的混合。可是,当混合LDR和HDR数据时,这就会成为问题。若是HDR组件很是亮,则结果也将被拉入HDR。这会将本来的LDR像素带入HDR范围,可能会增长锯齿而不是减小锯齿。
提供LDR数据是提供FXAA通道输入者的责任。在咱们的例子里,在使用亮度通道时能够完成这一点。
(LDR VS HDR 混合)
5.2 Gamma
着色时使用线性空间,由于它能够产生物理上正确的照明。可是,FXAA是关于感知的,而不是物理的。如原始FXAA代码中所述,与在γ空间中进行混合相比,在线性空间中进行混合会产生视觉上较差的结果。Unity的后期效果堆栈v2仅在线性空间中混合,所以结果还不错。可是咱们能够支持这两种方法。
当咱们假设是线性空间渲染时,请为咱们的效果添加一个伽玛空间混合切换开关,该开关可控制shader关键字。
若是须要,在亮度Pass中,将颜色转换为伽马空间,而后再输出。若是跳过此通道的话,则谁提供FXAA输入,谁来确保颜色在伽玛空间中。
在FXAA通道结束时,一旦得到了最终样本,则在适当的状况下将其转换回线性空间。咱们必须这样作,由于渲染管线假定输出在线性空间中。
(Gamma VS Linear 混合)
最后,咱们能够按照设计的默认设置重现FXAA效果。这些设置为“Contrast Threshold 0.0833”,“Relative Threshold 0.166”,“Subpixel Blending 0.75”,而且同时启用了“Low Quality”和“Gamma Blending”。
(具备原始默认设置的FXAA VS 锯齿化的图像)
固然,这只是配置FXAA的许多方法之一。你能够根据须要进行调整。你可能会决定仅适用边混合,并彻底不须要任何子像素混合。在这种状况下,您能够经过删除DefinePixelBlendFactor 函数(多是着色器变体)来加快效果。并且,当在游戏中提供FXAA选项时,你能够将其某些设置公开给播放器,而不只仅是FXAA开关。
本文分享自微信公众号 - 壹种念头(OneDay1Idea)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。