)
引子一尊没有骨头的雕像让我们从一个奇特的想象开始。假设你是一位造物主正准备在虚拟世界里创造一个人。你已经想好了他的一切——他该有怎样的肤色、穿怎样的衣服、在阳光下泛起怎样的光泽。你满怀期待地把这些外观信息准备妥当然后大喝一声“成形吧”然而什么都没有发生。因为你忘了最根本的一件事——你还没有给他一副骨架。没有骨架肤色该附着在哪里衣服该披在什么形状上光泽该在怎样的表面流转你精心准备的一切外观全都成了无处安放的空中楼阁。就像一尊雕像如果内部没有支撑的骨架结构再华美的外表也无从谈起——它连立起来都做不到更遑论呈现什么形态。在虚拟世界里这副决定万物形状的骨架就叫做 Mesh网格。在前面关于渲染的文章里我们把 Mesh 简单称为物体的形状骨架,并把它列为渲染四大要素之首。今天我们要走近这位沉默的骨架大师,看看它究竟是什么、由什么构成、又是如何撑起我们所见的整个立体世界的。一、什么是 Mesh——用三角形拼出的形状我们先给 Mesh 一个清晰的定义Mesh网格是描述一个三维物体形状的数据结构。它由一系列顶点Vertex以及这些顶点连接构成的三角形Triangle组成共同定义了物体在三维空间中的几何外形。这个定义里藏着一个可能会让你惊讶的真相在三维世界里几乎所有物体的形状归根结底都是由无数个小小的三角形拼接而成的。是的你没听错。无论是一个圆滑的球、一辆流线型的跑车、一张栩栩如生的人脸还是连绵起伏的山脉——当你把它们放大、再放大仔细去看它们的骨架,你会发现它们全都是由成千上万个平面的小三角形一块一块地拼接、逼近出来的。这就是 Mesh 的核心秘密用大量平直的三角形去逼近和模拟出各种复杂的、甚至看似圆滑的曲面。一个立方体只需 12 个三角形就能拼出6 个面每面 2 个三角形。而一个看起来光滑圆润的球体则可能需要成百上千个三角形才能让它的表面圆滑得看不出棱角。三角形越多形状就越精细、越平滑三角形越少形状就越粗糙、越有棱角。绝妙的比喻Mesh 就像用无数块小小的三角形瓷砖去拼贴出一座雕塑的表面。用的瓷砖越多、越小雕塑的曲线就越顺滑、越逼真用的瓷砖越少、越大雕塑就越显得棱角分明“方块感十足”。整个三维世界的万物形态都是这样一片片三角瓷砖拼贴出来的。二、为什么偏偏是三角形这里你一定会冒出一个好奇的疑问为什么是三角形用正方形、五边形拼不行吗这是一个非常好的问题答案里藏着几何学的智慧。三角形之所以成为三维图形的基本砖块,是因为它拥有几个无可替代的独特优势。优势一三个点必定共面这是最根本的原因。任意三个点永远能确定唯一的一个平面。想象一个四条腿的凳子如果四条腿不一样长它就会晃——因为四个点很难保证在同一个平面上。但三条腿的凳子永远稳稳地立在地上绝不会晃——因为三个点一定在一个平面里。同样的道理三角形的三个顶点无论怎么摆都必定构成一个平整的、没有歧义的平面。而正方形、五边形的顶点一旦某个点稍微翘起来这个面就扭曲了、不平了计算机就不知道该如何绘制这个扭曲的面。三角形的这种绝对平整、永不扭曲的特性让计算机的渲染计算变得简单、可靠、高效。这是它成为图形学基石的核心原因。优势二简单、通用、可拆分任何复杂的多边形四边形、五边形……都可以被拆分成若干个三角形。也就是说三角形是构成一切平面图形的最小单元。既然万物皆可拆成三角形那计算机干脆就统一用三角形来处理一切简单又通用。优势三硬件的极致优化正因为三角形如此基础和统一几十年来显卡GPU的硬件设计就是围绕着如何飞快地处理海量三角形来专门优化的。GPU 处理三角形的速度快得惊人。所以用三角形等于是在顺应硬件的天性能获得最佳的性能。一句话总结三角形永远平整、简单通用、硬件友好,这三大优势让它当之无愧地成为了构建三维世界的万能基础砖块。三、Mesh 的内部构造:顶点、三角形与更多现在让我们像解剖学家一样打开 Mesh 的身体,看看它内部究竟由哪些数据构成。理解了这些你就真正读懂了 Mesh 的语言。核心一Vertices顶点——骨架的关节点顶点Vertex是构成 Mesh 的最基本单位是三维空间里一个个具体的点。每个顶点最核心的信息就是它的坐标x, y, z——它在三维空间里的确切位置。无数个这样的顶点就像人体骨架上的一个个关节点,标定出了物体形状的关键位置。在 Unity 里一个 Mesh 的所有顶点被存储在一个数组里Vector3[]verticesmesh.vertices;// 每个 Vector3 就是一个顶点的 (x, y, z) 坐标顶点越多意味着形状能被刻画得越精细。一个高精度的人物模型可能拥有数万甚至数十万个顶点。核心二Triangles三角形——连接顶点的骨骼光有一堆孤立的顶点还不够——它们只是虚空中散落的点。我们还需要告诉计算机“哪三个顶点应该连成一个三角形”这就是Triangles三角形数据的作用。它不直接存坐标而是存储顶点的索引编号——通过第几号、第几号、第几号顶点连成一个三角形的方式把散落的顶点组织成一个个实实在在的三角面。int[]trianglesmesh.triangles;// 每三个数字代表一个三角形的三个顶点编号// 比如 {0, 1, 2} 表示第0、1、2号顶点组成一个三角形如果说顶点是关节点,那三角形数据就是连接这些关节的骨骼,把一盘散沙般的点组织成了一副有形状的骨架。核心三Normals法线——决定光照方向有了顶点和三角形形状就定好了。但要让物体在渲染时正确地响应光照、显现出立体的明暗还需要一个关键信息——法线Normal。法线是每个顶点或每个面上一条垂直于表面、指向外侧的方向向量。它告诉渲染系统“这个表面是朝着哪个方向的”为什么这很重要因为光照的明暗取决于表面朝向与光线方向的夹角。一个面正对着光就亮斜对着光就暗背对着光就是阴影。法线正是用来判断这个朝向的。没有正确的法线物体的明暗就会乱套——该亮的地方发暗该暗的地方发亮立体感荡然无存。法线是 Mesh 与光影之间的桥梁。核心四UV 坐标——贴图的定位图纸还有一个重要数据是UV 坐标。它解决的问题是“如何把一张二维的贴图纹理正确地’贴’到三维的 Mesh 表面上”UV 坐标就像给 Mesh 表面的每个顶点都标注了一个这块皮肤对应贴图上的哪个位置的坐标。它像一张展开的裁剪图纸——想象把一个立方体的纸盒拆开、铺平标注出每个面对应贴图的哪一块。有了 UV贴图才能严丝合缝地包裹在 Mesh 上。Mesh 内部构造小结顶点Vertices形状的关节点存坐标三角形Triangles连接顶点的骨骼存编号法线Normals决定光照明暗的表面朝向UV 坐标把贴图正确贴上去的定位图纸。这四样数据共同构成了一个完整的 Mesh。顶点定哪里,三角形定如何连,法线定如何被照亮,UV 定如何被贴图。四、Mesh 从哪里来——建模与代码生成理解了 Mesh 的构造你可能会问这些复杂的顶点、三角形数据是怎么被创造出来的呢主要有两条途径。途径一3D 建模软件最常见绝大多数游戏里的 Mesh——角色、道具、建筑、场景——都是由美术师在专业的3D 建模软件如 Blender、Maya、3ds Max里一点一点雕刻出来的。美术师在这些软件里通过拉伸、切割、雕刻等操作塑造出物体的形状软件会自动生成对应的顶点和三角形数据。完成后导出成模型文件如.fbx、.obj再导入 Unity就成了可以使用的 Mesh。这是最主流的方式——专业的事交给专业的工具和美术师。作为程序员你大多数时候是使用这些做好的 Mesh。途径二用代码动态生成 Mesh但在某些场景下我们需要用代码在运行时动态地生成Mesh。比如程序化生成的地形、根据数据实时绘制的图表、可破坏的物体、无限延伸的世界……这些形状无法预先做好必须靠代码实时计算顶点和三角形。下面是一个用代码生成一个三角形的简单例子帮你直观感受 Mesh 的构造usingUnityEngine;[RequireComponent(typeof(MeshFilter),typeof(MeshRenderer))]publicclassCreateTriangle:MonoBehaviour{voidStart(){// 1. 创建一个新的空 MeshMeshmeshnewMesh();// 2. 定义三个顶点的位置Vector3[]verticesnewVector3[3];vertices[0]newVector3(0,0,0);// 左下vertices[1]newVector3(0,1,0);// 上方vertices[2]newVector3(1,0,0);// 右下// 3. 定义三角形用第0、1、2号顶点连成一个三角形int[]trianglesnewint[]{0,1,2};// 4. 把数据赋给 Meshmesh.verticesvertices;mesh.trianglestriangles;// 5. 自动计算法线让它能正确响应光照mesh.RecalculateNormals();// 6. 把 Mesh 交给 MeshFilter 显示出来GetComponentMeshFilter().meshmesh;}}运行这段代码你会在场景里看到一个凭空出现的三角形虽然简单但它完整地展示了 Mesh 的诞生过程——定义顶点 → 定义三角形连接 → 计算法线 → 显示。理解了这个最小案例你就掌握了 Mesh 生成的核心逻辑。所有复杂的模型本质上都是这个过程的成千上万倍放大。五、Mesh 的好搭档:MeshFilter 与 MeshRenderer在 Unity 里Mesh 数据本身是无法直接显示出来的。它需要两位搭档组件的配合才能真正呈现在屏幕上。这也是新手常常混淆的地方我们理清一下。MeshFilter负责持有MeshMeshFilter网格过滤器组件的职责是持有 Mesh 数据——它像一个容器,装着这个物体的形状骨架Mesh。它回答的问题是“这个物体的形状是什么”MeshRenderer负责画出来光有形状还不够还需要把它渲染绘制到屏幕上。这就是MeshRenderer网格渲染器的工作。它拿到 MeshFilter 持有的形状结合材质Material把它真正地画出来。它回答的问题是“这个形状该用什么外观画出来、显示到屏幕上”黄金搭档的分工Mesh形状数据本身骨架MeshFilter持有 Mesh拿着骨架MeshRenderer把 Mesh 配合材质画出来给骨架披皮上色、显示。三者协作一个物体才能既有形状,又能被看见。缺了 MeshRenderer物体就有形状却隐形;缺了 MeshFilter就无形状可画。还记得渲染那篇讲的流程吗MeshFilter 持有的 Mesh 数据正是渲染流水线的原料入口——它的顶点被送进顶点处理阶段开启了整场渲染魔法。六、Mesh 与性能面数的甜蜜负担Mesh 的三角形数量俗称面数是一把双刃剑直接关系到游戏的画质与性能。这是每个开发者都必须权衡的核心问题。面数越高形状越精细、越平滑、越逼真——但顶点越多渲染时要处理的数据量越大性能开销越高可能导致卡顿。面数越低形状越粗糙、越有棱角——但数据量小渲染越快、越流畅。如何平衡这里有几个关键的优化智慧智慧一在够用的前提下尽量降低面数一个远处的、只占屏幕几个像素的小石头没必要用几万面的高模——玩家根本看不出区别纯属浪费性能。用恰好够看的面数是优化的基本原则。智慧二LOD细节层次技术这是一个绝妙的优化手段——为同一个物体准备好几套不同精度的 Mesh高模、中模、低模。当物体离摄像机近时用高模看得清需要精细离得远时自动切换成低模看不清粗糙点也无妨。LOD 就像近处用高清照片、远处用缩略图,在玩家几乎察觉不到的情况下大幅节省了性能。这是大型场景、开放世界必备的技术。智慧三合理使用 Mesh Collider还记得物理系统那篇提到的 Mesh Collider 吗它能让碰撞体完全贴合 Mesh 的复杂形状但开销很大。所以能用简单碰撞体Box、Sphere替代的地方尽量别用 Mesh Collider——这也是 Mesh 相关的一个重要性能考量。一句话智慧面数是甜蜜的负担——它带来精美也带来开销。优秀的开发者永远在画质与性能之间寻找那个恰到好处的平衡点。尾声沉默的骨架撑起整个世界我们从一尊没有骨头的雕像出发一路认识了Mesh 是描述物体形状的骨架由顶点和三角形构成用三角形逼近出万物形态三角形之所以是基石因为它永远平整、简单通用、硬件友好;Mesh 内部由顶点、三角形、法线、UV 四类核心数据构成Mesh 来自 3D 建模软件或用代码动态生成它需要 MeshFilter持有与 MeshRenderer绘制两位搭档才能显示而面数则是画质与性能的甜蜜负担,需要用 LOD 等技术巧妙平衡。回过头品味Mesh 在整个虚拟世界里扮演着一个极其根本却又极其低调的角色。它低调是因为玩家永远不会直接看到它。玩家看到的是华美的材质、绚丽的光影、生动的角色——那些浮于表面的、吸引眼球的东西。而 Mesh那副默默支撑起这一切的骨架永远隐藏在皮肤之下、光影背后从不显山露水。但它又如此根本。因为——没有骨架就没有一切。再精美的材质也需要附着在 Mesh 的表面上再绚丽的光影也需要沿着 Mesh 的法线去计算明暗再逼真的角色其一举一动也是这副骨架在牵引变形。Mesh是所有视觉华彩得以存在的那个1,其余的一切都是它后面的0。这多么像现实世界的道理啊。我们赞美一座雕塑的优美线条却常常忘了正是内部那看不见的骨架结构才让这优美得以矗立我们惊叹一栋大厦的华丽外观却容易忽视是那深埋地下的地基和钢筋骨架才撑起了这份华丽。真正重要的东西往往是沉默的、看不见的、藏在表象之下的。所以当你下次在游戏里看到一个精美的角色、一件华丽的道具、一片壮阔的场景时愿你能透过那层绚丽的皮肤,看到它之下那副由无数三角形拼接而成的、沉默而坚实的骨架。那副骨架就是 Mesh。它不争、不显、不语却用自己的每一个顶点、每一个三角形实实在在地撑起了我们所见的、这整个璀璨的虚拟世界。而理解了它你就理解了这个世界成形的最初也是最深的那一层秘密。