5.4 锯齿与抗锯齿——Aliasing and Antialiasing
想象一个大的黑色三角面片在白色背景上缓慢移动。屏幕网格被三角面片覆盖,网格的像素值应该平滑变化强度。一般基础渲染的顺序是网格中心北覆盖,像素突然从白色变成黑色。标准GPU渲染不希望这样。看看下图最左边的情况。
三角形显示的像素要么在那边或者不在那边。画出来的先有一个相似的问题,边缘有锯齿感,这种显示问题就叫做锯齿(the jaggies),动起来的时候就像小爬虫(the crawlies)。更通常下被称作aliasing,用于避免这种情况的技术则是antialiasing技术。
取样理论以及数字滤波的主题足够自己出本书了。作为渲染领域的核心之一,基础的采样以及滤波理论我们将会提及。我们会专注于目前在实时渲染领域我们可以做什么来减轻走样问题。
Gonzalez, Rafael C., and Richard E. Woods, Digital Image Processing, Third Edition, Addison-Wesley, 2007. Cited on p. 130, 543, 661
Proakis, John G., and Dimitris G. Manolakis, Digital Signal Processing: Principles, Algorithms, and Applications, Fourth Edition, Pearson, 2006. Cited on p. 130, 133, 135, 136
Szeliski, Richard, Computer Vision: Algorithms and Applications, Springer, 2011. Cited on p. 130, 200, 543, 549, 587, 661, 1048
5.4.1 取样以及滤波理论——Sampling and Filtering Theory
渲染图像处理天然就是一个采样问题。这是因为产生三维场景的图片就是为了获取图片中每一个像素的颜色值(离散像素数组)。为了使用贴图映射(章节6)图素必须在不同的情况下重采样以获取较好的结果。为了生成一系列的动画图像,动画经常在固定时间采样。这一节将会介绍采样、重建、滤波的主题。为了简化,大部分的材质都会是一维的。这些概念在二维中也是起作用的,并且可以因此用于处理二维图像。
下图显示了如何将连续信号采样到固定间隔的数据,也就是离散的。这个采样的目标是将数据数字化。通过这种方式信息量可以下降。然而信号采样需要重建来恢复原有数据,这个就是通过滤波来采样信号。
无论采样什么时候完成,走样就有可能发生。这是我们不想得到的结果,我们需要与走样抗争来得到理想的图片。一个经典的走样例子就是在老西部片老电影摄像机拍摄出来的滚动的马车轮。因为辐条的移动远远快鱼摄像机的图片记录速度,轮子也许看上去滚动变慢(向前或者向后),或者看上去完全没有在转。这个可以从下图看到。这个效果发生是因为轮子的图片在一系列时间戳中被记录,称作时间域走样(temporal aliasing)。
计算机图形中的一般走样是长线条以及三角片边缘的锯齿,高光闪烁则是称作“fireflies”,贴图随距离变小的时候也会发生(6.22节)【when a texture with a checker pattern is minified】。
走样发生在信号频率过低的情况下。采样信号显得比原始的更低。这可以在下图中看到。
为了能够正确进行采样(这样才有可能从采样中重构出原始信号)信号频率必须大于最大信号频率的两倍。这经常被称作采样定理(sampling theorem)以及这样的采样频率则称作Nyquistrate或者Nyquist limit(奈奎斯特极限),在Harry Nyquist(1889-1976)之后,一个瑞典科学家于1928年发现。
Proakis, John G., and Dimitris G. Manolakis, Digital Signal Processing: Principles, Algorithms, and Applications, Fourth Edition, Pearson, 2006. Cited on p. 130, 133, 135, 136
奈奎斯特极限也在上面轮子的图中显示了。实际上定理使用了最大频率的术语意味着信号必须是有限带宽的。意味着没有任何频率可以超过这个限制。另一句话说,信号在相邻采样的空间必须足够平滑。
在三维场景在点采样的时候一般从来不是有带宽上线的。三角片的边缘,阴影边缘,以及其他不连续产生信号的现象产生的频率是无限的。
Chan, Eric, and Fr´edo Durand, “Fast Prefiltered Lines,” in Matt Pharr, ed., GPU Gems 2, Addison-Wesley, pp. 345–359, 2005. Cited on p. 133
当然,无论多么将采样设置得多么紧密,物体总是有足够小的空间来让我们无法采样。因此通过点渲染来完全避免走样是不可能的,而且我们几乎总是用点采样。然而有时我们是有可能知道什么时候信号是有带宽限制的。例如当一个贴图应用到表面,计算贴图采样频率与像素的采样频率是可能做到的。如果这个频率低于Nyquist limit,就不需要特殊的操作来采样贴图。如果频率太高,有很多算法来限制贴图(6.2.2节)。
重建——Reconstruction
给予一个有带宽限制的采样信号,我们现在讨论如何将原有信号从采样信号中还原回来。为了做到这个,滤波器是必须用到的,常用的三种滤波器如下图所示。
需要注意的是滤波器的范围总是1,否则重建信号有可能变高或者变低。
在上图中,Box滤波器用于重建一个采样信号,这个是最差的滤波器,因为结果是非连续的阶梯状。不过,它经常用于计算机图形,因为其简单性。从图像中可以看到,box滤波器在每个采样点防止,然后进行缩放,其最高点被考虑为采样点。这些box的几何作为重构出来的信号,如上图右侧所示。
box滤波器可以被其他滤波器替代,如下图。
tent滤波器,也被称作三角滤波器,用于重构采样信号。注意这个滤波器在采样点之间实现线性插值,所以比Box滤波器更好,因为重构的信号是连续的了。
然而三角滤波器的平滑度很差。突然的斜率变化。这个导致了三角滤波器并不是最好的解决方案。为了得到最佳的重构方案,低筒滤波被使用。信号的频率组件是sine波:Sin(2πf),f是组件的频率。通过这个方式低通滤波器移除了高于当前频率的频率组件。可以直观看到低通滤波器移除了所有尖锐的特点,低筒滤波器的概念是sinc滤波。
Fourier的理论分析讨论了为什么sinc滤波是理想的低通滤波。
Proakis, John G., and Dimitris G. Manolakis, Digital Signal Processing: Principles, Algorithms, and Applications, Fourth Edition, Pearson, 2006. Cited on p. 130, 133, 135, 136
简单来说,原因如下。理想低通滤波是一个频率范围内的box滤波器,乘以信号的时候移除了所有频率高于频率宽度高于利波器的频率。box滤波器从频率范围变换到空间域使用了一个sinc函数。在同时乘法操作变换到了convolution(卷积)函数,在这一节我们用到的,并没有实际描述的概念。
使用sinc滤波器来重建信号可以得到更平滑的结果,就像下图显示的一样
采样过程描述了信号的高频组成,而低通滤波器则是移除他们。实际上,sinc滤波器使用高于二分之一采样率来消除所有sine波。在sinc函数中评率为1.0的时候才是完美的重建滤波(最大的信号采样频率必须小于二分之一)。更一般来说,假设采样评率是fs,那么相邻间隔是1/fs。在这种情况下,最完美的重建滤波是sinc(fsx)并且消除了所有高于fs/2的频率。这个对于重采样信号而言非常有用。然而,sinc的滤波器宽度是有限的,并且在一些区域是负数,所以在实际运用中很少起到作用。
有一个有用的中间区域是,低质量box和三角滤波器一方面,然后无法使用的sinc滤波器在另一侧。最广泛使用的滤波器方程在这两端之间。
Mitchell, D., and A. Netravali, “Reconstruction Filters in Computer Graphics,” Computer Graphics (SIGGRAPH ’88 Proceedings), vol. 22, no. 4, pp. 239–246, Aug. 1988. Cited on p. 136
Novosad, Justin, “Advanced High-Quality Filtering,” in Matt Pharr, ed., GPU Gems 2, Addison-Wesley, pp. 417–435, 2005. Cited on p. 136, 517, 521
Pharr, Matt, Wenzel Jakob, and Greg Humphreys, Physically Based Rendering: From Theory to Implementation, Third Edition, Morgan Kaufmann, 2016. Cited on p. 136, 144, 145, 165, 271, 442, 445, 512, 589, 623, 630
Turkowski, Ken, “Filters for Common Resampling Tasks,” in Andrew S. Glassner, ed., Graphics Gems, Academic Press, pp. 147–165, 1990. Cited on p. 136
所有这些滤波函数,有一些sinc函数的近似,但是有着影响多少像素的限制。最接近sinc函数的滤波器有负值。应用程序中滤波器负值是期望外以及没法用的,一般会使用无负值的滤波器(经常称作高斯滤波器,因为他们衍生自高斯曲线又与高斯曲线相近)。
Pettineo, Matt, “Experimenting with Reconstruction Filters for MSAA Resolve,” The Danger Zone blog, Oct. 28, 2012. Cited on p. 136, 142
12.1节讨论了滤波器函数以及他们更多的使用细节。
在使用了任意的滤波器之后,一个连续的信号形成了,然而在计算机图形中,我们不能显示一个连续信号,但是我们可以对它们进行重采样成离散信号。扩张信号或者缩短信号,下面我们就会讨论它。
重采样
重采样用于放大或者缩小采样信号。假设原有信号落在整数坐标上(0,1,2…)也就是采样单位间隔。进一步,我们假设重采样我们希望新的采样点落在一个间隔a的常量距离上。如果a > 1,缩小采样(downsampling),而a < 1则放大采样(upsampling)。
放大采样相交而言更简单一些。假设采样信号像前一节一样被重构了。直观地因为采样信号完美重构成连续的了,那么我们需要做的就是对重建信号进行重采样以我们希望的间隔,这个过程可以在下图看到。
然而这个方法不能在缩小信号时使用。原始频率对于采样率而言太高而无法避免走样。它通过sinc(x/a)的滤波从采样点创建了一个连续的信号。
Proakis, John G., and Dimitris G. Manolakis, Digital Signal Processing: Principles, Algorithms, and Applications, Fourth Edition, Pearson, 2006. Cited on p. 130, 133, 135, 136
Smith, Alvy Ray, Digital Filtering Tutorial for Computer Graphics, Technical Memo 27, revised Mar. 1983. Cited on p. 136
在这之后,用一个希望的间隔重采样。可以在下图看到。
换句话说,使用sinc(x/a)的方式进行滤波,低通滤波器的宽度增加了,所以更多的信号内容被移除了。如图所示,滤波宽度加倍了,用于降低重采样频率到只有一半的初始采样频率。这表现到图像上面类似于模糊,重采样的图片会处于一个更低的质量中。
通过采样以及滤波器的知识,多种用于降低实时渲染走样的方法将在下面描述。
5.4.2 基于屏幕的反走样 —— Screen-Based Antialiasing
如果采样或者滤波的方式不对的话也有可能造成可察觉的显示错误。阴影边缘,高光,以及其他颜色快速改变的地方都可能会出现相似的问题。这一节提到的算法可以帮助我们针对这些情况改进我们的渲染质量。他们都是基于屏幕实现的,也就是说他们只针对管线输出的结果进行操作。没有最好的范走样技术,不同的技术方案有着不同的优点,在质量,细节捕捉或者其他现象,移动时候的细节,内存的消耗,GPU要求以及速度。
在上面黑色三角形图片的例子中,其中一个问题是低采样率。一个单独的采样来自于每个网格中间进行的一次采样。这样就能知道一个网格点是否被三角形遮挡。每个网格更高的采样并且进行混合能够计算出更好的颜色。就像下图所示的一样。44
基于屏幕的范走样侧率是用一个采样模式来获取像素权重,最后算出一个像素的颜色,p:
其中n则是每个像素的采样数。方程c(i, x, y)是一个采样颜色,而wi则是权重,在0~1之间,这些采样值会一起对像素颜色进行描述。采样位置一般基于数字序列,可选择的方程用于将这个数字转换为一个具体的位置。换句话来说,在一个屏幕网格中的采样需要在不同的位置,对于不同像素的采样方式也可以多种多样。在渲染系统中的采样一般是点采样(大部分其他的渲染系统也是一样)。所以方程c可以看做两个方程,第一个方程f(i, n)取到一个具体的点(xf, yf)在一个需要采样的屏幕点上。然后在通过这个位置从屏幕上取回想要的颜色。这种采样方法被选择并且用于计算每一个子像素的位置,一般基于每帧设置。
另外一个反走样的参数是wi也就是每一个采样的权重。这些权重总和为1.大部分的方法用于实时渲染系统,通过给与一个常量权重,例如wi=1/n。对于硬件默认的模式,一个像素中间的但采样,是最简单的反走样模式。这个采样的权重也是1,采样函数f也总是返回像素中央的采样值。
每个像素计算多于一个完整采样的反走样算法叫做超采样(supersampling或者oversampling)方法。概念上最简单,full-scene antialiasing (FSAA),也被称作“supersampling antialiasing”(SSAA),用更高的分辨率渲染场景,并且进行临近采样创建一个图片。举个例子,希望得到一张1280×1024的图片,如果你用2560×2048的屏幕外图片然后通过2×2的像素区域进行平均,那么每个像素就需要采样4次,通过box滤波器。注意2×2网格采样与图5.25相符。这个方法消耗很大,因为所有子采样必须完全渲染填充,以及zbuffer深度也是。FSSAA的好处显而易见。另外,低质量的函数可以在一个屏幕轴上采样两次,也叫做1×2或者2×1超采样。一般会用2的倍数的分辨率以及box滤波器以达到简单的目的。NVIDIA的dynamic super resolution特性是更精细版本的超采样,场景通过更高的分辨率进行渲染,并且使用13采样的高斯滤波器用于显示图片。
Wasson, Ben, “Maxwell’s Dynamic Super Resolution Explored,” The Tech Report website, Sept. 30, 2014. Cited on p. 139
超采样的采样函数基于accumulation buffer(累积缓冲区)的概念。
Haeberli, P., and K. Akeley, “The Accumulation Buffer: Hardware Support for High-Quality Rendering,” Computer Graphics (SIGGRAPH ’90 Proceedings), vol. 24, no. 4, pp. 309–318, Aug. 1990. Cited on p. 139, 529, 537, 547
Mammen, Abraham, “Transparency and Antialiasing Algorithms Implemented with the Vir- tual Pixel Maps Technique,” IEEE Computer Graphics & Applications, vol. 9, no. 4, pp. 43–55, July 1989. Cited on p. 139, 15
不是通过一个大的OffScreen Buffer,而是使用一个与期望图像相同的Buffer,但是每个通道有更多的颜色数据。为了获取场景的2×2数据,产生了在y方向以及x方向各自移动半像素的四张图片。每一个图片基于不同的像素格采样点位置生成。因为要重新渲染场景几次,并且将结果拷贝到屏幕的开销,导致在实时渲染系统中的开销非常大。当性能不是必要的时候,例如渲染高质量图片的时候这确实是有用的,因为任意数量任意位置的采样,可以用于每个像素。
Sousa, Tiago, Nickolay Kasyan, and Nicolas Schulz, “CryENGINE 3: Three Years of Work in Review,” in Wolfgang Engel, ed., GPU Pro3, CRC Press, pp. 133–168, 2012. Cited on p. 139, 234, 238, 245, 252, 257, 542, 786, 793, 932, 937
累积的buffer在多个分离的硬件中使用。这个在OpenGL的API中是直接支持的,但是在3.0中被弃用了。在现代GPU中累积buffer的概念可以在输出buffer中使用高清晰度颜色格式通过pixel shader进行实现。
额外的采样用于像物体边缘,高光以及硬阴影产生的颜色突变现象。阴影可以经常通过软阴影规避,高光也可以通过柔和来规避。特定的物体,例如电线可以通过增加尺寸来保证至少覆盖一个像素。
Persson, Emil, “Wire Antialiasing,” in Wolfgang Engel, ed., GPU Pro5, CRC Press, pp. 211– 218, 2014. Cited on p. 139
物体边缘的反走样依旧是一个主要的采样问题。通过分析物体边缘所在以及影响出现的地方来解决这样的问题,但是这样做往往比起更多采样而言是更加高消耗并且不够鲁棒的。不过GPU特性类似于保守光栅化(conservative rasterization)以及光栅顺序视口(rasterizer order views)打开了新的可能性。
类似于超采样以及累计buffer通过增加包含了完全计算shader以及深度的采样。这样的采样收益非常小并且消耗非常大,每一个采样都要经过完整的pixel shader。
Multisampling antialiasing(MSAA)减少了计算每一个像素带来的高消耗,并且将采样之间的结果进行共享,也就是说,每个片元四个(x,y)采样,每个采样有它们自己的z-depth,但是pixel shader针对每一个像素只会执行一次。如果所有的MSAA位置被一个片段覆盖了,那么shading采样只需要计算像素的中间点。如果片段覆盖了更少的采样,那么采样会移动到更好的位置。这样做的好处是可以的避免在非贴图的地方进行渲染采样点,这个行为被GPU自动执行,如果GPU支持的话。中央采样可以避免不再三角面片上的问题,但是可能产生衍生的计算以返回一个不正确的值。看下图。
Giesen, Fabian, “A Trip through the Graphics Pipeline 2011,” The ryg blog, July 9, 2011.
Cited on p. 32, 42, 46, 47, 48, 49, 52, 53, 54, 55, 141, 247, 684, 701, 784, 1040
- Licea-Kane, Bill, “GLSL: Center or Centroid? (Or When Shaders Attack!)” The OpenGL Pipeline Newsletter, vol. 3, 2007. Cited on p. 141
MSAA比完全超采样的方法要快,因为片元只需要渲染一次。它专注于在片元覆盖的部分更高频进行采样,并且共享已经计算的内容。这让计算采样以及覆盖节约更多内存成为可能,也同样让MSAA更加快(更少的内存访问)。NVIDIA在2006年推出了coverage sampling antialiasing(CSAA),AMD随后用enhanced quality antialiasing(CSAA)与之匹配。这些技术通过只将覆盖部分的片元进行高频采样来加速。例如,EQAA的“2f4x”模式储存了两个颜色以及深度值,在4个采样位置中进行共享。颜色和深度不再储存在一个特定的位置上而是存疑一个table中。每一个四个一组的采样中只需要用一字节指定位置上储存了哪个值即可。就像上图一样。这些覆盖采样储存的值描述了每个像素的最终颜色值。如果颜色储存的数量超过了,那么颜色将会被剔除,这个采样的也被标记位未知。这些采样不代表最终的颜色。
Drobot,Michal,“HybridReconstructionAntiAliasing,”SIGGRAPHAdvancesinReal-Time Rendering in Games course, Aug. 2014. Cited on p. 141, 142, 146, 165
Drobot, Michal, “Hybrid Reconstruction Antialiasing,” in Wolfgang Engel, ed., GPU Pro6, CRC Press, pp. 101–139, 2015. Cited on p. 141, 146, 165
对于大部分的情况而言,只有很少的一部分像素包含了三个或者以上可见不透明片元,所以这种方法在实际使用中非常好用。
- Pettineo, Matt, “Rendering the Alternate History of The Order: 1886,” SIGGRAPH Advances in Real-Time Rendering in Games course, Aug. 2015. Cited on p. 141, 142, 143, 245, 256, 257, 803, 896
但是为了最高质量,游戏《Forza Horizon 2》使用了4 x MSAA,虽然EQAA有更好的性能提升。
- Leadbetter, Richard, “The Making of Forza Horizon 2,” Eurogamer.net, Oct. 11, 2014. Cited on p. 141, 900
一旦所有图元被渲染到multiple-sample buffer,一个resolve操作将会被执行。也就是将采样也JS的值进行平均,来决定像素的最终颜色。所以使用HDR的时候会有一个明显的渲染错误。在这种情况下,为了避免出错,你一般需要在resolve之前进行tone-map。
Persson, Emil, “Post-Tonemapping Resolve for High-Quality HDR Anti-aliasing in D3D10,”
in Wolfgang Engel, ed., ShaderX6, Charles River Media, pp. 161–164, 2008. Cited on p. 142
这也许会非常耗,所以近似的tone map方程或者其他方法有可能进行使用。
Karis, Brian, “High Quality Temporal Supersampling,” SIGGRAPH Advances in Real-Time Rendering in Games course, Aug. 2014. Cited on p. 142, 143, 144, 620
- Pettineo, Matt, “Rendering the Alternate History of The Order: 1886,” SIGGRAPH Advances in Real-Time Rendering in Games course, Aug. 2015. Cited on p. 141, 142, 143, 245, 256, 257, 803, 896
默认情况下MSAA使用一个box滤波器。在2007年ATI推出了custom filter antialiasing(CFAA)
Shilov, Anton, Yaroslav Lyssenko, and Alexey Stepin, “Highly Defined: ATI Radeon HD 2000 Architecture Review,” Xbit Laboratories website, Aug. 2007. Cited on p. 142
通过使用一个比较宽的三角形滤波,稍微进入其他的像素格。这种模式已经被EQAA支持取代了。在现在GPUpixel或者compute shader中可以获取MSAA采样然后使用任何想要的重建滤波器,也就是把周围像素的采样包含进来。一个更宽的采样器能够减少走样,虽然会损失一些细节。Pettineo发现立方平滑以及包含2到3个像素宽的B-spline滤波器有最好的结果。但是会有一定的性能消耗,即使是模拟默认的box滤波器使用自定义shader也会花更长时间,一个更宽的滤波器内核意味着增加采样的获取时长。
NVIAIDA内置的TXAA支持用一个宽于一像素的重建滤波器来获得更好的结果。它和MFAA(multiframe antialiasing)方法都使用了temporal antialiasing(TAA),一个从前一帧内容改进图片的通用方法。使得程序员每帧设置MSAA采样模式成为了可能。这样的技术可以解决类似于滚动的车轮之类的走样问题,也可以改进边缘渲染质量。
想象一下生成一系列的图片,这些图片每一张使用的采样点使用像素不同的位置。偏移是在投影矩阵上追加了微小的变换[1938]。更多的图片生成并进行取均值,图片结果就越好。使用多个偏移图片用于temporal antiliasing算法。一个图片生成的时候可能使用MSAA或者其他函数,并且与之前的图片混合。经常使用两帧或者四帧。更老的图片会用几何倍数衰减的权重,虽然这可能会造成不动的图片闪烁,所以经常把上一帧与这一帧权重相同【so often equal weighting of just the last and current frame is done.】。每一帧采样在不同的子像素位置,采样的权重总和给与比单像素了更好的边缘估计。所以使用至少两帧平均采样可以获得更好的结果。每一帧不需要额外的采样,这也使得这种方式非常有吸引力。即使使用低分辨率图片来提升显示分辨率也是有可能的[1110]。光照函数以及其他技术需要许多的采样来得到比较好的结果,而不是每一帧获取更少的采样,因为结果会在多帧进行混合【1938】。
对于静态场景提供反走样没有额外的采样消耗,这种算法对于temporal反走样有一定的问题。如果帧没有同等的权重,静态场景的图物体可能会出现闪烁。频繁移动物体或者摄像机可能产生ghosting现象,之前帧的结果会产生拖尾。一种解决ghost的方式是只使用在慢速物体上【1110】。另一种重要的方法是使用reprojection【12.2节】来更好修正之前以及现在的物体。在这种方法中,物体产出移动向量,并且储存在一个分开的速度缓存区velocity buffer【12.5节】中。这些向量用于修正像素位置来找到之前帧颜色的用于物体表面的颜色像素。采样到与当前帧表面不符的像素就会被裁剪掉【1912】。因为没有额外的采样,所以对于tempral antialiasing来说只有相当少的工作,最近这几年对于这一类算法有着强大的关注度以及应用。一些关注来自于延迟渲染技术【1846】,这是MSAA以及其他一些multisampling所不支持的。方法很多样,并且与应用程序的内容以及目标相关,一部分用于避免渲染问题以及提升质量的技术已经开发出来了【836,1154,1405,1533,1938】。Wihlidal的讲座【1885】,举个例子,展示了EQAA,temporal antialiasing以及多种滤波器技术应所有到棋盘采样能够在保持质量的同时,减少像素着色器的调用。Iglesias-Guitan【796】 总结了之前的一些工作,并且将他们的方法用于像素历史以及预测用于将滤波器瑕疵降到最小。Patney【1357】在虚幻引擎的实现中扩展了Karis以及Lottes的TAA【862】用于虚拟现实应用当中,添加可变尺寸的采样,用于眼睛移动的补偿【21.3.2节】。
采样模式
高效的采样模式是降低走样,temporal以及其他部分的关键因素。Naiman【1257】展示了人们经常被在几乎水平边缘以及几乎垂直边缘干扰。接近45°的边缘是接下去最干扰的。Rotated grid supersampling(RGSS)使用了一个旋转的方形模式用于给与更多的像素中横向与纵向的分辨率。5.25显示了这种模式的例子。
RGSS模式是一种Latin hypercube或者N-rooks sampling的形式,也就是将n个采样点放到nxn的网格下,其中每一个采样都占有一行以及一列【1626】。通过RGSS,四个像素分别在4×4网格的不同的行与列中。这样的模式对于捕捉近水平以及垂直的边缘的效果比传统2X2采样模式要更好,边缘看上去像占用多个像素所以看上去没有这么强烈。
N-rooks是创建一个好的采样模式的开始,但是是不足够的。举个例子,采样可能全部都在一个斜的网格中,这样就会返回一个很差的效果,近乎与斜率相同。就像下图一样。
为了更好的采样我们希望避免两个采样互相接近。我们希望一个常规的表示,在这个区域上进行分离。为了组织这样的模式,stratified sampling(分层采样)技术类似于Latin hypercube采样与其他方式结合,类似于jittering,Halton sequences以及Poisson disk sampling【1413,1758】。
在实际中,GPU厂商经常将采样模式硬编码(hard-wire)入他们的硬件用于mutisampling antialiasing。下图显示了一些实际中运用的MSAA模式。对于temporal antialiasing,覆盖模式可以是任意程序员所期望的模式,因为在帧直接可以多样化采样位置。比如,Karis【862】发现了基本的Halton sequence比任何的GPU提供的MSAA模式都要好。Halton sequence 产生了看起来随机但是有差异的采样点,它们在空间中分散得很好,并且没有集群现象【1413,1938】。
虽然子像素网格模式能够更好模拟每一个三角面如何与网格覆盖,但不是理想的。一个场景能够被一些屏幕上非常小的物体组成。意味着没有采样率可以真正完美地捕捉到它们。这些微小的物体通过固定间隔进行采样,可能是Moire frings以及其他的干涉模式【If these tiny objects or features form a pattern, sampling at constant intervals can result in Moir ́e fringes and other interference patterns.】。网格模式在超采样中特别容易出现锯齿。
一个解决方案是使用stochastic sampling(随机采样),更随机性的采样模式。类似于5.28中的模式就是这样。一个传统的采样模式可能因为采样模式与齿状频率相进相出【goes in and out of phase】而产生严重的瑕疵。一个非有序的采样可以打破这样的模式。随机性倾向于使用噪音提到有序的锯齿效果,对于人类视觉系统而言是更容易接受的【1413】。一个模式更少的结构化,但是依旧可能在重复的像素直接出现走样。一种解决方案是在每个像素之间使用不同的采样模式,或者通过时间改变采样点。Inerleved sampling(插入采样),每个像素集都有不同的采样模式,在过去的十年已经被部分的硬件厂商所使用了。举个例子,ATI的SMOOTHVISION允许最高没像素16代养以及最高16种不同的用户定义采样模式,可以在不同的像素间进行混合(例如在4×4像素块中)Molnar以及Keller和Heidrich【880】发现使用interleaved stochastic sampling 可以最小化每个像素相同采样结果产生的走样瑕疵。
一些其他的GPU支持的算法没有太大的价值。一个采样效果超过一个像素实时反走样的方法,是NVIDIA更老的Quincunx方法【365】“Qunicunx”意味着五个物体的平均,四个在方形中,一个在中间。如六面骰子上的五个点的图案。Quincunx MSAA使用了这种模式,将四个外围的采样在像素的角落。看下图,每一个角落中的采样描述了它四个相邻的像素。而不是每个像素相同的权重(像其他大部分实时方法一样)中间的采样占权重的二分之一,其他每一个采样则占了八分之一。因为这种分享方式,只要两个采样就可以表示一个像素,结果也比两采样的FSAA要更好【1678】。这种模式近似了一个二维的三角滤波器,就如之前讨论到的一样比box采样器更优。
Quincunx采样也被用于temporal antialiasing通过每个像素一个采样的方法【836,1677】。每一帧相对前一帧在每一个轴上偏移半个像素,在帧直接不同方向进行交替。前一帧提供了这一帧角落的采样,线性插值用于计算每一帧的贡献。结果与本帧进行平均。每一帧相同权重意味着对于静态视角没有闪烁的瑕疵。移动物体的问题还是存在,但是这个采样方法本身在每一帧采样一像素时给予了更好的结果。
如果在一帧中使用,Quincunx由于在像素之间共享像素,有着只要两个采样的低消耗。RGSS模式对于捕捉几乎水平以及垂直边缘的渐变有更好的效果。第一个为手机设计的图形,FLIPQUAD模式结合了所有这些特性【22】。它的优势在于只需要每个像素两个采样点,并且效果接近于RGSS(需要每像素采样4个点)。这个采样模式在上图展示。Hasselgren等人探索了其他利用样本共享的廉价抽样模式【677】。
就像Quincunx,采样两个像素的FLIPQUAD模式也可以用作temporal antialiasing以及分离的两帧。Drobot【382,383,1154】,在他的hybrid reconstruction antialiasing(HRAA)中解决了哪种双采样模式最好的问题。他为temporal antialiasing探索了不同的采样模式,发现FLIPQUAD模式是五种测试方法中最好的。棋盘模式也被用于temporal antialiasing。El Mansouri【415】讨论了使用双采样MSAA来创建一个棋盘渲染来减少shader消耗,并且解决走样问题。Jimenez【836】使用SMAA,temporal antialiasing,以及大量其他的技术来提供一种解决方案可以解决抗锯齿质量随着引擎负载而改变的问题。Carpentier和Ishiyama【231】在边缘采样,将采样网格旋转45°。他们结合了temporal antialiasing方法以及FXAA(在后面提到)高效呈现在高分辨率显示器上。
形态学方法(Morphological Methods)
反走样经常源自于边缘,例如几何体、硬阴影或者高光。这样结构相关的信息可以用于提供更好的反走样结果。在2009年Reshetov【1483】提出了基于这些点的算法,称作形态学抗混叠【MLAA】。形态学意思是结构与形状相关。早期在这部分的工作【830】可以追溯到Bloomenthal1983年的研究【170】。Reshetov的论文重新提出了抗混叠方法的替代方案,着重与搜索以及边缘的重构。
这一类的抗混叠使用后处理进行实现。这是因为,渲染以一般方式实现,然后将处理结果提供给处理反走样结果的进程。大量的技术从2009年开始被开发了。依赖于额外buffer,例如深度以及法线可以获得更好的结果,例如subpixel reconstruction antialiasing(SRAA)【43,829】但是仅限几何边缘的抗混叠。分析方法,类似于geometry buffer antialiasing(GBAA)以及distance-to-edge antialiasing(DEAA),有着关于三角形边缘以及位置的额外计算信息,例如边缘距离像素中心有多远【829】。
最通用的方法只需要颜色buffer,意味着他们也可以改进阴影边缘,高光以及大量之前的后处理技术,例如轮廓边缘渲染【15.2.3节】。比如,directionally localized antialiasing(DLAA)【52,829】基于观察几乎水平的边缘是否应该在垂直方向模糊,几乎垂直方向的边缘也是一样。
更精细的边缘检测形式试图找到可能包含任何角度边缘的像素,并确定其覆盖范围。研究潜在边缘周围的邻域,目的是尽可能重建原始边缘所在的位置。像素的边缘效果可以用于混合邻近的像素颜色。可以看到下图,对过程的概念视图。
Iourcha等[798]通过以像素为单位检查MSAA样本来改进边缘查找,从而计算出更好的结果。注意,与基于样本的算法相比,边缘预测和混合可以得到更高的精度。例如,每个像素使用4个样本的技术只能为对象的边缘提供5个层次的混合:没有覆盖的样本、一个覆盖的样本、两个、三个和四个。估计的边缘位置可以有更多的采样位置,从而提供更好的结果。
有几种基于图像的算法可能会误入歧途。首先,如果两个物体之间的色差小于算法的阈值,则不能检测到边缘。有三个或三个以上不同表面重叠的像素点很难解释。具有高对比度或高频元素的表面,颜色在像素之间快速变化,可能会导致算法遗漏边缘。特别是,当应用形态学反锯齿时,文本质量通常会受到影响。物体角可能是一个挑战,一些算法给它们一个圆角。假定边是直的,曲线也会受到不利的影响。单个像素的改变会导致重构边缘的方式发生很大的变化,从而在帧与帧之间创建明显的伪影。改善这一问题的一种方法是使用MSAA覆盖掩模来改进边缘确定[1484]。
形态学抗混叠方案仅使用所提供的信息。例如,宽度小于一个像素的物体,如电线或绳子,只要没有恰好覆盖像素的中心位置,屏幕上就会有间隙。在这种情况下,多采样可以提高质量;仅仅基于图像的抗混叠是不行的。此外,执行时间可以根据查看的内容而变化。例如,一块草地的视图的反锯齿时间可能是天空视图的三倍[231]。
尽管如此,基于图像的方法可以为有限的内存和处理成本提供抗混叠支持,因此它们在许多应用程序中得到了应用。只需要颜色的方法也与渲染管道解耦,使它们更容易修改或禁用,甚至可以作为GPU驱动程序选项公开。两种最流行的算法是快速近似反走样(fast approximate antialiasing,FXAA)[1079、1080、1084]和亚像素形态反走样(subpixel morphological antialiasing ,SMAA)[828,830,834],部分原因是它们都为各种机器提供了可靠的(和免费的)源代码实现。两种算法都使用纯颜色输入,SMAA具有访问MSAA采样点的优势。每个都有自己的设置,在速度和质量之间进行权衡。成本通常在每帧1到2毫秒之间,主要是因为这是视频游戏愿意花费的耗时。最后,两种算法都可以利用temporal antialiasing[1812]。Jimenez[836]提出了一种改进的SMAA实现,比FXAA更快,并描述了一种temporal antialiasing方案。最后,我们推荐读者阅读Reshetov和Jimenez[1486]对形态学技术及其在电子游戏中的应用所作的广泛综述。
5.5 透明度、Alpha值和合成(Transparency, Alpha, and Compositing)
半透明物体允许光通过它们的方式有很多种。对于渲染算法,可以大致分为基于光和基于视图的效果。基于光的效果是指物体使光线减弱或转移,导致场景中的其他物体被照亮并呈现不同的效果。基于视图的效果是那些呈现半透明对象本身的效果。
在本节中,我们将讨论基于视图的透明的最简单形式,其中半透明对象作为对象颜色的衰减器。更精细的基于视图和光的效果,如磨砂玻璃,光的弯曲(折射),透明物体的厚度产生的光的衰减,反射率和透射率随着观察角度的变化将在后面的章节中讨论。
一种给人透明错觉的方法被称为过滤网门(screen-door trans- parency)[1244]。其想法是用像素对齐的棋盘填充模式呈现透明三角形。也就是说,三角形的其他每个像素都被渲染,从而使后面的对象部分可见。通常,屏幕上的像素距离足够近,以至于棋盘图案本身是不可见的。这种方法的一个主要缺点是,通常只能在屏幕的一个区域上令人信服地呈现一个透明对象。例如,如果透明的红色对象和透明的绿色对象呈现在蓝色对象之上,那么这三种颜色中只有两种可以出现在棋盘格模式中。另外,50%的棋盘是有限的。其他较大的像素掩模可以用来给出其他百分比,但这些掩模往往会创建可检测的模式[1245]。
也就是说,这种技术的一个优点是简单。透明对象可以在任何时间、以任何顺序呈现,不需要特殊的硬件。透明度问题通过使所有对象在其覆盖的像素处不透明来解决。同样的想法也适用于裁剪纹理的抗锯齿边缘,但是在亚像素级,使用alpha覆盖(第6.6节)。
由Enderton等[423]提出,随机透明采用亚像素屏门掩模结合随机采样。一个合理的虽然有噪声的图像是通过使用随机点画模式来表示片段的alpha覆盖创建的。参见图5.31。为了使结果看起来合理,每个像素需要大量的样本,并且所有的亚像素样本都需要相当大的内存。吸引人的是,不需要混合,而且antialiasing、透明度和任何其他创建部分覆盖像素的现象都由一个机制实现。
大多数透明算法都是将透明对象的颜色与其背后对象的颜色混合在一起。为此,需要alpha混合的概念[199,387,1429]。当对象在屏幕上呈现时,RGB颜色和z-buffer深度与每个像素相关联。另一个组件,称为阿尔法(α),也可以为每个像素定义对象覆盖。Alpha是一个值,用于描述给定像素的对象片段的不透明度和覆盖率。alpha值为1.0意味着该对象是不透明的,并且完全覆盖了像素感兴趣的区域;0.0表示像素完全不被遮挡,即,片段是完全透明的。
一个像素的alpha值可以表示不透明度、覆盖率,也可以同时表示两者,这取决于环境。例如,肥皂泡的边缘可能覆盖0.75像素的四分之三,而且可能几乎是透明的,让十分之九的光线进入眼睛,所以它是十分之一不透明的,0.1像素。它的是0.75×0.1 = 0.075。然而,如果我们使用MSAA或类似的抗混叠方案,覆盖范围将由样本本身考虑。四分之三的样本会受到肥皂泡的影响。在每个样本中,我们将使用0.1的不透明度值作为alpha值。
混合顺序 Blending Order
要使对象看起来透明,可以在现有场景的顶部呈现它,alpha值小于1.0。每个像素覆盖的对象将会收到一个结果RGBα从像素着色器(也称为RGBA)。将该片段的值与原始像素颜色混合通常使用over操作符,如下所示:
cs是透明的颜色对象(称为源),αs对象的α,cd是混合前的像素颜色(称为目标),和co是现有的场景透明的对象的最终颜色。在渲染管道的情况下发送在cs和αs,像素的原始颜色cd被结果co取代。事实上如果传入RGBα,是完全不透明(αs = 1.0),那么将像素的颜色全部替代成了对象的颜色简化了方程。 例如:混合。一个红色的半透明物体被渲染到一个蓝色背景上。假设在某个像素处,对象的RGB渲染为(0.9,0.2,0.1),背景为(0.1,0.1,0.9),对象的不透明度设置为0.6。然后是这两种颜色的混合
0.6(0.9, 0.2, 0.1) + (1 − 0.6)(0.1, 0.1, 0.9),
最后的结果是(0.58, 0.16, 0.42)。
over操作符给呈现的对象一个半透明的外观。透明性就是这样工作的,在某种意义上,当我们看到后面的物体时,我们就认为它是透明的[754]。
使用over模拟了薄纱织物的真实效果。织物后面的对象视图部分是模糊的——织物的线是不透明的。在实践中,松散的织物具有随角度变化的alpha覆盖[386]。我们这里的重点是alpha模拟了材质覆盖像素的程度。
over运算符在模拟其他透明效果方面的说服力较差,尤其是通过彩色玻璃或塑料进行观察。在现实世界中,蓝色物体前的红色滤光片通常会使蓝色物体看起来很暗,因为这个物体反射的光很少能通过红色滤光片。参见图5.32。当使用over进行混合时,结果是红色和蓝色的一部分加在一起。最好将这两种颜色相乘,并添加透明物体本身的反射。这种物理透过率在14.5.1和14.5.2节中讨论。
在基本的共混阶段算子中,over是一种常用的反填充效应算子[199,1429]。另一种有一定用途的操作是加法混合,它简单地对像素值求和。
这种混合模式可以很好地用于发光效果,如闪电或火花,这些发光效果不会减弱后面的像素,而只会使它们变亮[1813]。然而,这种模式在透明度方面看起来并不正确,因为不透明的表面没有经过过滤[1192]。对于一些层状半透明表面,如烟或火,additive混合具有饱和现象颜色的效果[1273]。
为了正确呈现透明对象,我们需要在不透明对象之后绘制它们。先关闭所有混合然后渲染所有的不透明物体,然后打开over并且渲染透明物体。理论上,我们可以总是打开over操作,因为1.0的不透明alpha值会给出源颜色并隐藏目标颜色,这样做的代价更大,没有真正的好处。
z缓冲区的一个限制是每个像素只能存储一个对象。如果多个透明对象重叠在同一个像素上,则z缓冲区无法单独保存并在稍后解析所有可见对象的效果。所有使用了over操作渲染的透明物体需要以从后往前的顺序渲染。不这样做会出现明显的渲染错误。实现这种排序的一种方法是对单个对象进行排序,例如,根据其中心点沿视图方向的距离。这种粗略的排序可以很好地工作,但是在不同的情况下会有很多问题。首先,这个顺序只是一个近似值,所以被划分为较远的物体可能在被认为较近的物体前面。相互渗透的对象不可能在所有视角上每个网格的基础上解决顺序问题,除非将每个网格分割成单独的部分。有关示例,请参见图5.33中的左边图像。即使是一个带有孔洞的网格,在屏幕上与自身重叠的视角方向上也会出现排序问题。
尽管如此,由于它的简单性和速度,以及不需要额外的内存或特殊的GPU支持,执行粗略的透明度排序仍然是常用的。如果进行实现,通常最好在执行透明时关闭z-depth替换。也就是说,z缓冲区仍然正常测试,但存活的表面不会改变存储的z深度;最接近不透明表面的深度保持不变。这样,所有透明的物体至少都会以某种形式出现,而不是在相机旋转改变排序顺序时突然出现或消失。其他技术也可以帮助改善渲染,比如每次绘制两次透明网格,首先绘制背面,然后绘制正面[1192,1255]。
over方程也可以修改,使从前往后混合得到相同的结果。这种混合模式称为under操作符:
注意,under要求目标保持alpha值,而over不需要。换句话说,目标(正在混合的较近的透明表面)不是不透明的,因此需要有一个alpha值。下面的公式和over差不多,但是源和目标交换了。另外,请注意计算alpha的公式是与顺序无关的,因为可以交换源和目标阿尔法,最终alpha是相同的结果。
alpha的方程来自于考虑片元的alpha作为覆盖。Porter和Duff[1429]指出,由于我们不知道任何一个片段的覆盖区域的形状,我们假设每个片段覆盖另一个片段的面积与它的alpha值成比例。例如,如果αs = 0.7,像素是分为两个区域,与0.7由片元覆盖而0.3则没有。没有其他信息,目标片元覆盖,将由αd = 0.6的比例被源片元重叠。该公式具有几何解释,如图5.34所示。
5.5.2 顺序无关透明
under操作用于绘制所有透明物体到一个分开的颜色buffer,然后使用over将颜色buffer合并到不透明的场景视角。另一个under操作的用途是用于一个顺序无关的透明渲染,称为深度剥离depth peeling[449,1115]。顺序无关意味着应用程序不需要执行排序。深度剥离背后的思想是使用两个z缓冲区和多个通道。首先,生成一个渲染遍历,以便所有表面的z深度(包括透明表面)都位于第一个z缓冲区中。在第二遍中,所有透明对象都被呈现。如果一个对像的z-buffer符合第一个z-buffer的值,我们知道这是最接近的透明对象并将其RGBα保存到一个单独的颜色缓冲区。我们还通过保存任何一个透明物体的z深度(如果有的话)来“剥离”这一层,这个z深度超过第一个透明物体的z深度,并且是最近的。这个z深度是第二个最近的透明物体的距离。连续通过继续剥离并且使用under添加透明层。我们在经过一些遍历之后停止,然后将透明图像混合到不透明图像之上。参见图5.35。
已经开发了该方案的几个变体。例如,Thibieroz[1763]给出了一种从后向前的算法,它的优点是能够立即混合透明值,这意味着不需要单独的alpha通道。深度剥离的一个问题是知道需要多少遍才能捕获所有透明层。一种硬件解决方案是提供一个像素绘制计数器,它告诉绘制过程中写入了多少像素;当一个遍历没有呈现像素时,渲染就完成了。使用under的好处是最重要的透明层—眼睛最先看到的层—最早进行渲染。每个透明的表面总是增加它所覆盖像素的alpha值。如果一个像素的alpha值接近1.0,混合的贡献使得像素几乎不透明,因此距离更远的物体将产生微不足道的影响[394]。当一个遍历呈现的像素数低于某个最小值,或者指定一个固定的遍历数时,可以缩短从前到后的剥离。这对于从后往前的剥离效果不太好,因为最近的(通常也是最重要的)层是最后绘制的,因此可能会在早期终止时丢失。
虽然深度剥离是有效的,但它可能是缓慢的,因为每一层剥离是所有透明对象的单独渲染通道。Bavoil和Myers[118]提出了双重深度剥离,即每次剥离两个深度剥离层(最近的和剩余的最远的),从而将渲染次数减半。Liu等人[1056]探索了一种桶式排序方法,一次可以捕获多达32层。这种方法的一个缺点是,它需要相当大的内存来为所有层保持有序。通过MSAA或类似的抗混叠技术将大大增加成本。
以交互速率将透明对象正确地混合在一起的问题并不是我们缺少算法,而是将这些算法有效地映射到GPU的问题之一。1984年Carpenter提出了A-buffer[230],这是multisampling的另一种形式。在a -buffer中,呈现的每个三角形为它完全或部分覆盖的每个屏幕网格单元创建一个覆盖掩码。每个像素存储所有相关片段的列表。不透明的片段可以剔除它们后面的片段,类似于z-buffer。所有的片元都存储在透明的表面。一旦所有列表都形成,通过遍历片段并解析每个示例就会产生最终的结果。
在GPU上创建片段链表的想法是通过DirectX 11[611,1765]中公开的新功能实现的。使用的特性包括无序访问视图(uav)和原子操作,见第3.8节。通过MSAA的抗锯齿功能是通过访问覆盖掩模和评估像素着色器在每个样本。该算法对每个透明表面进行栅格化,并将生成的片段插入到一个长数组中。除了颜色和深度之外,还生成一个单独的指针结构,将每个压裂段链接到为像素存储的前一个片段。然后执行一个单独的遍历,其中渲染一个填充屏幕的四边形,以便在每个像素处计算像素着色器。该着色器通过跟踪链接检索每个像素处的所有透明片段。检索到的每个片段依次与前面的片段进行排序。然后将这个排序后的列表混合到前面,以给出最终的像素颜色。由于混合是由像素着色器执行的,如果需要,可以为每个像素指定不同的混合模式。GPU和api的不断发展通过降低使用原子操作符的成本提高了性能[914]。
与GPU上的链表实现一样,A-buffer的优点是只分配每个像素所需的片段。在某种意义上,这也可能是一个缺点,因为在开始呈现帧之前,所需的存储量是未知的。一个有毛发、烟雾或其他物体的场景,可能有许多重叠的透明表面,可以产生大量的片元。Andersson[46]指出,对于复杂的游戏场景,多达50个透明的物体网格,如树叶和多达200个半透明粒子可能重叠。
gpu通常预先分配内存资源,如缓冲区和数组,链表方法也不例外。用户需要决定多少内存是足够的,而内存耗尽会导致明显的瑕疵。Salvi和Vaidyanathan[1532]提出了一种解决这个问题的方法,即使用Intel引入的名为像素同步的GPU特性的多层alpha混合。参见图5.36。这种功能提供了可编程的混合,与原子相比开销更小。他们的方法重新定义了存储和混合,以便在内存耗尽时优雅地降级。粗略的排序顺序可以使他们的方案受益。DirectX 11.3引入了光栅化顺序视图(rasterizer order views)(第3.8节),这是一种缓冲区,允许在支持该特性的任何GPU上实现这种反插入方法[327,328]。移动设备也有类似的技术,称为平铺本地存储(tile local storage),允许它们实现多层alpha混合[153]。然而,这样的机制有性能成本,因此这种类型的算法可能很昂贵[1931]。
这种方法建立在k-buffer的思想上,由Bavoil等人[115]提出,在k-buffer中,前几个可见层被尽可能地保存和排序,更深层的层被尽可能地丢弃和合并。Maule等人[1142]使用k-buffer,通过加权平均来解释这些较远的深层。加权和(Weighted sum)[1202]和加权平均(Weighted average)[118]透明技术是顺序无关的,是单遍的,并且几乎在每个GPU上运行。问题是它们没有考虑对象的顺序。例如,使用alpha表示覆盖范围,薄纱蓝色围巾上的薄纱红色围巾呈现紫罗兰色,而正确地看到红色围巾上有一点蓝色。虽然几乎不透明的物体给出的结果很差,但这类算法对可视化很有用,对高度透明的表面和粒子也很有效。参见图5.37。
加权的半透明公式如下
其中n是透明的表面,ci和αi代表透明度的设置值,和cd的颜色不透明部分的场景。这两个和分别作为透明表面进行累积和存储,在透明遍历结束时,在每个像素处对方程求值。这种方法的问题是第一个和过大,即。,生成大于(1.0、1.0、1.0)的颜色值,并且该背景颜色可能会产生负面影响,因为alpha的和可以超过1.0。
通常首选加权平均方程,因为它避免了以上问题:
第一行代表了在透明渲染中两个独立缓冲区中的渲染结果。每一个曲面对csum都有加权的影响。几乎不透明的表面贡献了更多的颜色,而几乎透明的表面几乎没有影响。csum除以asum得到加权平均透明的颜色。aavg是所有透明值的平均值。u的值是目标在这个平均alpha之后的估计可见性(不透明的场景)对n个透明表面应用n次。最后一行完成了计算,用(1-u)表示源的alpha值。
加权平均的限制是,对于相同的透明度,将所有颜色同等地进行了混合,而无视顺序。McGuire和Bavoil[1176, 1180]引入了顺序无关的加权混合透明(weighted blended order-independent transparency)来给出更令人信服的结果。在他们的方案中,距离也会影响权重,更近的表面会产生更大的影响。此外,u并不是通过取平均值,而是通过将几个(1-ai)的值相乘并且用一相减,以得到曲面集的alpha覆盖。这个方法产生了视觉上更令人信服的结果,如下图。
缺点是在大型环境中对象之间的距离可能几乎一致,导致结果与平均加权有所不同。而且当相机与透明物体的距离改变,深度的权重也会发生变化,而这种改变是渐进式的。
McGuire和Mara[1181,1185]扩展了这种方法,加入了一种可信的透射效果(transmission color effect)。就像之前所说的,这一节所有的透明度算法都在讨论如何混合各种颜色效果而不是通过滤波,模拟像素覆盖。为了给颜色滤波效果,不透明场景由像素shader以及所有半透明表面用其颜色乘以其覆盖的场景颜色,保存到第三个婚宠去。这个缓冲区中,不透明物体被透明物体染色。然后使用透明缓冲区替代不透明场景。这种方法之所以有效是因为颜色透射与半透明覆盖不同,是顺序无关的。
还有其他的一些算法使用了几种技术中的元素。例如,Wyman[1931]根据内存需求对以前的工作进行分类,插入以及合并方法,使用alpha还是几何覆盖,以及如何裁剪和处理片元。他提出了两种新的方法来填补之前研究的空白。他的随机透明分层混合算法使用了k-buffer,加权平均以及随机透明。他的另一个算法是Salvi和Vaidyanathan方法的一个变体,使用覆盖掩码而不是alpha。
在给出各种类型的透明内容渲染方法以及GPU功能后,在渲染透明物体中并没有完美的解决方案。感兴趣的读者可以去看一看Wyman的论文[1931]以及Maule等人更详细的透明交互算法综述[1141]。McGuire的演讲[1182]可以开阔视野,通过其他相关的现象,类似于体积照明、透射光以及折射,更深入的描述在本书的后面进行。
5.3.3 预乘Alpha以及合成 ——Premultiplied Aphas and Compositing
over操作符也用于将照片或者合成效果图进行混合。这种过程被称作合成(compositing)[199,1662]。在这种情况下,alpha每个像素的值与RGB颜色一起储存。由Apha通道形成的通道图像有时候叫做matte(哑光?)。它显示了物体的轮廓形状。如下图:
这种RGBa图像可以将其与其他元素或者背景混合进行使用。
一种使用合成RGBa数据的方法是使用预乘alpha(premultiplied alphas)(也称作associated alphas)。也就是RGB值在使用之前与alpha值进行相乘。是的合成方程更有效率:
其中cs‘是预乘源通道,替代了方程5.25中的ascs。预乘alpha也是的不改变混合状态的情况下使用over以及additive混合成为了可能,因为现在在混合过程中加上了源颜色[394]。需要注意的是预乘的RGBa,其中的RGB部分一般不会比透明值更高,即使它们可以产生一个特别明亮的半透明值。
渲染合成图像使用预乘alpha是很自然的。一个黑色背景上渲染的反锯齿不透明对象提供了默认预乘值。假设一个白色(1,1,1)的三角形在其边缘的像素覆盖了40%。使用精确的反锯齿,像素值会设置为0.4的灰色值 ,也就是说我们将会为该像素保存(0.4,0.4, 0.4)的颜色值。如果储存Alpha值也会是0.4,因为这是三角形覆盖的面积。RGBa值也就会是(0.4,0.4,0.4,0.4),也就是预乘值。
另外一个图像储存的方法是unmultipy alphas,也被称为unassociated alphas或者是比较难理解的非预乘alpha。unmultipy alphas顾名思义,RGB不会被alpha相乘。还是白色三角形的例子,未相乘的颜色是(1,1,1,0.4)这种方式的优点是可以保存三角形的原始值,但是这样做的话颜色总是需要乘以alpha值。最好在使用滤波和混合的时候使用预乘数据,因为进行线性插值的时候unmultiply alphas不能正常工作[108,164].类似于边缘周围的黑色条纹的瑕疵可能会出现[295,648]。在6.6节的末尾我们会进一步讨论。而预乘则允许更干净的理论处理[1662]。
对于图像处理应用程序,使用unassociated alpha对照片进行蒙版并且不影响原始数据是比较有用的。童谣的unassociated alpha意味着可以使用颜色通道的全精度范围。也就是说,对于计算机图形计算的线性空间处理RGBa需要非常小心。例如没有浏览器可以正确进行处理,也不可能做到,因为不正确的行为是目前预期内的[649]。支持透明的图像文件格式有PNG(unassociated alpha only)、OpenEXR(associated only)以及TIFF(同时支持两种)。
与Alpha通道相关的一个概念是chroma-keying[199]。这个概念来自于视频制作,演员在绿色或者蓝色屏幕上拍摄并与背景混合。在电影工业中这一过程被称作绿幕或者蓝幕。其中的思路是,将一个特定的色彩(用于胶片)或者精确的值(计算机图形)指定为透明。背景在检测到的时候就显示。这就允许在只使用RGB的情况下产生轮廓形状,而不需要任何alpha值。这个方案的缺点是对象不是完全透明就是完全不透明的,也就是alpha值只能选1或者0。GIF格式允许一种颜色指定为透明。
5.6 显示编码
当我们计算光照,纹理以及其他的效果的时候数值一般假设是线性的。一般来说,这意味着addition以及multiplication的计算结果是预期内的。然而为了避免各种可见瑕疵,显示缓冲区和纹理我们必须使用非线性的编码格式。简单地来说,如下所示:shader的输出在[0,1]范围内,并且将其进行1/2.2次幂计算,这样的过程称作伽马矫正。而对输入的颜色以及贴图进行相反的操作。在大部分情况下GPU帮你做了这件事,而本节将快速总结解释如何以及为什么。
我们从阴极发射管(cathode-ray tube,CRT)开始。在数字成像的早期,CRT是标准配置。这些器件在输入电压与现实亮度之间呈现幂律关系。当一个像素的能级增加的时候,发出的辐射不是线性增长的,而是(意外地)以大于一的幂级增长。举个例子,假设幂是2.一个像素设置为50%则只会发射光亮的四分之一,也就是0.52=0.25,一个像素本身是1.0[607]。尽管LCD和其他显示技术和CRT相比有着不同的内在特性,它们通过电路转换来模拟出CRT的效果。
这个幂函数几乎与人的明度敏感度成反比[1431]。这个幸运巧合的结果是编码大致是一样的。也就是说一对编码为N与N+1的值在可显示范围中感知的差异不变。测量阈值对比,我们检测到大概为1%的亮度差异。这种近似分布最小化了颜色储存在有限精度的缓冲区时的带状瑕疵【23.6节】。这样的好处同样适合于纹理,它们通常也使用相同的编码方式。
显示转换函数(display transfer function)描述了在显示缓冲区中数字值的关系,以及显示中的亮度级别。因为这个原因,它也被称作电光转换函数(electrical optical transfer function,EOTF)。显示转换功能是硬件的一部分,电脑显示器、电视机、电影放映机有不同的标准。在图像处理、视频捕捉设备的最后也有一个标准转换函数,称作光电转换函数(optical electric transfer function,OETF)。
当对于显示线性颜色值进行编码时,我们的目标是消除显示转换函数的影响。无论我们计算的值是多少,最终都能发出相应等级的光。举个例子,比如我们计算值加倍的时候,需要让输出亮度加倍。为了保持这种关系,我们需要使用显示转换函数来抵消其非线性的效果。消除显示器非线性影响的过程被称为伽马校正,很快会变得清晰。当解码纹理值的时候,我们需要应用,显示转换函数产生线性值,用于着色。下图显示了显示过程中的编码与解码。
个人计算机的标准转换函数由一个称作sRGB的颜色空间来决定。大部分控制GPU的API可以自动将合适的sRGB转换应用在读纹理或者写颜色缓冲区的时候[491]。如6.2.2节所说的,mipmap的生成也会考虑sRGB编码。纹理间的双线性插值在第一次转换到线性空间之后在进行计算即可获得正确的插值结果。alpha混合也要先将储存值还原到线性空间,混合完毕后再编码器结果。
在渲染的最后阶段进行转换非常重要。此时写入framebuffer的数据是用于显示的。如果后处理是在编码后进行的话,这样会影响计算非线性值,往往导致不正常的渲染结果。显示编码可能经常被看做一种压缩形式,保持最好的感官效果[491]。一个好的思考方式是线性值永远用于实际计算,当我们想要显示结果或者访问可显示图像,例如彩色纹理的时候,我们需要将数据装环岛显示编码格式,使用合适的解码以及编码转换。
如果你需要手动应用sRGB模式。有一个标准转换方程以及若干简化版本。在实践中,显示器是按照每个颜色通道的几位控制的。例如8位是消费者级显示器,给出的范围是0 ~ 255.这里我们将编码级别表示为0 ~ 1,而忽略位数。线性值也在这个范围内,使用浮点数表示。我们用x来表示线行值而用y来表示储存在framebuffer中的非线性值。为了将线行值转换到sRGB非线性值。我们应用sRGB显示转换的逆函数:
用x表示线性RGB三元组的通道。这个方程应用于每个通道,这三个生成的值将影响显示。如果你手动使用这些函数,一个错误的来源是编码的颜色并不是线性的,另一种是解码或者编码两次。
这两个变换表达式的底部是一个简单的乘法,由数字硬件使得转换完全可逆的需要而来[1431]顶部表达式,包括幂运算,几乎适用于[0,1]整个范围的x输入。考虑到偏移量以及比例 ,这个函数近似于更简单的公式:
其中y=2.2,这个希腊字母的来源是gamma correction。
正如必须对计算机进行编码后才能显示一样,静止图像或者摄像机图像在计算前必须变成线性值。任何你在显示器或者电视上看到的由rgb编码的颜色都可以通过截屏或者取色器中获得。这些值是否以PNG、JPEG以及GIF等可以直接储存的文件格式放松到framebuffer以便在屏幕上显示,无需转换。换句话说,你在屏幕上看到的任何内容都是经过编码的,在对这些颜色进行着色计算之前,我们必须将其从编码形式中转换回来,我们需要从显示编码到线性值的转换是:
其实y代表了一个标准化的显示通道值,储存在framebuffer或者图片里面的,描述范围在[0.0,1.0]之间。这个解码方程式我们之前sRGB方程的逆方法。这意味着如果如果一个纹理被shader使用并且输出没有任何修改的话,它将于处理之前的显示应该是一样的。解码方程与显示转换函数相同,因为储存在纹理中的值已经正确显示。这个更简单的gamma显示转换方程式5.31的逆方法:
有时候我们会看到更简单的转换方法,特别是在手机以及浏览器上。
也就是说,取线性值的平方根进行转换,然后相乘反函数的值。粗略的近似比完全忽略问题要更好。
如果我们不关注gamma,较低的线性值在屏幕上会显示得暗淡。一个与之相关的错误是,如果没有gamma矫正,某些颜色的色调会发生变化。我们的y=2.2。我们希望从显示的像素中发射的亮度是线性的,已计算的值,意味着我们必须对其乘以1/2.2次幂。一个线性值0.1获得到0.351,0.2获得到0.481,以及0.5或得到0.730.如果不进行编码,这些值会造成显示更少的光线,需要注意的是0和1的值在转换前后是没有变化的。在使用gamma矫正之前,dark surface colors would often be artificially boosted by the person modeling the scene, folding in the inverse display transform.
另外一个问题是着色计算,对于物理线性辐射计算在非线性值上进行了。如下图。
忽略gamma修正也会影响反走样边缘的质量。举个例子,三角边缘覆盖4个屏幕格,如下图:
三角片的照射是1(白色),背景是0(黑色)。从左到右,各自覆盖1/8、3/8、5/8以及7/8。如果我们使用box滤波器我们希望表示的标准化线性照射度是0.125,0.375,0.625以及0.875。正确的方法是利用编码函数对四个结果进行编码在线性值中进行反走样。如果不这么做的话,表示像素的亮度会看起来太暗,导致边缘变成如右图所示的样子。这样的瑕疵被称作roping,因为边缘看上去像一个扭曲的绳子[167,1265]。下图显示了这样的效果
sRGB的标准于1996年创建,已成为大部分计算机显示器的标准。然而从那时候起,显示技术不断发展,显示器颜色更亮并且有更宽的色域。8.1.3节讨论了色彩显示和亮度,而高动态范围显示编码在8.2.1中进行了讨论。Hart的文章[672]关于显示的更高级话题有更多信息。