中EN
概念页

三角形带、扇与索引网格

Triangle Strips, Fans & Indexed Meshes

让共享顶点的三角形成组送入管线,可以减少顶点着色器调用。三角形扇和带把每三角形发送的顶点数降到接近 1;现代 GPU 的标准做法是带单一索引缓冲的三角形网格,理论上每三角形只需处理约 0.5 个顶点,实际效果取决于变换后顶点缓存,由三角形顺序优化(Forsyth、Tipsify 等)决定。

三角形扇(16.4.1 节)

顶点列表 {v0,…,vn−1}\{v_0,\dots,v_{n-1}\},v0v_0 为中心顶点,第 ii 个三角形为 △v0vi+1vi+2\triangle v_0 v_{i+1} v_{i+2}(0≤i<n−20\le i<n-2)。任何凸多边形都能直接表示为扇。长度为 mm 的扇平均每三角形发送顶点数 va=3+(m−1)m=1+2mv_a = \frac{3+(m-1)}{m} = 1+\frac{2}{m} m→∞m\to\infty 时趋于 1;m=5m=5 时 va=1.4v_a=1.4。

三角形带(16.4.2 节)

第 ii 个三角形为 △vivi+1vi+2\triangle v_i v_{i+1} v_{i+2},nn 个顶点构成 n−2n-2 个三角形;朝向逐个交替,由首个三角形决定,内部按 [0,1,2]、[1,3,2]、[2,3,4]、[3,5,4]… 保持逆时针。平均顶点数与扇相同,m=20m=20 时 va=1.1v_a=1.1。相比三角形列表最多可省约三分之二的时间(避免重复发送、变换、裁剪)。适合草叶等边缘顶点不被其他带复用的物体,几何着色器输出多个三角形时也用带。可用重复顶点或重启索引在一个缓冲中存多条带。曾有大量研究如何把网格分解成带,随着索引网格普及已经式微。

三角形网格与欧拉公式(16.4.3 节)

连通平面图的 Euler-Poincaré 公式: v−e+f+2g=2v - e + f + 2g = 2 gg 是亏格(洞数,球为 0、环面为 1);面有多个环时为 v−e+2f−l+2g=2v-e+2f-l+2g=2。封闭模型每条边两个面、每面至少三条边,2e≥3f2e\ge 3f;全三角形时 2e=3f2e=3f,代入 g=0g=0 得 f=2v−4f=2v-4。经验法则:大型封闭三角网格的三角形数约为顶点数的两倍,每个顶点平均连接近 6 个三角形(度 / valence 约 6)。因此用带表示时每个顶点平均发送两次,而网格极限下每三角形只需 0.5 个顶点。有边界边、多个平滑组(立方体角点有三条法线 → 三条顶点记录)、纹理等数据变化时,顶点与三角形之比会升高。

顶点缓存与 ACMR

  • GPU 把顶点着色器结果放入 FIFO(或近似 LRU)的变换后顶点缓存,命中则不再调用顶点着色器;三角形随机顺序时缓存几乎无用。带算法相当于优化大小为 2 的缓存。缓存很小:PS3 约 24 个顶点,新 GPU 一般最多 32 个。
  • 平均缓存未命中率 (ACMR) (Hoppe):平均每三角形需处理的顶点数,范围从 3 到 0.5;给定缓存大小与顺序可精确计算。

