Unity3D学习笔记5——创建子Mesh
目录
1. 概述
在文章Unity3D学习笔记4——创建Mesh高级接口通过高级API的方式创建了一个Mesh,里面还提到了一个SubMesh的概念。Mesh是对于三维物体对象的封装概念,一个很容易的需求是,有的地方我希望用到材质A,有的地方我希望用到材质B,我不想把这个Mesh进行拆分,那么很简单,就在这个Mesh中划分两个子Mesh就可以了。
2. 详论
2.1. 实现
我们创建如下脚本,并且随便挂接两个不同的材质在属性material1和属性material2上:
using UnityEngine; using UnityEngine.Rendering; [ExecuteInEditMode] public class Note5Main : MonoBehaviour { public Material material1; public Material material2; // Start is called before the first frame update void Start() { Mesh mesh = CreateMesh(); MeshFilter mf = gameObject.GetComponent<MeshFilter>(); if (mf == null) { mf = gameObject.AddComponent<MeshFilter>(); } mf.sharedMesh = mesh; MeshRenderer meshRenderer = gameObject.GetComponent<MeshRenderer>(); if (meshRenderer == null) { meshRenderer = gameObject.AddComponent<MeshRenderer>(); } Material[] materials = new Material[2]; materials[0] = material1; materials[1] = material2; meshRenderer.materials = materials; } Mesh CreateMesh() { Mesh mesh = new Mesh(); const int vertexCount = 8; Vector3[] vertices = new Vector3[vertexCount] { new Vector3(-5, 0, 0), new Vector3(-5, 5, 0), new Vector3(5, 0, 0), new Vector3(5, 5, 0), new Vector3(-5, -5, 0), new Vector3(-5, 0, 0), new Vector3(5, -5, 0), new Vector3(5, 0, 0), }; Vector3[] normals = new Vector3[vertexCount] { new Vector3(0, 0, -1), new Vector3(0, 0, -1), new Vector3(0, 0, -1), new Vector3(0, 0, -1), new Vector3(0, 0, -1), new Vector3(0, 0, -1), new Vector3(0, 0, -1), new Vector3(0, 0, -1), }; Vector2[] uv = new Vector2[vertexCount] { new Vector2(0, 0), new Vector2(0, 1), new Vector2(1, 0), new Vector2(1, 1), new Vector2(0, 0), new Vector2(0, 1), new Vector2(1, 0), new Vector2(1, 1), }; mesh.vertices = vertices; mesh.normals = normals; mesh.uv = uv; int[] triangles = new int[12] { 0, 1, 2, 1, 3, 2, 4, 5, 6, 5, 7, 6 }; MeshUpdateFlags flags = MeshUpdateFlags.DontValidateIndices | MeshUpdateFlags.DontResetBoneBounds | MeshUpdateFlags.DontNotifyMeshUsers | MeshUpdateFlags.DontRecalculateBounds; //MeshUpdateFlags flags = MeshUpdateFlags.Default; int indexCount = triangles.Length; mesh.SetIndexBufferParams(indexCount, IndexFormat.UInt32); mesh.SetIndexBufferData(triangles, 0, 0, indexCount, flags); mesh.subMeshCount = 2; SubMeshDescriptor subMeshDescriptor1 = new SubMeshDescriptor(0, 6); mesh.SetSubMesh(0, subMeshDescriptor1, flags); SubMeshDescriptor subMeshDescriptor2 = new SubMeshDescriptor(6, 6); mesh.SetSubMesh(1, subMeshDescriptor2, flags); return mesh; } // Update is called once per frame void Update() { } }
我这里得到的效果如下:
2.2. 解析
很明显,我这里创建了两个四边形,并且将其放到一个Mesh下。创建顶点属性我使用的是简单接口,创建顶点索引属性信息使用的是高级接口。关键点在于对SubMesh的描述:
mesh.subMeshCount = 2; SubMeshDescriptor subMeshDescriptor1 = new SubMeshDescriptor(0, 6); mesh.SetSubMesh(0, subMeshDescriptor1, flags); SubMeshDescriptor subMeshDescriptor2 = new SubMeshDescriptor(6, 6); mesh.SetSubMesh(1, subMeshDescriptor2, flags);
SubMeshDescriptor类定义了从那个顶点索引开始,之后多长的空间是一个SubMesh,也就是对Mesh做了一个划分。另外,GameObject上挂接的材质个数也要对应:
MeshRenderer meshRenderer = gameObject.GetComponent<MeshRenderer>(); if (meshRenderer == null) { meshRenderer = gameObject.AddComponent<MeshRenderer>(); } Material[] materials = new Material[2]; materials[0] = material1; materials[1] = material2; meshRenderer.materials = materials;
MeshRenderer上能挂接多个材质,有多少个SubMesh就应该有多少个材质,它们是一一对应的。数量没对应上Unity编辑器会报错。
通过划分SubMesh的方式来描述一个Mesh通常是用于存在多个材质的情况,如果使用的都是同一个材质,就最好不要作SubMesh划分。我们打开Frame Debug,可以看到:
一个Mesh分成了居然两个渲染指令来实现!原因在于图像引擎通常是一个状态机,一个材质需要对应一个渲染指令,这就是为什么我们往往要尽可能复用材质,减少不同材质的个数。
3. 参考
分类: Unity3D