GPU Gems 2 · Chapter 3. Inside Geometry Instancing

把重复物体拆成共享的几何包与逐实例属性,比较 static batching、dynamic batching、vertex constants 和 Geometry Instancing API 的更新边界。

学习目标

  • 能把可复用的顶点、索引、材质与逐实例的变换、颜色、动画参数分开描述
  • 能比较 static batching、dynamic batching、vertex constants 和 Geometry Instancing API 的更新代价与灵活性
  • 能根据实例是否静态、是否需要 skinning、数量是否超过常量容量,为一个场景选择合适策略
  • 能解释两个 vertex stream 的更新频率如何让 GPU 在不复制几何数据的情况下展开多个实例

重复的树、石头、士兵和粒子往往共享同一张网格,却拥有不同的位置、朝向、颜色或动画状态。最直接的做法是复制整套顶点;这样代码容易写,但 GPU 看到的是许多份相同拓扑,内存和提交次数一起增长。

本章把问题拆成两个更新频率:几何本身通常很少变,实例属性可能每帧变。只要保持这两个频率分离,渲染器就能用一个 GeometryPacket 生成很多 GeometryInstance。

1. 先分清 packet、attributes 与 instance

是“不变的那一侧”。它描述三角形如何连接、顶点有哪些属性,以及这批几何使用哪套材质;它不应该因为某一棵树移动而被复制。

是“变化的那一侧”。一条记录可以包含模型矩阵、颜色、LOD 选择或动画相位。它的布局要由 vertex shader 约定,不能把 packet 中的顶点索引误当成实例编号。

是渲染器真正提交的逻辑对象。多个实例可以共享 packet,但每个实例仍需有自己的变换和可见性状态。

GeometryInstance = GeometryPacket + InstanceAttributesGeometryPacket顶点、索引、材质一批实例共享InstanceAttributesT0位置 + 旋转T1位置 + 旋转T2位置 + 旋转每个实例不同GPU 只需重复读取同一几何包,再为每个实例应用不同属性
把不变的三角形拓扑和每棵树、每名士兵各自的变换属性拆开,才有复用几何的空间。

这个拆分还影响批次排序:渲染器可以先按材质和 GeometryPacket 分组,再在组内排列实例属性。这样状态切换发生在批次边界,而不是每遇到一个物体就重新绑定整套几何。

2. 四种 batching 路径

把重复几何在加载期或构建期合并。它的 CPU 更新很低,适合室内装饰、静态建筑或完全不会移动的物体;代价是灵活性低,单个实例很难再独立更新。

把数据更新放到帧循环里。它适合需要 skinning、每个实例顶点真正不同,或其他静态布局无法表达的场景,但上传和 CPU 组批的代价最高。

四种 batching 策略:把更新责任放在哪里?策略实例数据CPU 更新灵活性Static batching预先合并Dynamic batching每帧流式Vertex constants常量寄存器受容量限Geometry Instancing API双 stream选择标准:实例是否静态、是否要 skinning、是否超出常量容量
四种策略不是谁永远更快:静态程度、动画需求和实例数量决定可接受的更新路径。

第三条路径是把变换写入 vertex shader 可读的常量寄存器。它比复制几何轻,但实例数量被常量容量限制;同时 shader 需要按实例索引读取正确的矩阵,不能把“数量很多”当作它的优势。

适合实例少、属性结构固定且 shader 可控的批次。若实例需要复杂的动态数据,或者数量已经接近寄存器上限,就应该换用真正的实例属性 stream。

3. Geometry Instancing API:让 GPU 组合两条 stream

将 GeometryPacket 放进一个 stream,将 InstanceAttributes 放进另一个 stream。顶点 stream 对一个实例反复使用,属性 stream 每个实例提供一次,GPU 在顶点阶段把二者组合。

Geometry Instancing API:两个更新频率不同的 streamStream 0GeometryPacket顶点频率:每个实例复用Stream 1InstanceAttributes属性频率:每个实例一次GPU instance expansioninstance 1instance 2instance 3instance 4同一拓扑 + 不同变换
静态 stream 只上传一次,实例 stream 每个实例提供一次属性;GPU 将两者组合成许多副本。