缓存无关的网格布局(16.4.4 节)

  • Hoppe 的算法要预知缓存大小,假设偏大时收益明显下降。缓存无关 (cache-oblivious) 布局对任意大小都表现良好,也称通用索引序列。
  • Forsyth / Lin & Yu 贪心算法:按顶点在缓存中的位置和尚未处理的相连三角形数打分,处理总分最高的三角形;最近用过的三个顶点分略低,避免退化成带而形成类似 Hilbert 曲线的模式;剩余三角形少的顶点分更高,避免留下孤立三角形。缓存大小 12 时,30 个模型平均 ACMR 从 1.522 降到 0.664 或更低。
  • Tipsify (Sander 等):更快(非缓存无关),还尽量把最外层三角形排在前面以减少过度绘制(如咖啡杯先画外侧)。
  • Kapoulkine 在三家 GPU 上的比较:Intel 使用 128 项 FIFO、每顶点占三项或更多,AMD 与 NVIDIA 近似 16 项 LRU;Tipsify 和(稍逊的)Forsyth 跨平台表现较好。
  • 结论:离线预处理实际 O(n)O(n),无额外存储、不影响工具链,顶点阶段为瓶颈时能明显提升帧率,是成熟开发系统的常规环节(据称 PlayStation 网格工具链使用 Forsyth 算法)。

顶点缓冲与索引缓冲(16.4.5 节)

  • 顶点缓冲:连续内存中的顶点数组,单个顶点字节数称为步长 (stride)。交错缓冲(每顶点数据放一起)在 GPU 上通常更高效,但多流也并非要避免:多流的主要代价是更多 API 调用(CPU 受限时才值得在意);好处包括便于 SSE/AVX 处理、只有位置随时间变化时只更新该流。
  • 绘制方式:点、独立线段、折线、三角形列表、三角形扇、三角形带;DX10 起可附带相邻三角形顶点供几何着色器使用。
  • 索引缓冲:16 位无符号整数,网格大且支持时用 32 位。索引三角形列表最常用;索引三角形带省索引空间但生成和工具支持麻烦,实践中很少用。索引的优势:数据共享、三角形顺序任意、传输与存储通常更小。
  • 文件格式(如 Wavefront OBJ)用多套索引分别引用位置、法线、纹理坐标,但 GPU 只有一个索引缓冲:立方体在 GPU 上要 24 个顶点(每个角三条法线)和 12 个三角形的索引。把网格存进纹理用顶点拉取更紧凑,但无法利用变换后缓存。
  • 顶点缓冲中顶点顺序应与索引首次访问顺序一致,以减少变换前缓存未命中,这与三角形排序同样重要。
  • 流输出可以先把网格当点集在顶点着色器中处理、写入新顶点缓冲,再与原索引缓冲配对继续处理和显示。

GEA 的视角:三角形列表、顶点格式与顶点缓存优化(卷 II 11.3.5.3、11.3.6)

为什么是三角形

GEA 列出三角形成为实时渲染首选图元的理由(卷 II 11.3.5.3):最简单的多边形;永远共面(四个以上顶点不一定);在仿射变换和透视投影下仍是三角形(最多侧视时退化成线段);几乎所有图形硬件都围绕三角形光栅化设计,这可以追溯到《Doom》和《Ultima Underworld》的软件光栅器。三角网格是曲面的分段线性近似,把曲面拆成三角形的过程叫曲面细分 (tessellation),拆成三角形时也叫三角化(见 多边形三角化)。

原书问题(11.3.5.3):

  • 《Doom》(1993)并不光栅化三角形:它用 BSP 树按前后顺序遍历二维地图,把墙画成逐列的竖直纹理条带、把地板天花板画成水平跨度 (visplane),引擎本身也不支持任意三维多边形(Wikipedia "Doom engine";Fabien Sanglard《Game Engine Black Book: DOOM》)。《Ultima Underworld》(1992)倒确实是纹理映射的多边形渲染器。把三角形光栅化硬件的源头追到 Doom 不成立。
  • "透视投影下仍是三角形"只对完全位于相机前方的三角形成立。跨过相机平面(w=0w=0)的三角形投影后不再是一个有界三角形,所以管线必须先在齐次裁剪空间裁剪,裁剪后的多边形可能变成四边形乃至更多边,再重新三角化(见 坐标空间 中的裁剪部分)。

