Direct3D 12 保守光栅化

保守光栅化为像素渲染增加了一定的确定性,这对碰撞检测算法特别有帮助。

概述

保守光栅化是指将至少由渲染基元部分覆盖的所有像素光栅化,同时也意味着会调用像素着色器。 正常行为是采样,而启用保守光栅化时不使用采样。

在许多情况下,保守光栅化非常有用,包括在碰撞检测、遮挡剔除和图块式渲染中实现确定性。

例如,下图显示了一个使用保守光栅化渲染的绿色三角形,它的外观与在光栅器中相同(使用 16.8 定点顶点坐标)。 褐色区域称为“不确定性区域”- 表示三角形扩展边界的概念性区域,需确保光栅器中的基元相对于原始浮点顶点坐标而言是保守的。 每个顶点上的红色方块显示如何计算不确定性区域:扫描方块。

灰色的大方块显示要渲染的像素。 粉红色方块显示使用“左上规则”渲染的像素。当三角形的边缘越过像素的边缘时,将应用该规则。 在正常情况下,系统可能会剔除某些误报(设置了原本不应该设置的像素),但不一定总会剔除。

左上规则

与管道交互

光栅化规则交互

在保守光栅化模式下,应用光栅化规则的方式与未启用保守光栅化模式时相同,但对于上述“左上规则”和“像素覆盖”例外。 必须使用 16.8 定点光栅器精度。

对于硬件使用完整浮点顶点坐标时不会覆盖的像素,仅当这些像素位于不大于定点域中像素的一半的不确定性区域内时,才可以包含这些像素。 将来的硬件有望达到第 2 层中指定的紧密不确定性区域。 请注意,此要求会阻止银色三角形进一步扩展为超过所需的大小。

类似的有效不确定性区域也适用于 InnerCoverage,但它会更紧密,因为在此情况下,没有任何实现需要更大的不确定性区域。 有关更多详细信息,请参阅 InnerCoverage 交互

内部和外部不确定性区域必须大于或等于定点域中的子像素网格大小的一半,或某个像素的 1/512。 这是最小的有效不确定性区域。 1/512 取自 16.8 定点光栅器坐标表示形式,以及将浮点顶点坐标转换为 16.8 定点坐标时应用的近似值舍入规则。 如果光栅器精度发生变化,1/512 可能会变化。 如果某个实现实现此最小不确定性区域,当不确定性区域的某个边或角落在某个像素的边或角上时,这些值必须遵循左上规则。 应将不确定性区域的裁剪边视为最靠近的顶点,即,将它算作两条边:在关联的顶点上联接的两条边。 使用最小的不确定性区域时需要应用左上规则,否则,保守光栅化实现无法光栅化当禁用保守光栅化模式时可能覆盖的像素。

下图演示了扫描围绕定点域中基元边缘(即,顶点已由 16.8 定点表示形式量化)的方块后生成的有效外部不确定性区域。 此方块的维度基于有效的外部不确定性区域大小:对于像素的 1/2,此方块的宽度和高度是 1 个像素;对于像素的 1/512,此方块的宽度和高度是 1/256 个像素。 绿色三角形表示给定的基元,红色虚线表示绑定在高估保守光栅化绑定中,纯黑色方块表示沿基元边缘扫描,蓝色棋盘区域是外部不确定性区域:

外部不确定性区域。

多重采样交互

无论 RenderTarget/DepthStencil 图面中的样本数 (,或者是否) 使用 ForcedSampleCount ,保守光栅化光栅化的像素都会覆盖所有样本。 无论各个样本位置是否在基元范围内,都不会对其进行测试。

样本掩码交互

应用 SampleMask 光栅器状态的方式与未为 InputCoverage 启用保守光栅化时相同,但不影响 InnerCoverage(即,不会通过 AND 运算符将它与使用 InnerCoverage 声明的输入合并)。 这是因为 InnerCoverage 与 MSAA 样本是否屏蔽无关:0 InnerCoverage 仅表示不保证像素完全覆盖,也不表示不会更新任何样本。

深度/模具测试交互