这条路径的边界必须说清楚:它节省的是重复几何的内存和 CPU 组批成本,不会把不同拓扑魔法般变成相同拓扑。若实例需要不同顶点数量或不同索引结构,就要拆批、改用 dynamic batching,或采用更合适的几何表示。

4. 用场景而不是口号做选择

场景选择:相同几何只是起点室内静态装饰Static batching大量行走士兵Dynamic batching少量可控实例Vertex constants植被 / 树木 / 粒子Geometry Instancing API关键约束:同一 GeometryPacket;不同网格不能靠实例属性“变成同一个网格”
先判断几何是否相同、实例是否会动,再决定是合并、流式、常量还是专用实例化 API。

可以用三问快速筛选:第一,所有对象是否真的共享一个 GeometryPacket?第二,变化发生在变换和少量属性,还是顶点本身需要重写?第三,实例数是否超出 vertex constants 的可用容量?

静态建筑优先考虑 static batching;大量需要骨骼或顶点级动画的士兵可以接受 dynamic batching;少量、布局稳定的实例可用 vertex constants;植被、树木和粒子这类同拓扑的大量副本,则更适合保留静态几何 stream,再更新实例属性。

5. 动手实验:改变实例属性的更新边界

先预测:把实例数量从 12 调到 24,哪种策略仍然只需要一份三角形拓扑?再打开或关闭动画,观察“静态几何”与“实例属性”哪一侧发生变化。

Geometry instancing 实验

同一组几何包保持不变,只改变实例属性的更新方式。

12 个实例 · 实例属性随帧变化

Geometry Instancing API

数据路径:实例 stream

绘制组织:一组几何

相同拓扑的大量实例优先考虑

实验中的三角形刻意保持同一拓扑。策略选择只改变数据路径说明,实例数量改变只改变属性记录的数量;这能帮助你把“画了很多物体”和“复制了很多几何数据”区分开。

小结

  • geometry packet 保存可共享的拓扑,instance attributes 保存逐实例变化。
  • static batching 最省更新,dynamic batching 最灵活,vertex constants 受容量约束。
  • Geometry Instancing API 用两个更新频率不同的 stream 让 GPU 复用同一份几何。
  • 实例化只适用于相同 GeometryPacket;不同拓扑必须拆批或选择别的表示。
  • 场景选择应同时考虑静态程度、动画需求、实例数量和 shader 能力。

练习

问题 1|拆数据 一个场景里有 500 棵同网格树,每棵树的位置和颜色不同。哪些字段应该进入 geometry packet,哪些字段应该进入 instance attributes?

问题 2|选策略 100 个需要骨骼动画的相同士兵和 1000 个只改变变换的树实例,是否应该使用同一种 batching 策略?说明你的判断边界。

问题 3|找上限 使用 vertex constants 后,实例数量从 50 增加到 200,画面只出现前一部分实例。你先查哪里,而不是立刻复制顶点?

名词解释

本章出现的专业名词,用大白话再讲一遍。

geometry packet
可共享的顶点、索引、材质和拓扑数据。
instance attributes
描述每个实例变换、颜色或动画状态的属性记录。
geometry instance
一个 geometry packet 与一条实例属性记录的组合。
static batching
在加载或构建阶段合并静态几何、减少运行时更新的方式。
dynamic batching
每帧流式重建或更新批次、换取顶点级灵活性的方式。
Geometry Instancing API
用静态几何 stream 和实例属性 stream 让 GPU 展开多个实例的接口。
vertex constants instancing
把逐实例数据放进 vertex shader 常量空间的实例化方式。

资料与写作方式声明

本章以GPU Gems 2 · Chapter 3. Inside Geometry Instancing权威目录界定学习范围,并结合正文列出的技术资料独立重写;不宣称复现原书正文,也不沿用原作表述。

原作版权归作者与出版社所有;本站原创教学结构与表述仅供学习交流。

讨论

评论区加载中…