Home Mesh
Post
Cancel

Mesh

image-20230205203206859

Mesh

  1. FBX 文件在Unity中会实例化成GameObject,MeshFilter 中的 Mesh 存储了网格的顶点,三角形索引,UV,顶点颜色,切线,法线,骨骼等渲染所需的必要数据。

从头开始构建mesh

  1. 先设置顶点(vertices),再设置三角形(triangles)。
1
2
3
4
5
6
7
8
9
10
11
12
Vector3[] newVertices;
Vector2[] newUV;
int[] newTriangles;

void Start()
{
    Mesh mesh = new Mesh();
    mesh.vertices = newVertices;
    mesh.uv = newUV;
    mesh.triangles = newTriangles;
    GetComponent<MeshFilter>().mesh = mesh;
}

仅修改顶点属性, 不改变顶点数目

  1. 获取顶点属性,修改,重新设置到mesh中。
1
2
3
4
5
6
7
8
9
10
11
12
13
private void Update()
{
    Mesh mesh = GetComponent<MeshFilter>().mesh;
    Vector3[] vertices = mesh.vertices;
    Vector3[] normals = mesh.normals;

    for (var i = 0; i < vertices.Length; i++)
    {
        vertices[i] += normals[i] * Mathf.Sin(Time.time);
    }

    mesh.vertices = vertices;
}

连续不断的增减三角形和顶点

  1. 先调用Clear清空整个mesh, 再设置顶点(vertices)和其他属性,最后设置三角形索引(triangles indices)。
  2. Unity总是检查三角形索引看他们是否引用了数组外的顶点。调用Clear然后再设置顶点,最后设置三角形,可以确保三角形不会引用非法的数据。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
private Vector3[] vertices;
private int verticesCount;
private Vector2[] uvs;
private int uvsCount;
private int[] triangles;
private int trianglesCount;



private Mesh mesh;

private void Awake()
{
    mesh = GetComponent<MeshFilter>().sharedMesh = new Mesh();
    mesh.name = "CustomMesh";
    vertices = new Vector3[128];
    uvs = new Vector2[128];
    triangles = new int[128];

}

private void Update()
{
    // Do some calculations...
    // update vertices, uvs, triangles...
    mesh.Clear();
    mesh.SetVertices(vertices, 0, verticesCount);
    mesh.SetUVs(0, uvs, 0, uvsCount);
    mesh.SetTriangles(triangles, 0, trianglesCount, 0);

}

SubMesh

  1. Unity中所有的submesh存储在一个mesh中,materials中记录每个子网格用到的材质。
  2. 需要一个网格使用多个材质时就拆分成多个子网格,每个子网格赋予不同的材质。
  3. 每个子网格都有自己材质球,导致子网格越多,增加的 drawcall越多,且子网格无法与其他网格合并,导致优化的一个重要环节被阻断。
  4. 有时可以选择把网格完全拆分成其他网格来代替子网格。

![[Pasted image 20221203145759.png]] 这种不叫子网格,这种还是拆分好的独立的网格。


MeshFilter

  1. MeshFilter 承载网格数据,Mesh 实例化后存储在 MeshFilter 类中。
  2. MeshFilter 对 mesh 操作将生成新的网格实例,对 sharedMesh 操作将会改变与其他模型共同拥有的那个指定的网格数据实例。

mesh & sharedMesh

  1. mesh 是实例型的变量,对mesh执行任何操作,都会额外复制一份后再重新赋值,即使只是get操作。
  2. sharedMesh 是共享型变量,多个 3D 模型可以公用同一个指定的 sharedMesh,修改 sharedMesh,指向同一个 sharedMesh 的多个模型会同时发生变化。
  3. 优先使用 sharedMesh

MeshRenderer

  1. 一个 Renderer 只能渲染一个网格。
  2. MeshRenderer 提取 MeshFilter 中的网格数据,用给定的 [[Material]] 绘制。
  3. MeshRenderer 对 material 操作将生成新的材质实例,对 sharedMaterial 操作将会改变与其他模型共同拥有的那个指定的材质实例。

material & sharedMaterial

  1. material 的工作原理类似 mesh,是实例型的变量。
  2. sharedMaterial 的工作原理类似 sharedMaterial,是共享型变量。
  3. 优先使用 sharedMaterial

materials & sharedMaterials

  1. materialssharedMaterials 可以针对不同的子网格。
  2. material = materials[0]
  3. sharedMaterial = sharedMaterials[0]

动态替换 sharedMaterials[0]

  1. sharedMaterials 的 get 方法返回的是一份拷贝的材质数组。
  2. sharedMaterial 拿到的是 m_Materials 数组的第一个元素。
  3. 两者的 set 方法都是对整个数组进行重新赋值。
1
2
3
4
5
6
7
8
9
10
11
12
Renderer renderer = gameObject.GetComponent<Renderer>();

// 错误方法,不生效
renderer.sharedMaterials[0] = newMat;    

// 正确赋值 materials & sharedMaterials 的方法
Material[] arrMat = renderer.sharedMaterials;
arrMat[0] = newMat;
renderer.sharedMaterials = arrMat;

// 单独赋值 material & sharedMaterial 也是生效的
renderer.sharedMaterial = newMat;

网格包围盒

  1. AABB包围盒基本概念见:[[包围盒]]
轴对齐包围盒
1
2
3
4
// 本地空间的轴对齐包围盒
mesh.bounds
// 世界空间的轴对齐包围盒
meshRenderer.bounds
通过某包围盒扩大本包围盒
1
2
Bounds b = new Bounds(Vector3.zero, Vector3.zero);
b.Encapsulate(childMeshRenderer.bounds);
编辑器中画出网格包围盒
1
2
3
4
5
6
7
8
private void OnDrawGizmosSelected () {
    // A sphere that fully encloses the bounding box
    var center = renderer.bounds.center;
    var radius = renderer.bounds.extents.magnitude;
    // Draw it
    Gizmos.color = Color.white;
    Gizmos.DrawWireSphere (center, radius);
}
This post is licensed under CC BY 4.0 by the author.
Contents