列表、索引列表、带与扇

  • 三角形列表:顶点按每三个一组放进顶点缓冲(DirectX)/顶点数组(OpenGL),简单但大量重复顶点,既浪费内存,也让重复顶点被变换和光照多次(卷 II 11.3.6.3)。
  • 索引三角形列表:每个顶点只存一次,用轻量的索引三元组组成三角形,索引放在索引缓冲/索引数组中;65,536 个顶点以内可用 16 位索引,更大的网格通常用 32 位(卷 II 11.3.6.4)。
  • 带与扇:预先规定顶点顺序,不需要索引缓冲又能减少重复。带中每个新顶点与前两个组成新三角形,前两个顶点每次交换位置以保持环绕方向一致;扇中每个新顶点与前一个顶点和第一个顶点组成三角形(卷 II 11.3.6.5)。
  • 补充:GEA 指出如今的 GPU 已不能再渲染三角形扇,因为其收益不足以抵消所需硬件的成本。本页 RTR4 部分仍把扇作为一种可用的图元介绍,阅读时注意这一时代差异。

    原书问题(11.3.6.5):"如今的 GPU 不能再渲染扇"说得过头。Direct3D 10 起确实去掉了三角形扇拓扑(MS Learn "Primitive Topologies"),Metal 也没有;但 Vulkan 核心规范仍有 VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN(仅 portability 子集可不支持),OpenGL 有 GL_TRIANGLE_FAN,桌面 GPU 驱动照常支持(Vulkan 规范 "Triangle Fans")。更准确的说法是:主流 API 和引擎已不再依赖扇,部分 API 已移除。

顶点格式与属性交错

  • 一个顶点的属性集合可写成 C 结构体,其布局叫顶点格式,例如只有位置的 Vertex1P(用于阴影体挤出、卡通描边、z 预通道等),位置-法线-UV 的 Vertex1P1N1UV,以及带漫反射色、镜面色、两套 UV、4 个关节索引和 3 个权重(第 4 个权重由前三个算出)的蒙皮顶点(卷 II 11.3.6.6)。
  • 属性组合的排列数会爆炸,管理大量顶点格式曾是图形程序员的头疼事。如今 GPU 能把多个同构属性数组动态交错:位置、法线、UV 分别放在三个顶点缓冲(或一个大缓冲的三段)里,由 API 拼成着色器看到的结构体——D3D11 用 ID3D11Device::CreateInputLayout 加 D3D11_INPUT_ELEMENT_DESC,OpenGL 用 glVertexAttribPointer。

顶点缓存优化

  • 顶点着色器的结果会被缓存,后续三角形引用缓存中的顶点就不必重算。三角形带一方面省索引缓冲,一方面访存更连贯;带索引的三角形带还能几乎消除顶点重复,同时保留带的缓存友好顺序(卷 II 11.3.6.7)。
  • 不拘泥于带的顺序也能优化:顶点缓存优化器是离线几何处理工具,考虑特定 GPU 的顶点缓存大小和替换策略来重排三角形。GEA 举例:索尼 PlayStation 的 Edge 几何处理库中的优化器比三角形带的吞吐高至多 4%;其他平台可用 NVIDIA Nsight 和微软 DirectXMesh。
  • 这与本页「顶点缓存与 ACMR」一致。GEA 还指出,传统管线即使用了优化器也有根本上限,这是网格着色器出现的原因之一,见 网格着色器管线。

PBRT 的视角:为什么用索引网格,以及离线渲染器怎样存三角形(PBRT 4e 6.5.1–6.5.2)

用欧拉-庞加莱公式估算存储

封闭网格满足 V−E+F=2(1−g)V-E+F=2(1-g)(gg 为亏格,即"把手"数)。三角网格每个面有 3 条半边、每条边由 2 条半边组成,所以 E=32FE=\frac32F。亏格的影响对大网格可以忽略,取 g=0g=0 代入得 F≈2VF\approx2V:面数约为顶点数的两倍,每个顶点平均被 6 个三角形引用。于是共享顶点时每个三角形约需 12 字节索引(3 个 32 位整数)加半个顶点的位置(6 字节),共 18 字节;直接存三个位置要 36 字节。每顶点法线和纹理坐标越多,节省比例越大。

