延迟渲染反锯齿技术

5
许多人指出MSAA与延迟着色不兼容。为什么会这样?在我的脑海中听起来还好啊。
根据wikipedia的说法:
由于将照明阶段与几何阶段分离,硬件抗锯齿不再产生正确的结果,因为插值子采样将导致无意义的位置、法线和切线属性。
你能解释一下吗?
我还有哪些其他的反锯齿选择?
3个回答

15
多采样技术在延迟渲染中确实有效。延迟渲染仅仅是改变了它的代价。
多采样基于一般思想,即你只需要执行一次片段着色器就可以对该像素内所覆盖的所有采样进行处理。因此,虽然你会写入多个采样(例如超级采样),但你只需执行一次片段着色器,从而节省了大量的纹理访问。相同的值将被写入多采样图像中的每个采样。
这种方法在延迟渲染中仍然可行。多采样规则下的几何通路仍然有意义。或者至少与以往一样有意义。
问题是在光照通路中需要做什么。
由于几何通道进行了多重采样,因此光照通道必须读取多采样数据。你不能对几何缓冲区进行多采样解析(这是维基百科所说的“荒谬”部分);你的光照通道必须读取和处理每个采样。对于每个光源,如果您使用了8倍多采样操作,则您的片段着色器将为每个像素运行8次。对于每个光源都要如此。
这通常不是很多人愿意承受的成本。
替代方案是任何“我不敢相信这不是反锯齿”的技术,例如FXAA或其他技术。

4
请参阅抗锯齿延迟渲染 - Chuck Walbourn
两个问题:1. 你认为还有公司在延迟着色中使用MSAA吗?2. 仍然有一些G缓冲区,如反射率缓冲区和法线缓冲区,多样本解析操作仍然有意义。考虑了这些吗? - McLovin
@MooseBoys:语义上的区别。就API而言,每个样本着色仍然被认为是使用多重采样。此外,OpenGL API中没有要求多重采样仅在每个像素上执行片段着色器。 - Nicol Bolas
1
在GL中,我们不做这些假设。超采样从未在非供应商API级别上公开(至少没有以名称形式)。多重采样包括两种技术,并且后来的扩展允许“采样着色”,这是更明确的,可能是您使用“超采样”短语时想到的内容。但我仍然认为这里讨论的是多重采样,因为您可以选择何时进行解决。一些引擎在进行解决之前将所有多重采样保留在初始照明和HDR后处理中。 - Andon M. Coleman
@MooseBoys:那些保持整个管道多重采样的...可以有效地说是超采样。否则,如果在照明期间解决,粒度不是1:1,就像如果一切都是超采样一样。 - Andon M. Coleman
显示剩余2条评论

3
多样本抗锯齿技术在几个亚像素位置上采样了一个图元的可见性,但是每个像素内部只对一个图元进行了一次着色。延迟着色技术对几何属性进行采样,并将着色推迟到第二阶段,在此过程中,原始图元和可见性采样之间的对应关系丢失,这使得难以避免冗余着色。此外,由于每个样本需要存储所有属性,因此在高分辨率和高可见性采样的情况下,几何缓冲区可能会变得非常大。——摘自《延迟属性插值着色》的介绍。

正如《反锯齿延迟渲染文章》所述,似乎有可能将与可见性覆盖相关的信息存储在另一个缓冲区中。 - Steven Lu

1
用MSAA实现延迟渲染是完全可能的,但这会增加渲染器的复杂度。
执行此操作的天真方法是(假定您目前在片段/像素着色器中应用光照):
- 使用每个像素N个样本来渲染您的G缓冲区(包括深度缓冲区) - 在光通道期间,对于每个像素,迭代N个样本以执行着色(在最后“平均”值)
这显然具有巨大的成本(因为如果您针对全高清渲染运行8个像素采样,则实际上正在进行8倍全高清分辨率的着色)。
幸运的是,可以对此过程应用一些优化(假设您的深度缓冲区采用支持模板的格式(d24s8常用于此)):
- 如上所述,使用每个像素N个样本来渲染您的G缓冲区。 - 在模板状态上设置一个模板位(如果您已经使用模板,则需要为此保留一个标志位),并将写入掩码设置为仅写入该位(假设您的模板在帧开始时被清除为零)。禁用读取测试,此位仅为写入模式。 - 运行“采样边缘检测”传递,您需要检查样本间深度/法线/颜色差异,如果其中某些高于某个值,则是边缘并需要对每个样本进行着色。否则可以在每个像素上运行着色。如果您的像素不是边缘,请执行丢弃操作(因此模板位仅包含边缘像素)。 - 将模板测试读取掩码设置为与写入掩码相同的值,并将比较设置为NOT_EQUAL,参考值= 1 - 在每个像素上运行着色通道(边缘像素将无法通过模板测试,因此片段着色器不会为其调用)。 - 将模板测试比较设置为EQUAL - 在每个样本的像素xN样本上运行着色通道(非边缘像素将无法通过模板测试,因此片段着色器不会为其调用)。
这将为您带来2个额外的传递成本,但将允许您可能只对总像素的5%进行样本运行(当然根据场景而定,甚至可以更低)。
现在,如果您在计算着色器中应用光照(基于Tile的延迟),MSAA和延迟也可以很好地工作。这主要针对没有阴影的点光源/聚光灯。
这是一个长话题,但大致上您使用迷你视锥和TGSM来剔除屏幕的一小部分并进行边缘检测,并使用TGSM障碍来首先对与筛选出的光源不相交的所有非边缘像素应用,然后应用Edge pixels,这都在一个传递中完成。
对于小灯光的另一种技术是为所有点光源渲染小剪裁矩形(您可以使用N个实例绘制带有6个顶点的矩形,其中N是灯光数量)。在顶点着色器中构建一个包围您的球体/锥体的矩形,并在片段着色器中执行点/聚光距离测试。这大大限制了要处理的像素数量。在这种情况下,您使用与上述相同的模板技术。
此时,您拥有一个不带多重采样的缓冲区,其中包含着色的不透明内容,但您可能有一些透明对象(当然希望这些对象能够启用MSAA)。
因此,通常透明对象将进入某种简化的正向着色过程。
步骤如下:
1. 附加一个MSAA彩色纹理(没有深度)。 2. 将您的着色纹理复制到MSAA纹理上。 3. 在MSAA彩色目标的顶部重新附加深度模板。 4. 将深度写入设置为false,深度测试设置为LESS或LESSEQUAL(根据您的喜好)。 5. 绘制透明对象(这将启用MSAA并进行正确的深度测试)。
注意事项:
1. 如果使用计算瓷砖,可以将每个瓷砖的光索引/计数存储到缓冲区中,因此透明通道可以利用它(在您的透明碎片材质中计算瓷砖索引,然后只需要迭代预先计算出的剔除光线即可)。 2. 由于现在可以在片段着色器中同时写入缓冲区和纹理,因此如果使用剪裁矩形技术,则还可以同时构建瓦片数据(从像素计算瓦片索引并将光索引附加到缓冲区),因此可以使用此技术来构建一些加速结构,以便进行进一步的透明传递。 3. 由于您将使用模板进行边缘检测,因此还可以保留另一个位,该位由所有不透明对象设置(这也将避免“天空片段”进入光着色器)。它还允许您设置未照亮的材料(在绘制之前,请确保您不会将其写入该位。这将需要通过传输子流程应用阴影(将所有未照亮的像素按原样写入着色纹理),但这非常快速。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接