用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. 由于您将使用模板进行边缘检测,因此还可以保留另一个位,该位由所有不透明对象设置(这也将避免“天空片段”进入光着色器)。它还允许您设置未照亮的材料(在绘制之前,请确保您不会将其写入该位。这将需要通过传输子流程应用阴影(将所有未照亮的像素按原样写入着色纹理),但这非常快速。