Unity 动态创建Mesh 基础方法与高级方法

本文介绍了在Unity项目中,使用高级方法创建Mesh以提高性能,特别是针对顶点数量大时的效率提升,以及与基础方法的区别,包括对Mesh的BufferParams和IndexBufferParams的设置。
摘要由CSDN通过智能技术生成

最近在做项目优化,注意到动态创建Mesh时,Unity提供了一套高级方法用于快速创建模型,特此记录学习一下。

前言

关于Mesh的基本概念再次不在阐述,可以参考Unity Mesh 官方文档,介绍的很详细,其中

基础方法包括:SetVertices、SetNormals、SetUVs、SetTriangles、SetIndices、SetColors、SetTangents、SetBoneWeights
高级方法包括:SetVertexBufferParams、SetVertexBufferData、SetIndexBufferParams、SetIndexBufferData、SetSubMesh。

优势

  1. 使用基础方法有个限制,就是Mesh的最大顶点数量不能超过65535,而高级方法则没有这个限制
  2. 高级方法跳过了一些检查,创建速度更快,尤其模型顶点数量较多的情况下,有性能提升,实测时间缩短将近1/3

示例

基础方法

需要提前准备好模型的数据

属性名含义类型
vertices顶点坐标Verctor3[]
normals法线Verctor3[]
triangles顶点索引int[]
uv纹理坐标Verctor2[]

	//创建Mesh,并赋值,相当于调用SetVertices、SetNormals、SetTriangles、SetUVs
	Mesh mesh = new Mesh();
	mesh.vertices = myMeshes[i].vertices;
	mesh.normals = myMeshes[i].normals;
	mesh.triangles = myMeshes[i].triangles;
	mesh.uv = myMeshes[i].uv;
	
	//将Mesh赋值给MeshFilter组件
	GameObject gameObject = new GameObject();
	MeshFilter mf = gameObject.AddComponent<MeshFilter>();
	mf.sharedMesh = mesh;
	
	//给模型赋予材质
	MeshRenderer mr = gameObject.AddComponent<MeshRenderer>();
	mr.material = material;

高级方法(推荐)

同上,先准备好模型的基础数据


	//
	//顶点属性描述中,添加该模型具有哪些属性,该例中有顶点、法线、一个uv,其中
	//顶点坐标  Position 用 3 个 Float32 数据表示
	//法线向量   Normal  用 3 个 Float32 数据表示
	//纹理坐标 TexCoord0 用 2 个 Float32 数据表示
	//
	VertexAttributeDescriptor[] vertexAttributes = new[]{
	    new VertexAttributeDescriptor(VertexAttribute.Position, VertexAttributeFormat.Float32, 3),
	    new VertexAttributeDescriptor(VertexAttribute.Normal, VertexAttributeFormat.Float32, 3),
	    new VertexAttributeDescriptor(VertexAttribute.TexCoord0, VertexAttributeFormat.Float32, 2)
	};
	
	//
	// 根据顶点数量创建缓冲区
	// 
	// 假设创建一个四方面片,则缓冲区数据如下
	//       顶点          法线           uv
	//    -5, -5,  0,    0, 0, -1,      0, 0,    //第 1 个顶点
	//    -5,  5,  0,    0, 0, -1,      0, 1,    //第 2 个顶点
	//     5, -5,  0,    0, 0, -1,      1, 0,    //第 3 个顶点
	//     5,  5,  0,    0, 0, -1,      1, 1     //第 4 个顶点
	//
	int vertexCount = myMeshes[i].vertices.Length;
	int bufferLength = 3 + 3 + 2;
	int vertexAttributeBufferLength = vertexCount * bufferLength;
	float[] vertexAttributeBuffer = new float[vertexAttributeBufferLength];
	
	//
	// 将准备好的模型数据填充到缓冲区
	//
	Vector3[] vertices = myMeshes[i].vertices;
	Vector3[] normals = myMeshes[i].normals;
	Vector2[] uv = myMeshes[i].uv;
	for (int j = 0; j < vertexCount; j++)
	{
	    int start = j * bufferLength;
		
		//此处 +0 ... +7 的原由。观察四方面片示例
	    vertexAttributeBuffer[start + 0] = vertices[j].x;
	    vertexAttributeBuffer[start + 1] = vertices[j].y;
	    vertexAttributeBuffer[start + 2] = vertices[j].z;
	
	    vertexAttributeBuffer[start + 3] = normals[j].x;
	    vertexAttributeBuffer[start + 4] = normals[j].y;
	    vertexAttributeBuffer[start + 5] = normals[j].z;
	
	    vertexAttributeBuffer[start + 6] = uv[j].x;
	    vertexAttributeBuffer[start + 7] = uv[j].y;
	}
	
	//将顶点缓冲区写入Mesh
	Mesh mesh = new Mesh();
	mesh.SetVertexBufferParams(vertexCount, vertexAttributes);
	mesh.SetVertexBufferData(vertexAttributeBuffer, 0, 0, vertexAttributeBufferLength, 0);
	
	//将顶点索引写入索引缓冲区
	int[] triangles = myMeshes[i].triangles;
	int indexCount = triangles.Length;
	mesh.SetIndexBufferParams(indexCount, IndexFormat.UInt32);
	mesh.SetIndexBufferData(triangles, 0, 0, indexCount);
	
	//每个Mesh至少包含一个SubMesh,也可将上面的缓冲区分开赋值,分别设置到不同的SubMesh
	mesh.subMeshCount = 1;
	SubMeshDescriptor subMeshDescriptor = new SubMeshDescriptor(0, indexCount);
	mesh.SetSubMesh(0, subMeshDescriptor);
	
	//高级方法由于跳过Unity检查,缺失Bounds信息,当任意三角面超过相机的裁剪区域时,整个模型会被裁剪掉(消失不见)
	mesh.RecalculateBounds();
	
	//将Mesh赋值给MeshFilter组件
	GameObject gameObject = new GameObject();
	MeshFilter mf = gameObject.AddComponent<MeshFilter>();
	mf.sharedMesh = mesh;
	
	//给模型赋予材质
	MeshRenderer mr = gameObject.AddComponent<MeshRenderer>();
	mr.material = material;

注意!!!

      由于使用高级方法创建出来的Mesh,跳过了Unity的检查,Mesh的Bounds信息缺失,这会导致一个现象,当模型的任意三角面不在相机的裁剪区域内时,模型会突然消失(被相机裁剪掉),因此需要调用RecalculateBounds方法计算一下模型的Bounds。

      基础方法创建出来的Mesh,Unity默认会对其进行Bounds计算,即使不调用RecalculateBounds方法,也不会出现上述情况。

      只要修改了Mesh的顶点位置后,都需要调用一下RecalculateBounds方法重新计算Bounds,否则包括物理检测、视窗裁剪这些设计到Bounds得计算,都将出错。

备注

此处为了展示高级方法的用法,因此未直接创建完整缓冲区数据,多执行一次数据的组装(即vertexAttributeBuffer数组)。实际应用时,会直接将缓冲区数据准备好(而不是分开存储vertices、normals、uv、triangles),直接调用SetXXXBufferParams、SetXXXBufferData。

参考:其他博主的文章Unity3D学习笔记4——创建Mesh高级接口

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jack_dos

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值