针对保守光栅化的像素继续处理深度/模具测试的方式,与针对未启用保守光栅化时覆盖的所有样本的处理方式相同。

继续处理所有覆盖的样本可能会导致有效的深度外插,必须将深度外插钳接到未启用保守光栅化时指定的视区。 这类似于对样本计数大于 1 的 RenderTarget 使用像素频率内插模式,不过,对于保守光栅化,它是进入可以外插的固定函数深度测试的深度值。

使用深度外插时的提前深度剔除行为是不确定的。 这是因为某些提前深度剔除硬件无法正常支持外插的深度值。 但是,即使硬件能够支持外插的深度值,使用深度外插时的提前深度剔除行为也会出现问题。 可以通过将像素着色器钳接到所要光栅化的基元的最小和最大深度值,并将该值写入 oDepth(像素着色器输出深度寄存器),来解决此问题。 由于 oDepth 写入,在此情况下,实现必须禁用提前深度剔除。

帮助器像素交互

应用帮助器像素规则的方式与未启用保守光栅化时相同。 在此过程中,所有像素(包括帮助器像素)必须根据 InputCoverage 交互部分中的指定准确报告 InputCoverage。 因此,完全未覆盖的像素将报告 0 覆盖度。

输出覆盖交互

保守光栅化像素的输出覆盖 (oMask) 行为与未对所有样本启用保守光栅化时相同。

InputCoverage 交互

在保守光栅化模式下,填充此输入寄存器的方式如同未针对给定保守光栅化像素启用保守光栅化时覆盖了所有样本时一样。 也就是说,所有现有交互都 (例如 SampleMask 应用于) ,并且对于保守光栅化像素,LSB 中的InputCoverage前 n 个位设置为 1,给定在输出合并处绑定的每个像素 RenderTarget 和/或 DepthStencil 缓冲区的 n 个样本,或一个 n 个示例 ForcedSampleCount。 余下的位均为 0。

无论是否使用保守光栅化,此输入在着色器中均可用,不过,保守光栅化会改变其行为,只显示所有覆盖的样本(或者不显示帮助器像素的任何样本)。

InnerCoverage 交互

此功能对于第 3 层是必需的,并且只能在第 3 层中使用。 如果实现支持低于第 3 层的层,则运行时无法为使用此模式的着色器创建着色器。

像素着色器可以使用一个 32 位标量整数“系统生成值”:InnerCoverage。 仅当保证像素完全位于当前基元中时,此位域才包含给定保守光栅化像素的、设置为 1 的 LSB 中的位 0。 未设置位 0 时,必须将所有其他输入寄存器位设置为 0,但是,当位 0 设置为 1 时,这些寄存器位是不确定的(本质上,此位域表示一个布尔值,false 必须确切地为 0,但 true 可以是任何奇数(即已设置 0 位)非零值)。 此输入用于低估的保守光栅化信息。 它告知像素着色器当前像素是否完全位于几何体内。

当分辨率大于或等于运行当前绘图操作时的分辨率时,必须将此算作贴靠错误。 不得出现误报(由于分辨率大于或等于运行当前绘图操作时的分辨率而出错,因此未完全覆盖像素时设置 InnerCoverage 位),但允许漏报。 总而言之,实现不得错误地将像素标识为完全已覆盖,否则光栅器中不会使用完整的浮点顶点坐标。

对于硬件使用完整浮点顶点坐标时完全覆盖的像素,仅当它们与内部不确定性区域相交时,才能将其忽略。在这种情况下,该区域不得大于定点域中的子像素网格的大小,或某个像素的 1/256。 换句话说,完全位于内部不确定性区域边界内的像素必须标记为完全已覆盖。 下图中的粗黑虚线演示了不确定性区域的内部边界。 1/256 取自 16.8 定点光栅器坐标表示形式,如果光栅器精度发生变化,此值可能也会变化。 此不确定性区域足以应对在光栅器中将浮点顶点坐标转换为定点顶点坐标导致的贴靠错误。

在光栅化规则交互中定义的 1/512 最小不确定性区域要求在此处也同样适用。

