前言
在 Unity 开发中,经常会听到两个词:Mesh 和 Renderer。
简单理解:
Mesh负责描述物体的几何数据,比如顶点、三角形、UV、法线。Renderer负责把这些数据交给渲染管线,结合材质、光照、相机等信息,最终显示到屏幕上。
如果把一个 3D 物体比作一件商品:
Mesh是商品的形状结构。Material是商品的外观材质。Renderer是把商品摆到镜头前并让它能被看见的展示系统。
一、Mesh 是什么?
Mesh 是 Unity 中表示网格模型的数据结构。一个常见的 3D 模型,本质上就是由大量三角形拼接而成的表面,而这些三角形又由顶点组成。
Mesh 本身不负责显示,它只保存模型的几何信息。
1. Mesh 中常见的数据
一个 Mesh 通常包含以下几类核心数据:
| 数据 | 作用 |
|---|---|
| Vertices | 顶点坐标,决定模型的基本形状 |
| Triangles | 三角形索引,决定顶点如何组成面 |
| Normals | 法线,影响光照计算 |
| UV | 贴图坐标,决定纹理如何贴到模型表面 |
| Tangents | 切线,常用于法线贴图 |
| Colors | 顶点颜色,可用于特殊表现 |
| Bounds | 包围盒,用于视锥裁剪等优化 |
2. 顶点和三角形
在 Unity 中,一个最简单的 Mesh 可以只包含顶点和三角形索引。
例如,一个三角形需要 3 个顶点:
csharp
Vector3[] vertices =
{
new Vector3(0, 0, 0),
new Vector3(1, 0, 0),
new Vector3(0, 1, 0)
};
int[] triangles =
{
0, 1, 2
};
这里的 triangles 数组并不是直接存坐标,而是存顶点数组的索引。0, 1, 2 表示使用第 0、1、2 个顶点组成一个三角形。
3. 三角形顶点顺序
Unity 默认通过三角形顶点顺序判断正反面。通常情况下,面朝摄像机的一侧才会被渲染,背面会被剔除,这叫做背面剔除。
如果模型看不见,但数据又没有报错,常见原因之一就是三角形索引顺序反了。
二、MeshFilter 是什么?
Mesh 是数据,但它不能直接挂到 GameObject 上。Unity 通常使用 MeshFilter 组件来保存静态网格引用。
常见组合是:
MeshFilter:保存 Mesh。MeshRenderer:负责渲染 Mesh。
一个普通 3D 模型对象通常长这样:
text
GameObject
├── Transform
├── MeshFilter
└── MeshRenderer
其中:
MeshFilter.sharedMesh指向模型网格资源。MeshRenderer.sharedMaterial指向材质资源。
MeshFilter 只负责"这个物体用哪个网格",它本身不决定显示效果。
三、Renderer 是什么?
Renderer 是 Unity 中所有渲染组件的基类。它负责让物体进入渲染流程。
常见的 Renderer 类型包括:
| Renderer 类型 | 用途 |
|---|---|
| MeshRenderer | 渲染普通静态 Mesh |
| SkinnedMeshRenderer | 渲染带骨骼动画的 Mesh |
| SpriteRenderer | 渲染 2D Sprite |
| LineRenderer | 渲染线段 |
| TrailRenderer | 渲染拖尾 |
| ParticleSystemRenderer | 渲染粒子 |
对于 3D 模型来说,最常见的是 MeshRenderer 和 SkinnedMeshRenderer。
1. MeshRenderer
MeshRenderer 通常和 MeshFilter 搭配使用。
它主要负责:
- 引用材质。
- 参与光照计算。
- 设置阴影投射和接收。
- 设置渲染层级、Probe、Motion Vector 等渲染参数。
- 将 MeshFilter 中的 Mesh 提交给渲染管线。
注意:MeshRenderer 自己不保存 Mesh 数据,它需要从同一个 GameObject 上的 MeshFilter 获取 Mesh。
2. SkinnedMeshRenderer
SkinnedMeshRenderer 用于角色、怪物、可变形模型等带骨骼动画的对象。
它和 MeshRenderer 的区别是:
MeshRenderer渲染静态网格。SkinnedMeshRenderer会根据骨骼动画实时改变顶点位置。
角色模型通常不需要 MeshFilter,因为 SkinnedMeshRenderer 自身就持有 Mesh 和骨骼信息。
四、Mesh、Material、Renderer 的关系
很多初学者会把 Mesh、Material、Renderer 混在一起。可以这样区分:
| 概念 | 负责内容 | 是否决定形状 | 是否决定外观 | 是否负责显示 |
|---|---|---|---|---|
| Mesh | 几何数据 | 是 | 否 | 否 |
| Material | 材质和 Shader 参数 | 否 | 是 | 否 |
| Renderer | 渲染组件 | 否 | 部分参与 | 是 |
一个物体能显示出来,通常需要三个条件:
- 有 Mesh 数据。
- 有 Renderer 组件。
- 有可用的 Material。
缺少其中任意一项,都可能导致物体无法正常显示。
五、手动创建一个 Mesh
下面通过代码创建一个最简单的三角形 Mesh。
csharp
using UnityEngine;
[RequireComponent(typeof(MeshFilter))]
[RequireComponent(typeof(MeshRenderer))]
public class SimpleTriangleMesh : MonoBehaviour
{
private void Start()
{
Mesh mesh = new Mesh();
mesh.name = "Simple Triangle";
Vector3[] vertices =
{
new Vector3(0, 0, 0),
new Vector3(1, 0, 0),
new Vector3(0, 1, 0)
};
int[] triangles =
{
0, 1, 2
};
mesh.vertices = vertices;
mesh.triangles = triangles;
mesh.RecalculateNormals();
mesh.RecalculateBounds();
MeshFilter meshFilter = GetComponent<MeshFilter>();
meshFilter.mesh = mesh;
}
}
使用方式:
- 创建一个空物体。
- 挂载上面的脚本。
- 给物体的
MeshRenderer设置一个材质。 - 运行场景即可看到一个三角形。
这里的关键点是:
Mesh创建几何数据。MeshFilter接收 Mesh。MeshRenderer使用材质把它渲染出来。
六、mesh 和 sharedMesh 的区别
Unity 中经常会看到两个属性:
csharp
meshFilter.mesh
meshFilter.sharedMesh
它们的区别非常重要。
1. mesh
访问 meshFilter.mesh 时,Unity 可能会自动复制一份 Mesh 实例。适合运行时修改当前对象自己的网格。
例如:
csharp
Mesh mesh = meshFilter.mesh;
mesh.vertices = newVertices;
mesh.RecalculateNormals();
这种方式修改的是当前对象使用的 Mesh 实例,通常不会影响其他使用同一个资源的对象。
2. sharedMesh
sharedMesh 指向共享的 Mesh 资源。多个对象可能引用同一个 sharedMesh。
如果直接修改 sharedMesh,可能会影响所有引用这个 Mesh 的对象,甚至影响项目资源。
因此:
- 运行时想单独修改某个物体,优先使用
mesh。 - 编辑器工具或明确要修改共享资源时,才使用
sharedMesh。
七、material 和 sharedMaterial 的区别
Renderer 上也有类似概念:
csharp
renderer.material
renderer.sharedMaterial
1. material
访问 renderer.material 时,Unity 会为当前 Renderer 创建材质实例。适合运行时单独修改某个物体的颜色、透明度等参数。
csharp
Renderer renderer = GetComponent<Renderer>();
renderer.material.color = Color.red;
2. sharedMaterial
sharedMaterial 指向共享材质资源。修改它会影响所有使用该材质的对象。
如果只是想改变一个物体的颜色,不建议直接修改 sharedMaterial。
八、渲染流程简化理解
一个 Mesh 最终显示到屏幕,大致会经历以下过程:
- GameObject 提供 Transform 信息。
- MeshFilter 提供 Mesh 几何数据。
- MeshRenderer 提供 Material 和渲染设置。
- Camera 判断物体是否在视野内。
- Unity 根据渲染管线、Shader、光照、阴影等信息绘制物体。
- 最终结果输出到屏幕。
可以简化为:
text
Mesh 数据
↓
MeshFilter 持有网格
↓
MeshRenderer 结合材质和渲染设置
↓
Camera + Render Pipeline
↓
屏幕显示
九、常见问题
1. 为什么 Mesh 创建了却看不到?
常见原因:
- 没有
MeshRenderer。 - 没有设置材质。
- 三角形索引顺序反了。
- Mesh 的顶点坐标太小、太远或不在摄像机视野内。
- 没有调用
RecalculateBounds(),导致包围盒异常。 - 材质使用的 Shader 不兼容当前渲染管线。
2. 为什么修改一个模型,其他模型也变了?
通常是因为修改了共享资源:
- 修改了
sharedMesh。 - 修改了
sharedMaterial。
如果只想修改当前对象,应该使用:
csharp
meshFilter.mesh
renderer.material
3. 为什么模型有些面显示,有些面不显示?
可能是以下原因:
- 三角形顶点顺序不一致。
- 法线方向错误。
- 材质开启了背面剔除。
- 模型导入时法线或面方向存在问题。
4. MeshRenderer 和 SkinnedMeshRenderer 怎么选?
简单判断:
- 静态模型、场景物体、道具:使用
MeshRenderer。 - 角色、带骨骼动画的模型、需要蒙皮变形的模型:使用
SkinnedMeshRenderer。
十、性能注意点
1. 控制 Mesh 顶点数和三角形数量
顶点和三角形越多,渲染成本越高。移动端项目尤其需要控制模型复杂度。
2. 避免频繁修改 Mesh
如果每帧修改 Mesh 顶点,需要注意 CPU 开销和内存分配。可以尽量复用数组,减少临时对象创建。
3. 合理使用静态合批和动态合批
对于大量不动的物体,可以考虑 Static Batching。对于小型动态物体,可以关注 Dynamic Batching 或 GPU Instancing。
4. 注意材质数量
Renderer 的材质数量会影响 Draw Call。相同 Mesh 如果使用不同材质,通常不能直接合并为同一次绘制。
5. 使用 LOD
远处模型可以使用低模,减少渲染压力。Unity 提供了 LODGroup 组件用于管理不同距离下的模型精度。
总结
Unity 中的 Mesh 和 Renderer 是两个不同层面的概念:
Mesh是模型的几何数据,决定物体长什么形状。Renderer是渲染组件,决定物体是否以及如何被绘制。MeshFilter是普通静态 Mesh 和MeshRenderer之间的桥梁。Material和 Shader 决定物体表面的视觉效果。
理解它们之间的关系后,再去学习模型导入、程序化建模、Shader、渲染管线、性能优化等内容,会清晰很多。
一句话概括:
Mesh 负责"形状",Material 负责"外观",Renderer 负责"显示"。