4.1 三维几何表示:顶点、索引与法线


4.1 三维几何表示:顶点、索引与法线

本节摘要:三维物体在计算机里最普遍的存在形式是三角形网格——一张顶点表加一张索引表。本节讲这套数据结构的组织方式与顶点复用的省内存逻辑,面法线与顶点法线的区别及计算方法,顺带一瞥细分曲面与参数曲面在"网格之外"的角色。它是第 4 章的起点:相机与投影作用的对象,就是这里定义的数据。

读完这节你该会什么

阅读完本节,你应当能够:

  1. 画出三角形网格的顶点表 + 索引表结构,算出索引化对显存的节省比例
  2. 用叉积计算面法线,说明顶点法线的面积加权平均求法
  3. 区分平面着色与光滑着色对法线的不同需求
  4. 说明参数曲面与细分曲面何时比静态网格更合适

一个立方体是怎么存进显存的

直觉方案:每个三角形存三个顶点坐标。一个立方体 12 个三角形 × 3 顶点 × 3 浮点 = 108 个数。但立方体只有 8 个角点,大量坐标被重复存储。工程方案是拆成两张表:顶点表存去重后的坐标,索引表存"第几个三角形用哪三个顶点":

import numpy as np vertices = [ # 顶点表:立方体 8 个角 (-1,-1,-1), (1,-1,-1), (1,1,-1), (-1,1,-1), (-1,-1, 1), (1,-1, 1), (1,1, 1), (-1,1, 1)] indices = [ # 索引表:12 个三角形各引用 3 个顶点编号 0,1,2, 0,2,3, # 后面 4,6,5, 4,7,6, # 前面 0,4,5, 0,5,1, # 下面 2,6,7, 2,7,3, # 上面 1,5,6, 1,6,2, # 右面 0,3,7, 0,7,4] # 左面 verts_used = 12 * 3 # 非索引化要存 36 个顶点副本 print(f"索引化: {len(vertices)} 个顶点复用出 {len(indices)//3} 个三角形") # 输出: 索引化: 8 个顶点复用出 12 个三角形 # 一百万三角形级的模型,索引化通常省 60% 以上的顶点存储, # 且 GPU 顶点缓存能直接复用变换结果,省的不止是内存

索引化还有个隐性收益:共享顶点的三角形在拓扑上"连着",网格变形、顶点动画(水波、布料)只需改顶点表,所有引用它的三角形自动跟随。

除了位置,顶点通常还携带一包属性:法线(三维)、纹理坐标(二维)、切线、颜色。顶点于是成了一个"结构体数组",按固定步长排布在显存里,管线按索引取用。

法线:多边形的朝向声明

面法线是垂直于三角形平面、指向外侧的单位向量,由两条边的叉积直接算出:

def face_normal(a, b, c): # 三点确定的面法线:两边叉积后单位化 n = np.cross(np.array(b)-np.array(a), np.array(c)-np.array(a)) return n / np.linalg.norm(n) n = face_normal((0,0,0), (1,0,0), (0,1,0)) print(n) # 输出: [0. 0. 1.] xy 平面上的三角形,法线朝 +z

叉积方向由顶点绕向决定,所以"顶点顺序"在模型文件里是有语义的:逆时针绕向配合右手定则确定正面。若导出时绕向混乱,光照会一半亮一半黑、背面剔除会错删正面——修法是统一绕向或按重心到顶点的方向校正,工具链里叫"重算法线"。

平面块(立方体面)用面法线就够,但光滑表面(球、皮肤)每个顶点需要一个"平滑法线":共享该顶点的所有面法线的平均(常按面积加权),插值后得到连续变化的朝向。这正是 5.2 节 Phong 着色与平面着色的分水岭:

平面着色:整个三角形用同一面法线,球体呈现明显棱面感 Gouraud/Phong 着色:顶点法线插值,棱面消失、表面光滑
def vertex_normal(v, faces_of_v, all_faces): # 面积加权平均共享面法线(面积 = 叉积模长的一半,此处用未归一化叉积即含面积因子) acc = np.zeros(3) for f in faces_of_v: a, b, c = all_faces[f] acc += np.cross(np.array(b)-np.array(a), np.array(c)-np.array(a)) return acc / np.linalg.norm(acc) cube_faces = [((0,0,1),(2,0,1),(2,2,1)), # 顶面 z=1 上的一个三角形 ((0,0,1),(2,2,1),(0,2,1))] print(vertex_normal((0,0,1), [0,1], cube_faces)) # 输出: [0. 0. 1.] 顶点 (0,0,1) 被两个共面三角形共享,平均后法线仍朝上

顶点法线是"骗眼睛"的艺术:低多边形的球配平滑法线,着色结果看起来圆润,轮廓仍是多边形——光照平滑与轮廓精度的分离,是第 5 章着色插值的入口话题。

网格之外:参数曲面与细分

网格不是唯一的表示。参数曲面(NURBS、贝塞尔面片)用少量控制点精确描述光滑曲面,CAD 领域的主场;细分曲面从粗网格出发,按规则(如 Catmull-Clark)反复切分并平滑顶点,皮克斯动画角色建模的标准路线。两者最终都按需要的精度"离散化成三角形网格"再进渲染管线——连续表示负责可编辑性,离散网格负责可渲染性,分工明确。

体素(规则的立体像素阵列)与点云(无拓扑的散点集)是另外两条路:前者在体绘制(7.2 节)与《我的世界》式风格化中大放异彩,后者是三维扫描的原始产物,需要重建成网格才能高效光栅化。

动手验证:立方体的棱面感与平滑感

用同一份立方体数据做一组对照:顶点法线直接取面法线(每面 4 个顶点各得一份朝外法线),着色后棱线分明;改为 8 个角点共享"对角线方向"的平滑法线,再跑 5.2 节的插值着色,立方体看起来竟像一个圆角八面体。几何一个顶点没动,仅法线不同,观感天差地别——这正是"法线是给光照看的、不是给轮廓看的"的直接证据。三维美术里"硬边与软边"的标记,控制的就是拆不拆共享顶点:硬边处顶点按面拆分(各带面法线),软边处共享(平滑法线),一个建模细节背后全是本节的代数。

本节要点回顾

  • 顶点表 + 索引表:去重复用顶点,顶点缓存让 GPU 连变换结果都能复用
  • 顶点属性是结构体:位置法线纹理坐标按步长打包
  • 顶点顺序有语义:绕向决定法线朝向与正面定义
  • 平滑法线是平均出来的:面积加权的面法线均值让低模看着光滑
  • 连续表示可编辑、离散网格可渲染:CAD 用曲面、游戏用网格、医学用体素

数据有了,4.2 节把相机请进来:观察矩阵如何把整个世界搬到相机脚下。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U