pbrt 的做法

  • TriangleMesh 存位置、索引、可选的法线 n、切线 s、uv;加载时就把顶点变换到渲染空间,省去每条射线变到物体空间、再把交点变回来的开销(二次曲面则相反,在物体空间求交)。
  • BufferCache 对内容相同的数组只存一份(即使场景没有用实例化);书中的风景场景因此节省 400 MB 以上。缓存分成 64 个分片、各有读写锁,读时 lock_shared(),写时升级为独占锁并处理"另一线程先插入"的情况。位置变换到渲染空间后命中率低,索引和 uv 命中率高。
  • 每个 Triangle 只存两个 int(网格编号、三角形编号),共 8 字节;若用虚函数,虚表指针就会让它翻倍,这也是 pbrt 用标签指针而不用虚函数的原因之一(见 C++ 内存布局与对齐)。
  • 网格是否翻转法线(ReverseOrientation、变换是否改变手性)对整个网格相同,只存一次。
  • InteractionFromIntersection() 是独立函数(参数为网格指针和三角形索引)而不是成员函数,这样 GPU 路径在硬件求交返回三角形索引和重心坐标后可以复用同一份 CPU 代码。
  • 着色法线和切线按重心插值,切线经两次叉积正交化:保持 n\mathbf n 不变,修正 s\mathbf s。

原书问题

  1. realtime_rendering_4ed.pdf 第 16 章 Polygonal Techniques
  2. Game_Engine_Architecture_4th_Edtion_Volume_2_-_Jason_Gregory.pdf
  3. Jason Gregory《Game Engine Architecture》第 4 版(2026)卷 II 第 11 章 11.3.5.3、11.3.6 节
  4. PBRT 4e source excerpt: chapter 06 (Shapes)
  5. Pharr, Jakob, Humphreys《Physically Based Rendering: From Theory to Implementation》第 4 版(MIT Press, 2023)第 6 章 6.5.1–6.5.2 节,pbr-book.org/4ed/Shapes

相关页面

