RealTime-Rendering25-Primitive Restart
Primitive Restart
Primitive Restart是渲染中的一个重要特性,主要用于高效渲染多个独立的图元序列(例如三角形条带、线条条带等),而无需发出多个绘制调用或在索引数据中插入“退化”图元(即面积为零或不可见的图元)。下面从概念、工作原理、使用场景、优缺点以及在不同图形API中的具体实现等方面进行详细解释。
基本概念
在实时渲染中,我们常用索引缓冲来复用顶点数据,以减少重复顶点传输和处理。当使用条带(strip)类型的图元(如 GL_TRIANGLE_STRIP、GL_LINE_STRIP)时,顶点顺序决定了几何连接方式。例如,三角形条带中,每新增一个顶点就会与前两个顶点构成一个新三角形。
然而,条带只能描述单个连续的序列。如果需要绘制多个不相连的条带(比如多个独立的三角形条带组成的不同物体),传统做法有两种:
- 多个绘制调用:每个条带调用一次 glDrawElements,但会增加CPU/GPU之间传输的IO开销。
- 插入退化三角形:在条带之间添加额外的顶点索引,使生成的三角形面积为零(退化),从而视觉上不产生新几何。这种方法浪费带宽和处理周期,且需额外计算。
Primitive Restart提供了一种更优雅的方案:在索引序列中插入一个特殊的“重启标记”,告诉GPU“当前条带在此结束,从下一个顶点开始新的条带”。
工作原理
- 重启索引(Restart Index):开发者指定一个不会用作真实顶点索引的数值(例如对于16位索引,常用 0xFFFF;对于32位索引,常用 0xFFFFFFFF)。当GPU在处理索引缓冲时遇到这个值,它会结束当前条带的装配,并忽略该索引本身(不访问顶点数据),后续的索引则作为全新条带的开始。
- 硬件支持:现代GPU均原生支持该特性,无需软件模拟,因此效率极高。
- 示例:
假设有两个三角形条带:- 条带 A:顶点索引 [0,1,2,3] → 生成三角形 (0,1,2) 和 (1,2,3)
- 条带 B:顶点索引 [4,5,6,7] → 生成三角形 (4,5,6) 和 (5,6,7)
若不使用Primitive Restart,你需要两个绘制调用。若使用,可以将索引合并为:[0,1,2,3, RESTART, 4,5,6,7].在一次绘制调用中,GPU会正确处理两个条带。
为什么需要 Primitive Restart
- 减少绘制调用:将多个条带合并为一个批次,降低CPU向GPU提交命令的开销。
- 节省内存和带宽:无需插入退化三角形(每个条带交界处通常需要2个退化顶点),索引缓冲更紧凑。
- 简化数据生成:某些建模或地形分块算法直接产生多个条带,利用重启标记可轻松合并。
API示例
openGL
- 启用:glEnable(GL_PRIMITIVE_RESTART)
- 设置重启索引:glPrimitiveRestartIndex(index)
- 在索引缓冲中使用指定索引作为重启标记。
- 注意:OpenGL 3.1 之后还提供了 GL_PRIMITIVE_RESTART_FIXED_INDEX,针对特定索引格式使用固定重启值(如16位的0xFFFF),无需单独设置。
1 | glEnable(GL_PRIMITIVE_RESTART); |
vulkan
- 在创建图形流水线时,通过 VkPipelineInputAssemblyStateCreateInfo结构体的 primitiveRestartEnable成员开启。
- 重启索引是固定的:对于16位索引为 0xFFFF,32位为 0xFFFFFFFF,不可自定义。
- 在绘制命令中,索引缓冲内直接插入该值即可。
1 | // 在创建 Pipeline 时配置 |