下图演示了扫描围绕定点域中基元边缘(即,顶点已由 16.8 定点表示形式量化)的方块后生成的有效内部不确定性区域。 此方块的维度基于有效的内部不确定性区域大小:对于像素的 1/256,此方块的宽度和高度为 1/128 个像素。 绿色三角形表示给定的基元,粗黑虚线表示内部不确定性区域的边界,纯黑色方块表示沿基元边缘扫描的方块,橙色棋盘区域是内部不确定性区域:

内部不确定性区域。

使用 InnerCoverage 不会影响是否对像素进行保守光栅化,即,在启用保守光栅化模式的情况下使用其中一种 InputCoverage 模式不会影响到要光栅化哪些像素。 因此,如果使用 InnerCoverage 并且像素着色器正在处理未完全由几何体覆盖的像素,则其值为 0,但是,像素着色器调用的样本将会更新。 这是不同于 InputCoverage 为 0 的情况,后者表示不更新任何样本。

此输入与 InputCoverage 互斥:不能同时使用两者。

若要访问 InnerCoverage,必须将它声明为某个像素着色器输入寄存器中的单个组件。 声明中的内插模式必须是常量(内插不适用)。

InnerCoverage 位域不受深度/模具测试的影响,也不会通过 AND 运算符与 SampleMask 光栅器状态合并。

此输入仅在保守光栅化模式下有效。 如果未启用保守光栅化,InnerCoverage 会生成不确定的值。

需要使用帮助器像素,否则不会由基元覆盖的像素着色器调用的 InnerCoverage 寄存器必须设置为 0。

属性内插交互

属性内插模式将保持不变,其处理方式与未启用保守光栅化(使用视区缩放和定点转换的顶点)时相同。 由于保守光栅化像素中的所有样本均被视为已覆盖,因此,它对于外插的值是有效的,类似于对样本计数大于 1 的 RenderTarget 使用像素频率内插模式。 质心内插模式生成的结果与相应的非质心内插模式相同;在此情况下,质心的概念没有意义 – 其中的样本覆盖度为“完全”或为 0。

保守光栅化允许退化三角形生成像素着色器调用,因此,退化三角形必须使用为所有内插值的顶点 0 分配的值。

裁剪交互

DepthClipEnable 光栅器状态设置为 FALSE () 时,启用保守光栅化模式并禁用深度剪辑时,基元段的属性内插可能存在差异,该段在 0 <= z <= w 范围之外,具体取决于实现:从基元相交相关平面 () 附近或远) , 或 属性内插的行为与禁用保守光栅化模式时的行为相同。 但是,无论保守光栅化模式为何,深度值行为都会相同,即,仍然必须为深度范围以外的基元分配最接近视区深度范围限制的值。 0 <= z <= w 范围内的属性内插行为必须保持不变。

裁剪距离交互

启用保守光栅化模式时,裁剪距离是有效的,保守光栅化像素的行为与在覆盖所有样本的情况下未启用保守光栅化时相同。

请注意,保守光栅化可能会导致 W 顶点坐标外推,这可能会导致 W <= 0。 这可能导致按像素裁剪距离实现在透视除以无效 W 值的裁剪距离上运行。 剪辑距离实现必须防止对顶点坐标 W <= 0 (像素调用光栅化,例如,在保守光栅化模式下) 由于推断。

目标独立的光栅化交互

保守光栅化模式与目标独立的光栅化 (TIR) 兼容。 TIR 规则和限制适用,保守光栅化像素的行为与覆盖所有样本时相同。

IA 原型拓扑交互

未为线条或点基元定义保守光栅化。 因此,如果在启用保守光栅化的情况下将指定点或线条的基元拓扑馈送到光栅器单元,这些拓扑会产生不确定的行为。

调试层验证会验证应用程序是否未使用这些基元拓扑。

查询交互

