添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
延迟渲染与MSAA的那些事

延迟渲染与MSAA的那些事

1 年前 · 来自专栏 猫游记

前言

如果你曾经通过百度搜索过延迟渲染相关的信息,或是简单了解过延迟渲染技术,那么你肯定知道延迟渲染的一大缺点就是“不支持抗锯齿”。以百度首页能搜到的几篇文章为例,我们来看看这些文章是如何说的

这些文章都提到不支持硬件抗锯齿(MSAA)是延迟渲染的缺点,却无一例外都没有具体解释到底为什么。

有人写了一篇名为“ 为什么延迟渲染无法使用多重采样抗锯齿(MSAA)? ”的文章,里面又给出了另一种说法

上面解释的很详细了,不是技术上不行,是采用msaa每张图x4 (四张图采样)负载过大,吐血,好吧,终贴。

这些说法并未形成一个统一的结论,所以当初在看到这里时我也纠结了很久。最终在自己尝试理清了MSAA和延迟渲染的原理,并与前辈 @柏莎 交流之后,总结出了一个还算有说服力的答案。

MSAA的原理

MSAA是在SSAA的基础上发展来的硬件抗锯齿技术。SSAA是理论上效果最好的抗锯齿方案,以4x为例(下同),4xSSAA对于每个像素(Pixel)计算4个子像素,将4个子像素的颜色求平均值,便能获得抗锯齿后的颜色。

SSAA的原理

实际上SSAA等于暴力渲染了4倍分辨率的图像,在目前的硬件条件下这种性能开销是不可接受的,因此在SSAA的基础上发展出了MSAA。

MSAA与SSAA的区别在于像素着色器(Pixel Shader)的运行次数。MSAA同样对于每个像素进行了4次子采样(Sample),但是只在像素中心位置运行一次像素着色,然后根据Sample是否被三角形覆盖而将像素着色的颜色复制到Sample上。

MSAA的原理

以上图为例,在前向渲染中,三角形的绘制是依次进行的。绘制蓝色三角形时,MSAA的具体执行步骤如下:

  1. 光栅化阶段,对四个X位置的Sample执行三角形覆盖判断,在一个四倍分辨率大小的coverage mask中记录每个Sample被覆盖的情况。
  2. 像素着色阶段,在像素中心圆点处执行像素着色器。该点的位置、深度、法线、纹理坐标等信息由三角形三个顶点重心插值得到。图中计算得到像素颜色为紫色。
  3. 对四个Sample执行模板测试与深度测试,并将测试通过的Sample数据写入四倍分辨率的模板缓冲与深度缓冲。每个Sample都拥有自己的深度值,依然是重心插值得到。
  4. 上图中左下两个Sample通过了深度测试,并且coverage mask为1,因此将紫色复制到这两个Sample对应的颜色缓冲中(依然是每个Sample一个颜色,共四倍大小)。其他两个Sample暂为背景色。
  5. 重复上述流程绘制第二个黄色三角形,将像素着色获得的黄色复制到右上角的Sample中。
  6. 所有绘制结束之后,通过一个对高层透明的PASS,将四个Sample的颜色插值获得最终输出的像素颜色。

可以看到在MSAA流程中所使用的所有缓冲区都变成了原来的四倍大小,这也是为什么MSAA增加了非常多的显存和带宽消耗。上述流程中第4步如果改成对每个Sample运行像素着色,MSAA就变成了SSAA。

延迟渲染能不能用MSAA?

延迟渲染到底能不能用MSAA呢?原理上是完全没问题的,延迟渲染分为 GBuffer阶段 光照阶段 ,我们来看其具体步骤:

  1. 执行一个GBuffer Pass,通过多目标渲染(Multiple Render Targets,MRT)技术,将最终会显示到屏幕上的像素的颜色(BaseColor)、深度(Depth)、法线(Normal)等信息写入多个RT/纹理中,这些纹理组成了GBuffer。
  2. 执行Lighting Pass,逐像素分别从GBuffer的纹理中取出需要的信息,运行像素着色器计算出最终的颜色缓冲进行显示。

光照阶段使用的输入是GBuffer,如果还像前向渲染一样,在光照计算以后执行MSAA,会得到错误的结果。具体来说,使用单倍GBuffer来进行计算,会因为得不到三角形的覆盖信息而无法判定应该将该点的颜色值复制到哪几个子Sample上,也不会出现同一个像素的子Sample会被不同面片覆盖的情况(因为GBuffer就是一张图,已经不知道该点被几个三角形覆盖了)。而使用多倍大小的GBuffer的话,又无法通过顶点插值获取中心处原始像素的位置、深度、法线、纹理坐标等数据,因为原始三个顶点的信息已经没有了。更重要的是,在多倍大小的GBuffer上我们是没办法判断哪几个子Sample是与中心像素在同一三角形上的,如果试图使用四个子Sample的数据插值获得中心像素,对深度和法线进行插值会导致意料之外的后果。上面两个原因综合起来,就是“丢失其他像素信息导致无法使用MSAA”这种说法的来源了。

那么,为什么会有“延迟渲染没法使用硬件抗锯齿”说法呢?因为在十几年前的DX9时代, MRT是不!支!持MSAA的!!!

写到这里,原因找到了,我也想吐血了。因为后来的DX10.1就支持了带MSAA的MRT,但是一大堆10年以后甚至18年的文章却还在说延迟渲染不支持硬件抗锯齿。。。得,又是搬运工们干的好事。

感谢评论区大神分享了一篇如何在延迟渲染下做MSAA的文章,原来我写的确实有点问题

Multisample anti-aliasing in deferred rendering

编辑于 2022-03-23 08:23

文章被以下专栏收录

    猫游记

    猫游记

    一个游戏爱好者的自留地
    程序媛转TA系列

    程序媛转TA系列