我要提问
ARTICLE DETAIL

资讯详情

前沿编程新知与开发实战干货的深度解读。

Unity3D代码创建三角面:Mesh顶点索引、绕序、法线与包围盒实战

Unity3D代码创建三角面:Mesh顶点索引、绕序、法线与包围盒实战 场景里拖一个 Cube转两下就能看到六个面这件事太顺手了顺手到不少人做了很久项目都没认真翻开过 Unity3D 里 Mesh 组件到底存了什么。可一旦需求变成“动态生成一块地形”、“做一根会扭动的绳子”、“把一堆测点数据变成曲面”内置几何体立马就不够用这时候绕不开的就是从最原始的三角面开始自己往 Mesh 里填数据。这篇就只干一件事在 Unity3D 里用代码创建一个最简单的三角面并把它渲染出来。听起来像个入门 Demo但里面藏着绕序、法线、包围盒、索引格式这一连串会被反复绊倒的坑后面做程序化建模、网格变形、运行时切割全都建立在对这几个概念的正确理解上。适合刚接触 Mesh 的同学打地基也适合写过一些生成器代码但总是“莫名其妙看不见东西”的同学回头补漏。1. 三角面虽小却是 Mesh 体系的地基1.1 引擎只认三角形显卡那套光栅化管线最终处理的图元只有三种点、线、三角形。所谓四边形、多边形、曲面到了硬件这一层统统被拆成一个个三角形。你在编辑器里看到的 Cube展开看其实是 12 个三角面拼起来的一个高模角色几十万面本质也是几十万个三角形按顶点索引排列在一起。这个事实带来一个很直接的推论只要你能正确地把顶点位置和它们的连接关系喂给引擎你就能造出任何形状。模型师在 DCC 软件里雕刻、拓扑、导出 FBX本质是在产出一张三角形列表我们在代码里手写顶点数组本质是在做同一件事只是没可视化的雕刻笔刷全靠数学计算。理解了这一点再看 Unity3D 的 Mesh 类就不会觉得它神秘。它就是一个容器里面装着若干数组顶点在哪、谁和谁连成面、面朝哪边、贴图怎么贴。剩下的工作由渲染管线和 Shader 完成。从三角面开始练最大的好处是变量最少。三个顶点、三个索引任何一处写错现象都很直观排查路径也短。等你把这套东西彻底理顺再去做几千个顶点的网格出问题时至少知道该从哪几个方向去怀疑。1.2 一张 Mesh 到底装了什么新手容易把 Mesh 想成“模型文件”其实它更接近一张结构化表格。核心的就下面这几项我按重要程度排一下数据项作用缺失后的现象vertices顶点数组定义所有顶点的局部坐标物体什么都不显示triangles索引数组三个一组声明哪些顶点连成面有顶点但无面同样看不见normals法线决定光照计算朝向面是黑的、光照错乱uv决定贴图采样位置贴图糊成一片纯色或拉伸bounds包围盒参与视锥剔除和射线检测物体在视野边缘突然消失colors、tangents顶点色、切线空间一般不影响可见性有一件事得说清楚只有 vertices 和 triangles 是必须的其它都能省但省了就要承担对应后果。法线不写Unity 不会自动补除非你主动调用重算接口包围盒不写Unity 会用初始值位置一偏移就会出问题。我见过太多“代码看起来没问题但就是不显示”的情况八成不是法线就是绕序这两块后面会重点拆。1.3 什么时候必须自己造 Mesh不是所有需求都值得手写网格。判断标准很简单如果内置几何体加缩放旋转就能表达那就别造。用代码拼一个立方体除了练手没有实际收益。真正需要动手的场合通常是这几类数据驱动形状从传感器、表格、接口拿到一批数值要转成可视化的面或体运行时变化网格顶点位置每帧都在动比如波浪、融化、布料感批量差异化成百上千个物体形状各不相同用预制体加缩放做不出来结构可见调试用的线框、法线可视化、碰撞体轮廓超低面数优化只需要一个面片做投影、贴花、UI 特效。这几类的共同点是“形状本身是逻辑的一部分”而不是美术资产的一部分。把形状交给代码生成好处是能参数化、能联动数据、能节省包体坏处是调试成本高看不到的东西全靠推。所以第一张网格越简单越好一个三角面刚刚好。注意程序化生成的网格默认不会存进工程文件运行时创建的对象在退出播放后就会消失。如果需要持久化得走编辑器脚本把它存成资源这一点在第 5 节会讲。2. 动手前的三个关键概念2.1 顶点数组与索引数组的分工很多人第一次看到 triangles 数组时是懵的明明只有三个顶点为什么还要再写一个{0, 1, 2}直接用顶点顺序连面不行吗不行的原因在于顶点复用。先看一个极端的例子一个立方体有 8 个角但它有 12 个三角面、总计 36 个“顶点位置”。如果按面依次写顶点顶点数组就得有 36 项其中大量是重复的同一个角。而用索引顶点数组只要 8 项索引数组写 36 个下标即可。显存占用差了好几倍GPU 的顶点着色器调用次数也差好几倍。所以这套设计的意图非常明确顶点数组负责“有哪些点”索引数组负责“怎么连”。点和连接关系解耦之后同一批顶点能被多个面共享也能在某些场合刻意拆开比如需要硬边法线或不同 UV 时同一个位置在数组里出现两次。索引数组还有两条硬约束需要记住长度必须是 3 的倍数否则 Unity 会报错或直接忽略多余的部分每个元素必须落在顶点数组的有效下标范围内越界会导致网格损坏表现是渲染错乱甚至编辑器崩溃。关于第二点我踩过一次用循环生成网格时行列算错了一格下标刚好超出顶点数一位Unity 没有立刻报错只是那个面变成一堆乱飞的三角形排查了半天才定位到循环边界。2.2 绕序决定正反面这是最容易翻车的地方。同一个三角形顶点顺序不同正面朝向就完全不同。Unity3D 使用左手坐标系规则是从正面看过去三个顶点按顺时针排列时这一面为正面逆时针则为背面。默认材质下背面会被剔除Cull Back也就是不渲染所以你看到的现象就是“三角面凭空消失”。举个具体的例子。三个顶点分别放在(0,0,0)、(1,0,0)、(0,1,0)它们都在 XY 平面上。从 Z 方向看向原点这三个点的排列是逆时针。按上面的规则这个三角面的正面朝向 -Z从 Z 看过去看到的是背面会被剔除。想让正面朝 Z索引顺序要写成{0, 2, 1}。这就是为什么很多人照着“0,1,2”写完发现什么都看不到——代码没毛病就是顺序反了。实操心得判断绕序别靠脑补。我的做法是先写{0,1,2}看不见就把索引改成{0,2,1}一秒验证。虽然粗暴但比盯着坐标算叉乘快得多。真要算可以取两个边向量做叉乘结果方向就是正面朝向右手定则反过来推。另外绕序不仅影响可见性也影响法线方向。RecalculateNormals是根据三角形的绕序来推算法线的绕序反了法线也就朝里了即便你关掉背面剔除把面显示出来光照也会是黑的。2.3 法线与包围盒的隐性作用法线是垂直于表面的单位向量Shader 用它和光线方向做点积算出这个面有多亮。你写RecalculateNormals()Unity 会遍历所有三角形算出每个面的法线再按共享顶点做平均得到平滑效果。对单个三角面来说法线就是它所在平面的垂线方向由绕序决定。这里有个细节对共享顶点做平均会让相邻面看起来是圆滑过渡的。如果你的模型需要硬边比如一个立方体就不能让相邻面共享顶点得把顶点拆开让每个面拥有独立顶点这样才能得到明确的棱角。这个技巧在后面做箱体、做切面时非常关键。包围盒Bounds容易被忽略它的作用是告诉引擎“这个网格占据的空间范围”。主相机做视锥剔除时拿包围盒来判断要不要渲染物理射线检测、Renderer.bounds也都依赖它。关键点在于修改顶点后必须重算包围盒。如果你先创建网格、调用RecalculateBounds然后在运行时移动顶点位置却没有再次调用包围盒还停留在旧位置。此时物体可能因为“包围盒不在视锥内”而被剔除现象是走到某个角度突然消失或者从摄像机边缘离开时整块不见了。我第一次遇到这个现象时以为是 Shader 的问题查了很久。3. 从零构建一个三角面的完整实操3.1 场景与脚本准备先做减法把环境清干净新建一个 3D 项目删掉默认场景里多余的对象只留一个主相机把主相机位置设成(0, 0, -5)朝向(0, 0, 0)保证它从 Z 方向看向原点创建一个空物体命名随意比如TriangleDemo给这个空物体挂一个脚本。脚本里需要两个组件MeshFilter负责持有网格数据MeshRenderer负责用材质把它画出来。用[RequireComponent]特性自动挂上避免手动遗漏。这里顺带说一个常见误区MeshFilter才是装数据的地方MeshRenderer只管渲染。很多人删了 Renderer 发现物体不显示第一反应是网格没了其实是没渲染器。反过来删了 FilterRenderer 还在但没有数据也不显示。两者缺一不可。注意Unity 会为每个渲染物体自动创建MeshRenderer但不会自动创建MeshFilter。如果只挂 RendererProfiler 里能看到渲染批次屏幕上却是空的。3.2 基础版本代码与逐段拆解先上能跑的最小实现代码不长但每一行都有它的位置using UnityEngine; [RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))] public class SimpleTriangle : MonoBehaviour { void Start() { Mesh mesh new Mesh(); mesh.name SimpleTriangle; // 三个顶点放在 XY 平面上 Vector3[] vertices new Vector3[3]; vertices[0] new Vector3(0f, 0f, 0f); vertices[1] new Vector3(1f, 0f, 0f); vertices[2] new Vector3(0f, 1f, 0f); // 索引顺序决定正反面这里用顺时针让正面朝 Z int[] triangles new int[3] { 0, 2, 1 }; // UV让贴图能正确采样 Vector2[] uvs new Vector2[3]; uvs[0] new Vector2(0f, 0f); uvs[1] new Vector2(1f, 0f); uvs[2] new Vector2(0f, 1f); mesh.vertices vertices; mesh.triangles triangles; mesh.uv uvs; mesh.RecalculateNormals(); mesh.RecalculateBounds(); GetComponentMeshFilter().mesh mesh; } }逐段解释一下。创建 Mesh 实例new Mesh()每次运行都会创建一个新的对象。在Start里创建没问题但要是在Update里反复newGC 压力会很明显。生产代码里更稳的做法是复用同一份网格每次更新时调用mesh.Clear()再重新填数据。顶点坐标三个点放在单位尺度上是为了方便观察。Unity 默认 1 单位约等于 1 米这个三角面在场景里就是 1 米见方相机在 5 米外看刚合适。索引顺序{0, 2, 1}而不是{0, 1, 2}理由在第 2 节讲过了让正面朝向相机。这个顺序看着别扭但写多了就习惯了。我一般在旁边留一行注释写明“顺时针面向 Z”下次打开不至于怀疑人生。UV 数组UV 是二维坐标取值 0 到 1 表示贴图上的相对位置。(0,0)是左下角(1,1)是右上角。三个顶点的 UV 和它们的空间位置一一对应贴一张三角形状的图就能完整覆盖。如果 UV 数组长度和顶点数组长度不一致Unity 会报错丢数据这是硬性要求。设置顺序vertices必须最先赋值因为triangles的合法性依赖于顶点数量。反过来先设triangles会直接报错。UV 和法线没有强制顺序但建议统一成 vertices → triangles → uv → normals → bounds 这个流程形成肌肉记忆。法线与包围盒重算RecalculateNormals必须在 triangles 设置之后调用否则它算不出任何东西。RecalculateBounds同理顶点改动后都要重算一次。赋值给 FilterGetComponentMeshFilter().mesh mesh。这里的.mesh属性有个细节读的时候会返回一份实例写的时候会直接替换。如果想改共享资源得用.sharedMesh。运行时动态生成的网格用.mesh是对的避免污染工程里的资源文件。3.3 材质配置与渲染验证代码写完点运行如果材质没配屏幕上大概率还是空的。内置管线的默认材质是Default-Material它接受光照你需要有光源才能看清。快速验证的话有两种做法做法一把材质换成 Unlit 类 Shader。URP 项目里新建材质Shader 选Universal Render Pipeline/Unlit颜色随便设个亮色。Unlit 不受光照影响能立刻确认几何体是否存在。做法二保留默认材质加一个方向光角度调到正对三角面。这样还能顺便验证法线方向对不对——如果整块是黑的说明法线朝反了。验证渲染结果时我通常按这个顺序检查Play 模式下看 Hierarchy对象是否存在有没有被误删选中对象看 InspectorMeshFilter 上是不是显示SimpleTriangle顶点数是不是 3Scene 视图切到线框模式能不能看到那条三角边Game 视图换角度从 Z 和 -Z 各看一次确认只有一面可见。第 3 步特别有用。Scene 视图里即使剔除掉了背面也能看到网格的线框轮廓这是判断“数据存在但渲染被剔除”最直接的手段。提示Scene 视图右上角有个小图标可以切换 Shaded / Wireframe快捷键在 Windows 上是Ctrl 数字系列。养成用手势切换的习惯排查网格问题的效率会高很多。3.4 改用 List 与 SetVertices 的进阶写法数组版本够用但每次生成都要申请新数组反复创建会有分配开销。Unity 后来加了一批SetVertices、SetTriangles接口接受ListT能复用内部缓冲区写法也更灵活。using System.Collections.Generic; using UnityEngine; [RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))] public class SimpleTriangleList : MonoBehaviour { private Mesh _mesh; private readonly ListVector3 _vertices new ListVector3(3); private readonly Listint _triangles new Listint(3); private readonly ListVector2 _uvs new ListVector2(3); void Start() { _mesh new Mesh { name SimpleTriangleList }; GetComponentMeshFilter().mesh _mesh; Rebuild(); } private void Rebuild() { _mesh.Clear(); _vertices.Clear(); _triangles.Clear(); _uvs.Clear(); _vertices.Add(new Vector3(0f, 0f, 0f)); _vertices.Add(new Vector3(1f, 0f, 0f)); _vertices.Add(new Vector3(0f, 1f, 0f)); _triangles.Add(0); _triangles.Add(2); _triangles.Add(1); _uvs.Add(new Vector2(0f, 0f)); _uvs.Add(new Vector2(1f, 0f)); _uvs.Add(new Vector2(0f, 1f)); _mesh.SetVertices(_vertices); _mesh.SetTriangles(_triangles, 0); _mesh.SetUVs(0, _uvs); _mesh.RecalculateNormals(); _mesh.RecalculateBounds(); } }几个值得说明的点List声明为只读字段并在构造时预分配容量。三角面只有三个点容量给 3 就够避免扩容时的数组拷贝。这个习惯在做大网格几万顶点时能省下可观的分配。_mesh.Clear()放在最前面。这个方法会清空所有顶点数据但保留 Mesh 对象本身和它的材质引用。如果不调用 Clear 而直接重新SetVertices旧数据会被覆盖但如果新数据的顶点数比旧的少残留的三角形索引可能会引用到不存在的顶点导致报错。Clear 是最省心的做法。SetTriangles的第二个参数是子网格索引从 0 开始。单材质的网格永远传 0。多材质网格才需要拆成多个子网格每个子网格对应一个材质槽。SetUVs的第一个参数是通道号也是 0 起。复杂效果可能用到通道 1、2但基础贴图用 0 就够了。这套写法好在结构清晰数据的准备和提交分开以后想做动态更新只要改数据源再调Rebuild就行不用重建整个 Mesh。4. 排查实录三角面不显示怎么办4.1 先确认物体真的生成了排查要从最外层往里收。很多“网格不显示”的问题其实是脚本根本没执行。第一步看 Hierarchy。Play 模式下如果对象不在列表里说明脚本没挂上、或者被别处销毁了、或者整个 GameObject 被 SetActive(false) 了。这种情况跟 Mesh 一点关系都没有。第二步看 Console。Unity 里网格相关的问题有时会以警告形式出现比如“Mesh.triangles is invalid”或者“Index buffer is too small”。这类提示要优先处理别急着改逻辑。第三步看 Inspector 上 MeshFilter 的显示。正常情况会显示网格名字和顶点数。如果显示None (Mesh)说明赋值那行没执行到或者赋值给错对象了。如果显示的名字是Mesh这种默认名说明赋值的是个临时对象可能是写成了new Mesh()之后忘了保存引用。我见过最隐蔽的一种情况脚本挂在了子物体上但相机对着的是父物体看起来“什么都没生成”。实际是生成在别的地方了。这种情况用 Scene 视图拉远一点看全景通常一眼就能发现。4.2 逐层排查渲染链路确认物体存在、网格有数据之后问题基本集中在渲染链路上。按这个顺序过一遍绝大多数问题能定位第一层包围盒是否合理。选中对象Scene 视图里有没有看到那个橙色的线框盒子如果盒子在很远的地方说明包围盒没重算。这时相机可能判定物体不在视锥内直接不渲染。解决方法是在改顶点之后补一句RecalculateBounds()。第二层面是否被剔除。把相机绕到物体背面再看。如果一面有一面没有那就是绕序问题把索引顺序反转即可。也可以在材质里临时把 Cull 改成 Off 验证如果两面都出来了确诊无误。第三层材质是否正常。把材质换成 Unlit 纯色。如果出来了说明是光照问题如果还是不出来往数据层查。第四层法线方向。如果换成受光材质后整块发黑但轮廓能看到那是法线朝里了。要么调绕序要么在 Shader 里做双面法线处理。快速验证可以在临时材质上用 Unlit 对比。第五层缩放与层级。父物体如果被缩放成 0子物体的网格也会消失。检查 Transform 的 Scale 三项有没有 0。这个坑非常经典尤其是从代码里localScale Vector3.zero之后忘了恢复。第六层渲染层级与相机。检查相机的 Culling Mask 有没有把这一层排除检查对象的 Layer 设置。UI 相机和场景相机混用的时候特别容易出这种事。4.3 高频问题速查表把上面这些整理成表出问题时对着过一遍比漫无目的改代码快得多现象最可能的原因快速验证处理办法Hierarchy 里没有对象脚本未执行或被销毁看 Console 有无报错检查挂载与执行顺序有对象但 Mesh 显示 None赋值语句未执行断点或加日志检查 Filter 引用Mesh 有名字但画面空白背面剔除从反方向观察反转索引顺序只在特定角度消失包围盒未重算Scene 视图看橙色框补 RecalculateBounds面可见但全黑法线朝向错误换 Unlit 材质对比调绕序或重算法线贴图显示为纯色UV 缺失或全为 0Inspector 看 UV 通道补 SetUVs大网格报索引越界顶点数超 65535看顶点计数切 UInt32 索引格式编辑器报索引非法索引值超出顶点范围打印数组长度修正循环边界实操心得我给自己定了个规则凡是生成网格的脚本都在Rebuild末尾加一行日志打印顶点数和三角形数。跑起来先看这两个数字对不对能省掉一半的排查时间。数字对不上问题在数据层数字对得上但看不见问题在渲染层。5. 从单三角面延伸到实用网格5.1 双面三角面的三种实现一个三角面只有一面可见这在很多场景下不够用飘动的旗帜、双面显示的卡片、叶片、需要从任意角度观察的调试面板。解决方案有三种各有取舍。第一种索引翻倍。在 triangles 数组里把同样的三个顶点再写一遍顺序反过来int[] triangles new int[6] { 0, 2, 1, // 正面朝 Z 0, 1, 2 // 反面朝 -Z };这种做法最省事顶点数组不用动只多了三个索引。缺点是两面共享同一批顶点法线只能有一套RecalculateNormals平均之后法线会互相抵消导致光照发灰。如果用的是 Unlit 材质这个问题不存在用受光材质就需要注意。第二种Shader 关闭剔除。在材质对应的 Shader 里设置Cull Off两个面都会被光栅化。这种做法性能开销略高多画一遍而且背面光照计算用的是正面法线看起来会有点怪。适合快速验证、调试可视化。第三种顶点复制。把三个顶点在数组里再存一份让正反两面各用独立的顶点集合各自计算法线。顶点数从 3 变成 6索引也是 6 个但光照表现最正确。Vector3[] vertices new Vector3[6]; vertices[0] new Vector3(0f, 0f, 0f); vertices[1] new Vector3(1f, 0f, 0f); vertices[2] new Vector3(0f, 1f, 0f); vertices[3] vertices[0]; vertices[4] vertices[1]; vertices[5] vertices[2]; int[] triangles new int[6] { 0, 2, 1, // 正面 3, 4, 5 // 反面顶点独立 };三种方案怎么选我的经验是Unlit 场景用第一种最经济需要正确光照的双面物体用第三种临时调试、写工具用第二种改一行就完事。5.2 由三角面拼出四边形与网格条带一个三角面是练手两个拼起来就是一个四边形。四个顶点六个索引规律很直白Vector3[] vertices new Vector3[4]; vertices[0] new Vector3(0f, 0f, 0f); // 左下 vertices[1] new Vector3(1f, 0f, 0f); // 右下 vertices[2] new Vector3(0f, 1f, 0f); // 左上 vertices[3] new Vector3(1f, 1f, 0f); // 右上 int[] triangles new int[6] { 0, 2, 1, // 左下 - 左上 - 右下 2, 3, 1 // 左上 - 右上 - 右下 };注意这里用了“左下-左上-右下”这种拆法而不是“左下-右下-右上”再加一个。两种拆法都能成四边形区别在于对角线方向。做平面时看不出差别但如果顶点高度不同比如做起伏地形对角线方向会影响表面的折痕走向进而影响光照和形变。做地形时我倾向统一一个方向的对角线视觉上更整齐。把四边形按行列复制就是一个规则的网格面片private void BuildGrid(int rowCount, int colCount, float cellSize) { _vertices.Clear(); _triangles.Clear(); for (int r 0; r rowCount; r) { for (int c 0; c colCount; c) { _vertices.Add(new Vector3(c * cellSize, r * cellSize, 0f)); } } int stride colCount 1; for (int r 0; r rowCount; r) { for (int c 0; c colCount; c) { int bottomLeft r * stride c; int bottomRight bottomLeft 1; int topLeft bottomLeft stride; int topRight topLeft 1; _triangles.Add(bottomLeft); _triangles.Add(topLeft); _triangles.Add(bottomRight); _triangles.Add(topLeft); _triangles.Add(topRight); _triangles.Add(bottomRight); } } _mesh.Clear(); _mesh.SetVertices(_vertices); _mesh.SetTriangles(_triangles, 0); _mesh.RecalculateNormals(); _mesh.RecalculateBounds(); }这段代码是后面做地形、水波、布料的基础骨架。顶点数按(rowCount1) * (colCount1)算比如 100×100 的网格就是 10201 个顶点、20000 个三角形。这个量级完全没问题但如果做到 300×300 以上顶点数逼近 9 万就触到 16 位索引的上限了。索引格式这个点值得单独说。Unity 默认的网格索引用 16 位存储最大支持 65535 个顶点。超过这个数必须显式切换_mesh.indexFormat UnityEngine.Rendering.IndexFormat.UInt32;这行要放在设置顶点之前。切换成 32 位索引后显存占用会翻倍所以在不需要的时候别乱开。我在做一块高密度地形时忘了加这句结果网格只显示了一部分剩下的全是乱的加了一行就正常了。5.3 把 Mesh 存成资源文件运行时生成的网格关掉播放就没了。如果想让它在编辑状态下也保留得用编辑器脚本存成资源文件。#if UNITY_EDITOR using UnityEditor; using UnityEngine; public static class MeshSaver { [MenuItem(Tools/Save Mesh As Asset)] public static void Save() { MeshFilter filter Selection.activeGameObject ! null ? Selection.activeGameObject.GetComponentMeshFilter() : null; if (filter null || filter.sharedMesh null) { Debug.LogWarning(请先选中一个带有 MeshFilter 且已赋值网格的对象); return; } string path Assets/GeneratedMesh.asset; Mesh clone Object.Instantiate(filter.sharedMesh); clone.name GeneratedMesh; AssetDatabase.CreateAsset(clone, path); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); } } #endif几个细节值得注意。必须克隆。直接把运行时网格存成资源会把它和当前场景对象绑死之后改场景可能连带改坏资源文件。Instantiate一份再存能彻底断开关联。路径必须在 Assets 目录下。放到 Assets 外面 Unity 不认会报错。这个路径我习惯留一个可配置的字段但演示用写死也无妨。MenuItem 的位置。Tools/下面比较稳妥不会和内置菜单冲突。写编辑器脚本时#if UNITY_EDITOR包裹是必须的否则打包时会因为找不到UnityEditor命名空间而编译失败。这个坑几乎所有写编辑器工具的人都踩过一次。存完之后别忘了给 MeshFilter 换成新资源。资源文件是一个独立的资产如果没有被引用下一次工程刷新时可能被判定为无引用的孤立资源清理掉。做法很简单存完之后把filter.sharedMesh指过去即可。还有一点存下来的网格只包含数据不包含材质。材质是独立的需要在导入之后手动挂上或者用脚本一并处理。这一点在批量生成资产时需要特别注意否则会得到一堆没有材质的网格看着像个半成品。写完这套东西再回头看一个三角面确实简单到有点寒酸。但把它彻底跑通之后绕序、法线、包围盒、索引格式这四件事就算真正上手了。后面无论做地形、做变形、做网格切割都是在同一套数据结构上做加减法出问题时排查的路径也是完全一样的先看数据再看可见性最后看光照。这个顺序比任何工具都可靠。
返回列表