对于保守光栅化像素,查询行为与覆盖所有样本的情况下未启用保守光栅化时相同。 例如,对于保守的光栅化像素,D3D12_QUERY_TYPE) D3D12_QUERY_TYPE_OCCLUSION和 D3D12_QUERY_TYPE_PIPELINE_STATISTICS ( 的行为必须与未启用保守光栅化时涵盖所有样本时的行为一样。

每次在保守保守光栅化模式下以保守方式像素化像素,像素着色器调用都应该递增。

剔除状态交互

所有剔除状态在保守光栅化模式下有效,遵循的规则与未启用保守光栅化时相同。

将保守光栅化的各种分辨率与其自身或者未启用保守光栅化的情况相比较时,某些基元可能出现不匹配的多面性(即,一个朝向背面,另一个朝向正面)。 应用程序可以通过使用来自D3D12_CULL_MODE) 的 D3D12_CULL_MODE_NONE ( 而不是系统 IsFrontFace 生成值来避免这种不确定性。

IsFrontFace 交互

IsFrontFace 系统生成值可在保守光栅化模式下使用,遵循未启用保守光栅化时定义的行为。

填充模式交互

保守光栅化的唯一有效 D3D12_FILL_MODE 是D3D12_FILL_SOLID,任何其他填充模式都是光栅器状态的无效参数。

这是因为,D3D12 功能规范指定线框填充模式应将三角形的边转换为线条,并遵循线条光栅化规则,而保守线条光栅化行为尚未定义。

实现详细信息

Direct3D 12 支持的光栅化类型有时称为“高估保守光栅化”。 此外还存在“低估保守光栅化”的概念,表示仅光栅化未由渲染基元完全覆盖的像素。 可以使用输入覆盖数据通过像素着色器获取低估保守光栅化信息,只有高估保守光栅化作为光栅化模式提供。

如果基元类型的任何组成部分与某个像素重叠,该像素将被视为已覆盖,然后光栅化。 如果基元的边或角落在像素的边或角上,则“左上规则”的应用将根据实现而定。 但是,对于支持退化三角形的实现,边或角上的退化三角形必须至少覆盖一个像素。

保守光栅化实现根据不同的硬件而异,且不会产生误报,这意味着,它们可能错误判断已覆盖某些像素。 出现此问题的可能原因是,光栅化中使用的定点顶点坐标固有地提供特定于实现的详细信息,例如基元增长或贴靠错误。 误报(与定点顶点坐标相关)之所以有效,是原因需要提供一定数量的误报才能让实现针对后期贴靠的顶点(即,已从浮点转换为光栅器中使用的 16.8 定点的顶点坐标)执行覆盖度评估,但会遵循原始浮点顶点坐标生成的覆盖度。

对于非退化后期贴靠基元的浮点顶点坐标,保守光栅化实现不会生成漏报:如果基元的任何组成部分与某个像素的任何组成部分重叠,则会光栅化该像素。

退化的(索引缓冲区中出现重复索引,或 3D 中出现共线)或者在定点转换后退化的(光栅化器中出现共线顶点)的三角形不一定会被剔除;两者都是有效的行为。 必须将退化三角形视为朝向背面,因此,如果应用程序需要特定的行为,可以使用背面剔除,或者对正面朝向进行测试。 退化三角形使用为所有内插值的顶点 0 分配的值。

有三个硬件支持层,此外,硬件可能不支持此功能。

  • 第 1 层实施最大 1/2 像素不确定性区域,不支持后期贴靠退化。 这非常适合用于图块式渲染、纹理贴图集、光线贴图生成和子像素阴影贴图。
  • 第 2 层将最大不确定性区域减小为 1/256,要求不剔除后期贴靠退化。 此层对于基于 CPU 的算法加速(例如体素化)很有帮助。
  • 第 3 层保留最大 1/256 不确定性区域,并添加了对内部输入覆盖的支持。 内部输入覆盖将新值 SV_InnerCoverage 添加到高级着色语言 (HLSL)。 这是一个可以在像素着色器的输入中指定的 32 位标量整数,表示低估保守光栅化信息(即,是否保证完全覆盖某个像素)。 此层对于遮挡剔除很有帮助。

API 摘要

以下方法、结构、枚举和帮助器类引用保守光栅化:

DirectX 高级学习视频教程:保守光栅化

光栅器有序视图

呈现