三角网格就是全部由三角形组成的多边形网格。多边形和三角网格在图形学和建模中广泛使用,用来模拟复杂物体的表面,如建筑,车辆,人体,当然,还有茶壶等
任意多边形网格都能转换成三角网格。三角网格以其简单性而吸引人,相对于一般多边形网格,许多操作对三角网格更容易
14.1 表示网格
三角网格为一个三角形列表,所以最直接的表示方法是用三角形数组
三角网格需要存储三类信息:
- 顶点。每个三角形都有三个顶点。各顶点都有可能和其他三角形共享
- 边。连接两个顶点的边。每个三角形有三条边
- 面。每个三角形对应一个面。我们可以用顶点或边列表表示面
14.1.1 索引三角网格
维护两个列表:顶点表和三角形表
每个顶点包含一个 3D 位置,也可能有如纹理映射坐标、表面法向量、光照值等附加数据
每个三角形由顶点列表的三个索引组成。通常,顶点列出的顺序是非常重要的,因为我们必须考虑面的“正面”和“反面”。从前面看时,我们将用顺时针方向列出顶点
14.1.2 高级技术
想知道邻接信息,必须从三角形列表中搜索,另一种表达方法可以在常数时间内取得
方法是显式维护一个边列表,每一边由两个端点定义,同时维护一个共享该边的三角形列表
14.1.3 针对渲染的特殊表达
大多数图形卡并不直接支持索引三角网。渲染三角形时,一般是将三个顶点同时提交。这样,共享顶点会多次提交,三角形用到一次就提交一次
因内存和图形硬件间的数据传输是瓶颈,所以许多 API 和硬件支持特殊的三角网格式以减少传输量。基本思想是排序点和面,使得显存中已有的三角形不需要再次传输
按灵活性从高至低排列
- 顶点缓存
- 三角带
- 三角扇
14.1.4 顶点缓存
和其他缓存机制类似,顶点缓存基于最近使用的数据未来仍将被使用的原则。GPU 缓存一小部分最近使用的顶点,当 API 要发送顶点时,首先探测缓存内是否已存在。若没有,则发生脱靶,API 发送顶点,并更新缓存;若发现有,就命中,API 通知 GPU“使用缓存内位置 x 的顶点”
善用缓存,可能使发送到显卡的顶点数降低到平均每三角形少于一个
14.1.5 三角带
三角带是一个三角形列表,其中每个三角形都与前一个三角形共享一边
- 顶点 1,2,3 构成第一个三角形
- 顶点 2,3,4 构成第二个三角形
- 顶点 3,4,5 构成第三个三角形
“索引”信息不再需要,因顶点顺序已隐式定义了三角形
三角带的问题:
- 表达能力有限:实践中很多网格结构较为复杂,无法用一个三角带完整地表示,存在适用性的局限。
- 顶点发送冗余:当存在三个以上三角形共享一个顶点的情况时,即使采用三角带数据结构,该共享顶点也需要多次发送给图形卡,导致无法达到最佳的顶点发送效率,存在数据传输冗余的问题。
- 额外信息开销:顶点缓存机制虽有可能降低每个三角形发送的顶点数,但需要额外的簿记信息,如索引和缓存管理数据,这些额外信息对单个顶点来说相对较大,会在一定程度上影响操作速度。
分别渲染两个长为 n 的三角带所需时间长于渲染一个长为 2n 的三角带。于是,我们经常通过使用退化三角形(面积为 0)连接多个三角带,从而将整个网格置于一个连续的三角带中
-些硬件(如 PS2 上的 GS)可以跳过三角带中的三角形,方法是通过一个顶点上的标志位指出“不必绘制"此三角形。这给我们一种方法可以有效的从任意点开始新三角带而不必重复顶点或使用退化三角形
⬆️置灰行被标记为“不必绘制”
14.1.6 三角扇
与三角带类似,但不如三角带灵活,故比较少用
三角扇第一个点必须为所有三角形共享,实践中不太经常能遇到这样的场景。并且,三角扇不能像三角带那样连接(用退化三角形)。所以三角扇只能在特殊场合应用
14.2 额外信息
三角网可在三角形(面)或顶点级(点)保存额外信息
14.2.1 纹理映射坐标
纹理映射是将位图(称作“纹理图”或简称“纹理”)贴到多边形表面的过程。通常,在顶点保存纹理映射坐标,三角形面中其余各点的坐标通过插值进行计算
14.2.2 表面法向量
许多应用程序中,网格上的各点都需要一个表面法向量。它可以用来
- 计算光照
- 进行背面剔除
- 模拟粒子在表面“弹跳”的效果
- 通过只考虑正面而加速碰撞检测
表面法向量可能保存于三角形级或顶点级,或两者皆有
三角形级法向量可以通过 12.5.2 节的技术轻松获得,而顶点级法向量的计算则困难一些
- 顶点处没有法向量定义,因为此处网格表面不连续
- 根据产生三角网的方法,连续表面的法向量不一定现成可得(除非网格是自动生成的)
通常的计算方法,是平均相邻三角形的表面法向量并将结果标准化。这个方法在大多数情况下都能工作良好,但在某些情况下则不然
- “公告板”:两个法向量刚好相反的三角形共享一个顶点,其平均值为零不能标准化
- 应用 Gouraud 着色时:光照是按顶点法向量逐点计算的。若使用平均三角形法向量计算的顶点法向量,某些应该有尖锐边缘的地方会显得“过于平滑”
另一个小问题是这种平均方法会导致结果向较多拥有相同法向量的三角形偏移。例如,若干三角形共享一个顶点,但其中两个共面,使得平均计算出来的顶点法向量向两共面倾斜
14.2.3 光照值
光照值用于沿表面的插值,典型的方法如 Gouraud 着色。有些时候,顶点处仅保存法向量,渲染时动态计算光照值。另一些情况下,我们要自己指定光照值
14.3 拓扑与一致性
三角网格的拓扑是指当在三角网格中不考虑顶点位置与其他几何性质的逻辑连通性时,两个顶点数相同且三角形互联方式一致的三角网格为同拓扑的,即使它们对应的物体完全不同。例如球体和立方体是拓扑一致的
有一种特殊网格称作封闭网格,又称作“流形”。概念上,封闭网格完美地覆盖物体表面;网格中没有间隙,从外面完全无法看到任何三角形的背面。这是一种重要的网格,它的点和边组成形式就象平面图,即如果将顶点当成平面点,用直线连接顶点,此封闭网格可以画在一个2D平面上,而且没有边交叉。平面图符合 Euler 方程:v-e+f=2,其中 v 为顶点数,e 为边数,f 为网格上的面数
实践中,我们经常遇到拓扑异常的三角网格,导致网格不封闭:
- 孤立顶点:顶点未被任何三角形使用
- 重复顶点:完全相同的顶点。使用这些点的三角形几何上相邻而逻辑上不相邻。多数情况下,我们不希望看到这种现象,应该删除(参考 14.4.2 节)
- 退化三角形:使用一顶点超过一次的三角形。意味着这个三角形没有面积。一般,这种三角形应该删除。(但在 14.1.3 节,我们特意使用退化三角形来连接三角带。)
- 开放边:仅为一个三角形所使用
- 超过两个三角形共享的边:封闭网格中,任一边必须为两个三角形共享
- 重复面:网格中包含有两个或更多相同的面。这是不希望看到的,应该去掉多余面而只保留一个。
根据应用的不同,上述异常可能是严重的错误,也可能是小错误,或者无关紧要
14.4 三角网格操作
14.4.1 逐片操作
- 渲染:逐三角形
- 转换:逐顶点
14.4.2 焊接顶点
目的:
- 去除重复顶点,节约内存
- 使几何上相邻的边在逻辑上也是相邻的
步骤:
- 扫描三角形列表,将对 B 的引用全部替换成对 A 的引用
- 现在 B 是孤立的,将它从顶点列表中删除
有几个细节需要明确
- 焊接前应先去除孤立点
- 当两个顶点来自“细长”三角形,焊接可能会产生退化三角形。这样的三角形应被删除
-
焊接时,似乎应该用原顶点的平均作为新顶点,这在只有少量顶点需要焊接的情况下是个好主意。然而,焊接自动进行时可能引起“多米诺”效果,导致原来不在误差容限内的多个点被焊接
解决方法(但不应为不显著地性能提升而增加复杂性):
- 先找出所有误差容限内的顶点组,再焊接它们
- 不考虑已经焊接过的点
- 记录原顶点坐标,当它与比较点距离在容限外就不焊接
⬆️A 和 C 本不应被焊接,但我们“聪明地”将它们焊接了
记住焊接的目的:去除重复顶点,而不是为了消减网格——大量减少三角形数。要尽量保持三角网格外形不变
14.4.3 面拆分
与焊接刚好相反,面拆分即复制顶点,使边不再被共用
目的:使几何间断的地方拓扑也间断
⬆️上图只是为了显示,实际上新顶点并没有移动,与原来的顶点重合
14.4.4 边缩坍
边缩坍是将边缩减为顶点的方法。与之对应的是顶点拆分。
⬆️使边的两个顶点变为一个,共享该边的三角形消失
14.4.5 网格消减
是将三角形和顶点数较多的网格变为三角形和顶点数相对较少的网格,且要求网格外观和主要顶点尽可能保持不变
方法之一是边缩坍,但要选出操作的边相对费时