顶点优化指南:省内存60%的秘诀

📅 发布时间:2026/8/9 17:36:58
顶点优化指南:省内存60%的秘诀
一、先理解一个顶点里装了什么很多人以为顶点 一个3D坐标点 实际上一个顶点携带【一堆属性数据】 一个顶点(Vertex)包含 ┌─────────────────────────────────────┐ │ Position(位置) : 3个float 12字节 │ ← 必须 │ Normal(法线) : 3个float 12字节 │ ← 光照用 │ Tangent(切线) : 4个float 16字节 │ ← 法线贴图用 │ UV0(主纹理坐标) : 2个float 8字节 │ ← 贴图用 │ UV1(第二套UV) : 2个float 8字节 │ ← lightmap用 │ Color(顶点色) : 4个byte 4字节 │ ← 顶点着色用 │ BoneWeight(骨骼) : ... │ ← 蒙皮动画用 └─────────────────────────────────────┘ ↓ 一个完整顶点可能 60 字节内存计算Mesh内存 ≈ 顶点数 × 每顶点字节数 10000顶点 × 60字节 600KB 10000顶点 × 精简到28字节 280KB ↓ 省一半以上 顶点数越多精简收益越大核心思路不是每个网格都需要全部属性用不到的属性去掉直接减少每个顶点的字节数。二、各顶点属性的作用与去除条件1. Position位置— 永远保留顶点的3D坐标网格的骨架 → 绝对不能去去了就没网格了2. Normal法线— 光照相关作用决定表面朝向用于计算光照明暗 ┌──────────────────────────────────┐ │ 有法线光照下明暗自然、有立体感 │ │ 无法线无法正确计算光照 │ └──────────────────────────────────┘ 【可去除】的情况 ├── 纯色/自发光物体(不受光照) ├── UI元素(通常不参与光照) ├── 用了不需要法线的特殊shader └── 2D精灵 【必须保留】 └── 任何需要光照的3D物体(绝大多数)3. Tangent切线— 法线贴图相关作用配合Normal构成切线空间用于法线贴图(Normal Map) 法线贴图 用贴图模拟表面凹凸细节 ↓ 需要 切线空间 Normal Tangent 一起定义 ↓ 没有Tangent → 法线贴图无法正确工作 ┌──────────────────────────────────┐ │ Tangent占16字节是较大的一块 │ └──────────────────────────────────┘ 【可去除】的情况★常见优化点★ ├── 材质没用法线贴图(Normal Map) ├── 简单材质(纯色、只有主贴图) └── 大部分场景道具其实不用法线贴图 【必须保留】 └── 用了法线贴图的材质(角色、精细物体)实战很多物体用了简单材质却带着Tangent去掉能省16字节/顶点收益明显。4. UV0主纹理坐标— 通常保留作用贴图怎么贴到模型上的坐标映射 有贴图的物体都需要UV0 ↓ 【可去除】纯色无贴图物体(极少) 【保留】绝大多数(要贴图就要UV)5. UV1 / UV2第二套UV— Lightmap相关作用主要用于光照贴图(Lightmap)的独立UV展开 为什么需要第二套UV 主UV(UV0)贴纹理可能重叠/平铺 Lightmap UV(UV1)烘焙光照必须不重叠 ↓ 两者需求冲突 → 用独立的第二套UV ┌──────────────────────────────────┐ │ 【可去除】的情况★常见★ │ │ ├── 不用烘焙光照(实时光照/无光照) │ │ ├── 动态物体(不参与lightmap烘焙) │ │ └── 移动端很多不用lightmap的物体 │ ├──────────────────────────────────┤ │ 【必须保留】 │ │ └── 参与光照烘焙的静态物体 │ └──────────────────────────────────┘6. Color顶点色— 顶点着色相关作用给每个顶点存一个颜色用于 ├── 顶点级着色(渐变、遮罩) ├── 混合多张贴图的权重 └── 某些特效 【可去除】不用顶点色的物体(大部分) 【保留】用顶点色做混合/特效的7. BoneWeight骨骼权重— 蒙皮动画作用骨骼动画中顶点受哪些骨骼影响、影响多少 【只有】SkinnedMeshRenderer(蒙皮网格)需要 ↓ 静态物体(MeshRenderer)不该有这个三、Mesh Compression压缩原理前面是去掉整个属性这里是降低保留属性的精度。核心用更少的位表示数值默认每个数值用 float(32位) 存储 压缩用更少的位数近似表示 例位置坐标 未压缩float 32位精度极高 压缩可能用16位半精度精度降低但省一半四个档位┌──────────┬────────────────────────────────┐ │ Off │ 不压缩全精度float最大最准 │ ├──────────┼────────────────────────────────┤ │ Low │ 轻度压缩精度损失小 │ ├──────────┼────────────────────────────────┤ │ Medium │ 中度压缩 │ ├──────────┼────────────────────────────────┤ │ High │ 高度压缩最省但精度损失明显 │ └──────────┴────────────────────────────────┘ 压缩不同数据用不同精度 ├── Position影响形状压狠了会变形/抖动 ├── Normal/Tangent影响光照损失影响明暗 └── UV影响贴图压狠了贴图错位压缩的代价精度损失表现位置精度损失 模型顶点位置量化到网格点上 → 大模型可能出现顶点抖动、缝隙 → 精细模型慎用High UV精度损失 → 贴图可能轻微错位、接缝 Normal精度损失 → 光照明暗过渡不够平滑⚠️注意Mesh Compression 是存储压缩磁盘/传输小加载到运行时内存里通常还是解压状态。它主要减小包体运行时内存收益有限。真正省运行时内存的是去掉属性。四、关键区分两种优化的不同收益┌────────────────┬──────────┬──────────┐ │ │ 包体(磁盘) │ 运行时内存│ ├────────────────┼──────────┼──────────┤ │ Mesh Compression│ ↓↓ 明显 │ ~ 有限 │ │ (降精度) │ │ │ ├────────────────┼──────────┼──────────┤ │ 去除顶点属性 │ ↓ 一些 │ ↓↓ 明显 │ │ (删Normal等) │ │ │ └────────────────┴──────────┴──────────┘ 想省包体 → Mesh Compression 想省运行时内存 → 去除不用的属性 ★ 两者可以结合用五、实战如何去除顶点属性1. 模型导入设置ModelImporter选中模型 → Inspector Normals ├── Import用模型自带法线 ├── CalculateUnity计算 └── None不要法线 ← 去除 Tangents ├── Import/Calculate └── None不要切线 ← 去除(不用法线贴图时) Blend Shapes不用可关 Optimize Mesh优化顶点顺序(GPU缓存友好)2. 代码检查网格的属性Meshmesh...;Debug.Log($顶点数:{mesh.vertexCount});Debug.Log($有法线:{mesh.normals.Length0});Debug.Log($有切线:{mesh.tangents.Length0});Debug.Log($有UV2:{mesh.uv2.Length0});Debug.Log($有顶点色:{mesh.colors.Length0});// 估算每顶点大小3. AssetPostprocessor 自动化publicclassMeshImporter:AssetPostprocessor{voidOnPreprocessModel(){varimporterassetImporterasModelImporter;// 场景道具默认不要切线(省16字节/顶点)if(assetPath.Contains(/Props/)){importer.importTangentsModelImporterTangents.None;}// 优化网格importer.optimizeMeshPolygonstrue;importer.optimizeMeshVerticestrue;}}六、决策速查表根据物体类型决定保留哪些属性┌──────────────┬────┬────┬────┬────┬────┬─────┐ │ 物体类型 │Pos │Norm│Tang│UV0 │UV1 │Color│ ├──────────────┼────┼────┼────┼────┼────┼─────┤ │ 受光普通模型 │ ✅ │ ✅ │ ? │ ✅ │ ? │ ? │ │ 用法线贴图 │ ✅ │ ✅ │ ✅ │ ✅ │ ? │ ? │ │ 静态烘焙光照 │ ✅ │ ✅ │ ? │ ✅ │ ✅ │ ? │ │ 纯色/自发光 │ ✅ │ ? │ ❌ │ ? │ ❌ │ ? │ │ UI/2D精灵 │ ✅ │ ❌ │ ❌ │ ✅ │ ❌ │ ? │ │ 简单场景道具 │ ✅ │ ✅ │ ❌ │ ✅ │ ❌ │ ❌ │ └──────────────┴────┴────┴────┴────┴────┴─────┘ ✅需要 ❌可去 ?看需求 最常见的优化 1. 去Tangent(没用法线贴图) ← 收益大(16字节) 2. 去UV2(不烘焙光照) 3. 去Color(不用顶点色)七、常见误区误区真相“Mesh Compression能省运行时内存”主要省包体运行时通常解压“所有属性都留着保险”每个属性都占内存用不到就浪费“去了法线也能光照”不行法线是光照基础“Tangent必须有”只有法线贴图才需要很多物体不用“Compression High随便用”精度损失可能导致模型抖动/变形“UV1和UV0一样”UV1多用于lightmap需求不同八、核心要点总结顶点数据精简两条路 【路径1去除不用的属性】(省运行时内存 ★) 一个顶点含多种属性各自的去留 ├── Position必须留 ├── Normal受光就留不受光可去 ├── Tangent用法线贴图才留 ← 常见可去(16字节) ├── UV0有贴图就留 ├── UV1/UV2烘焙光照才留 ← 常见可去 ├── Color用顶点色才留 ← 常见可去 └── BoneWeight蒙皮动画才有 【路径2Mesh Compression】(省包体) 降低保留属性的数值精度(Off/Low/Med/High) ├── 省磁盘/传输 ├── 运行时通常解压内存收益有限 └── 压太狠会变形/抖动精细模型慎用High 关键区分 ├── 省内存 → 去属性 ├── 省包体 → Compression └── 两者可结合 实战 ├── ModelImporter设置Normals/Tangents为None ├── AssetPostprocessor自动化 └── 最常见收益去Tangent 去UV2 去Color一句话一个顶点携带位置、法线、切线、UV、顶点色等多种属性每种都占字节。去除用不到的属性如没用法线贴图就去切线、不烘焙光照就去UV2能直接减小运行时内存而Mesh Compression是降低数值精度主要减小包体运行时内存收益有限——两者针对不同目标可结合使用。