图形学网格整合与顶点法线Mesh Consolidation & Vertex Normals整合 (consolidation) 指把零散多边形合并成共享顶点的网格、让所有面朝向一致、判断是否为实体,并生成平滑的顶点法线与折痕边。这些步骤是背面剔除、折痕检测、阴影体、碰撞检测、3D 打印等的前提。图形学多边形三角化Polygon Triangulation模型数据很少恰好是渲染需要的格式。图形 API 和硬件都针对三角形优化,所以复杂多边形要先剖分 (tessellation),最常见的是三角化 (triangulation)。本页讲三维数据的来源、三角化算法(耳切法、凸剖分、模板缓冲法)、四边形对角线选择与纹理扭曲,以及边缘裂缝和 T 型顶点。图形学网格简化Mesh Simplification网格简化(数据缩减、抽取 decimation)在尽量保持外观的前提下减少三角形数,使应用能在低端机器上伸缩。核心操作是可逆的边折叠,用代价函数(典型是 Garland–Heckbert 的二次误差度量 QEM)排序;它产生的渐进网格可用于连续 LOD 与渐进传输,但自动化并非万能。图形学顶点压缩与精度Vertex Compression & Precision网格数据像纹理一样可以压缩:传输用的整体压缩(Draco 等)一次性解码,渲染用的定长压缩让顶点着色器在 GPU 上逐顶点解码,从而降低内存、带宽并改善缓存。另一面是精度问题:大世界中单一坐标系会让远离原点的物体抖动,需要相对相机的变换或分段原点。图形学顶点着色器Vertex Shader顶点着色器是程序员直接控制的第一个管线阶段,逐个处理输入顶点,至少要输出顶点在齐次裁剪空间里的位置。它不能创建或删除顶点,顶点之间也不能传递结果,正因如此可以大规模并行。图形学曲面细分与几何着色器Tessellation & Geometry Shader曲面细分、几何着色器和流输出是顶点着色器之后的三个可选阶段。曲面细分把曲面描述按需生成合适数量的三角形;几何着色器能把图元变成别的图元,但效率不好、实际用得少;流输出把处理后的顶点写回内存以便复用。图形学地形渲染Terrain Rendering地形渲染(游戏、Google Earth、开源大世界引擎 Cesium 等)要在巨大高度场上按距离分配细节,同时控制内存、绘制调用和 LOD 之间的裂缝与跳变。主流 GPU 友好方案有:类似纹理 clipmap 的几何 clipmap、按瓦片复用顶点缓冲只换索引缓冲的分块方案、基于四叉树和屏幕空间误差的 chunked LOD、预存所有过渡索引缓冲的受限四叉树,以及 GPU 细分和压缩等补充手段。任何方案都可叠加分形噪声以在拉近时提供细节,许多系统也在加载时程序化生成地形。图形学绘制调用与状态切换优化Draw Calls & State Change Optimization调用图形 API 的开销主要在 CPU 端的驱动里:状态切换昂贵,每次绘制调用有固定开销,大量小批次会让 GPU"饿死"。对策是:合理放置缓冲;按状态代价排序分组;共享纹理和着色器;合并网格和实例化以减少调用;并用一个整数排序键同时兼顾状态分组与前后排序。图形学网格着色器管线Mesh Shader Pipeline网格着色器管线(也叫网格几何管线)是 GPU 厂商为取代传统光栅管线前段而推出的新架构:光栅化器和像素着色器不变,网格着色器和可选的放大着色器(任务着色器)接管顶点、外壳、域和几何着色器的全部工作。它以类似计算着色器的线程组处理叫 meshlet 的小批图元,像启动计算着色器一样启动绘制,还能把部分应用层工作搬到 GPU 上。2019 年进入 DirectX 12,2022 年以 VKEXTmeshshader 扩展进入 Vulkan。图形学参数曲面与贝塞尔面片Parametric Surfaces & Bézier Patches参数曲面把参数曲线从一个参数推广到两个参数 (u,v),由少量控制点定义,运行时细分成任意多个三角形,是质量与速度之间的天然权衡:三角形多则着色和轮廓更好。矩形定义域的是贝塞尔面片(张量积曲面),三角形定义域的是贝塞尔三角形,后者是 PN 三角形和 Phong 细分的基础。图形学坐标空间Coordinate Spaces顶点从模型到屏幕要依次经过模型空间、世界空间、观察空间、裁剪空间、标准化设备坐标 (NDC)、窗口坐标。每一步变换都有明确目的,大部分可以用 4×4 矩阵表示并相乘合并。游戏数学光线求交测试Ray Intersection Tests光线求交用于拾取、光线追踪、地面高度查询等。本页给出四类经典测试:光线-球(代数解与带早期拒绝的几何优化解)、光线-盒(平板法与光线斜率法)、光线-三角形(Möller-Trumbore,直接从顶点求重心坐标与距离)、光线-多边形(先求平面交点,再投影到二维做交叉数测试)。共同思路是先做便宜的拒绝测试、推迟除法与开方、必要时降维。游戏开发C++ 内存布局与对齐C++ Memory Layout & Alignment理解 C/C++ 程序在内存中的样子,是写高性能引擎代码的前提。主要内容包括:声明与定义、内部与外部链接;可执行映像的几个段(text、data、BSS、rodata)以及栈和堆;结构体成员的自然对齐和填充;继承和虚函数如何改变类的布局(vpointer 和 vtable)。GEA 也简要回顾了面向对象:多重继承容易产生致命菱形,所以通常只允许 mix-in;组合往往比继承更好。

输入关键词开始搜索

滚轮或双指缩放 · 拖动平移 · 双击放大 / 还原