在新版本的Unity中提供了MeshDataArray和MeshData两个API,使Mesh数据操作支持多线程,以更好的支持DOTS。
API文档:https://docs.unity3d.com/es/2020.2/ScriptReference/Mesh.MeshData.html
1.IJob修改顶点
首先用Mesh.AllocateWritableMeshData分配一个可写的网格数据,然后通过jobs进行顶点操作,
最后通过Mesh.ApplyAndDisposeWritableMeshData接口赋值回Mesh。
用简单的正弦波x轴移动测试:
代码如下:
using System; using System.Linq; using Unity.Burst; using Unity.Collections; using Unity.Jobs; using Unity.Mathematics; using UnityEngine; using UnityEngine.Rendering; [RequireComponent(typeof(MeshFilter))] public class MDT1 : MonoBehaviour { [BurstCompile] public struct TestJob : IJobParallelFor { [ReadOnly] public float time; [ReadOnly] public NativeArray<Vector3> sourceVertices; [WriteOnly] public NativeArray<Vector3> writeVertices; public void Execute(int index) { Vector3 vert = sourceVertices[index]; vert.x += math.sin(index + time) * 0.03f; writeVertices[index] = vert; } } public MeshFilter meshFilter; private NativeArray<Vector3> mCacheVertices; private NativeArray<Vector3> mCacheNormals; private ushort[] mCacheTriangles; private Mesh mCacheMesh; private void Awake() { Mesh sourceMesh = meshFilter.sharedMesh; mCacheVertices = new NativeArray<Vector3>(sourceMesh.vertices, Allocator.Persistent); mCacheNormals = new NativeArray<Vector3>(sourceMesh.normals, Allocator.Persistent); mCacheTriangles = sourceMesh.triangles .Select(m => (ushort) m) .ToArray(); mCacheMesh = new Mesh(); GetComponent<MeshFilter>().mesh = mCacheMesh; } private void OnDestroy() { mCacheVertices.Dispose(); mCacheNormals.Dispose(); } private void Update() { Mesh.MeshDataArray dataArray = Mesh.AllocateWritableMeshData(1); Mesh.MeshData data = dataArray[0]; data.SetVertexBufferParams(mCacheVertices.Length, new VertexAttributeDescriptor(VertexAttribute.Position), new VertexAttributeDescriptor(VertexAttribute.Normal, stream: 1)); data.SetIndexBufferParams(mCacheTriangles.Length, IndexFormat.UInt16); NativeArray<Vector3> vertices = data.GetVertexData<Vector3>(); NativeArray<ushort> indices = data.GetIndexData<ushort>(); NativeArray<Vector3> normals = new NativeArray<Vector3>(mCacheNormals.Length, Allocator.TempJob); data.GetNormals(normals); for (int i = 0; i < normals.Length; ++i) normals[i] = mCacheNormals[i]; normals.Dispose(); for (int i = 0; i < mCacheTriangles.Length; ++i) indices[i] = mCacheTriangles[i]; TestJob job = new TestJob() { time = Time.time, sourceVertices = mCacheVertices, writeVertices = vertices }; job .Schedule(mCacheVertices.Length, 8) .Complete(); data.subMeshCount = 1; data.SetSubMesh(0, new SubMeshDescriptor(0, mCacheTriangles.Length)); Mesh.ApplyAndDisposeWritableMeshData(dataArray, mCacheMesh); mCacheMesh.RecalculateNormals(); mCacheMesh.RecalculateBounds(); } }
2.直接通过结构获取Mesh对应字段,并修改更新
也可以直接设置和Mesh字段结构一样的结构体,直接GetVertexData获取。这里需要注意,
顶点索引数据类型一般是uint16,可在编辑器内看下,不要声明错了
using System; using Unity.Collections; using Unity.Mathematics; using UnityEngine; using UnityEngine.Rendering; public class MDT2 : MonoBehaviour { struct VertexStruct { public float3 pos; public float3 normal; public float4 tangent; public float2 uv0; public float2 uv1; } public Mesh srcMesh; public MeshFilter meshFilter; private Mesh mCacheMesh; private NativeArray<VertexStruct> mCacheInVertices; private void Start() { mCacheMesh = new Mesh(); meshFilter.sharedMesh = mCacheMesh; } private void Update() { Mesh.MeshDataArray inMeshDataArray = Mesh.AcquireReadOnlyMeshData(srcMesh); Mesh.MeshData inMesh = inMeshDataArray[0]; mCacheInVertices = inMesh.GetVertexData<VertexStruct>(); int vertexCount = srcMesh.vertexCount; int indexCount = srcMesh.triangles.Length; Mesh.MeshDataArray outMeshDataArray = Mesh.AllocateWritableMeshData(1); Mesh.MeshData outMesh = outMeshDataArray[0]; outMesh.SetVertexBufferParams(vertexCount, new VertexAttributeDescriptor(VertexAttribute.Position), new VertexAttributeDescriptor(VertexAttribute.Normal), new VertexAttributeDescriptor(VertexAttribute.Tangent, VertexAttributeFormat.Float32, 4), new VertexAttributeDescriptor(VertexAttribute.TexCoord0, VertexAttributeFormat.Float32, 2), new VertexAttributeDescriptor(VertexAttribute.TexCoord1, VertexAttributeFormat.Float32, 2)); outMesh.SetIndexBufferParams(indexCount, IndexFormat.UInt16); NativeArray<ushort> indices = outMesh.GetIndexData<ushort>(); for (int i = 0; i < srcMesh.triangles.Length; ++i) indices[i] = (ushort) srcMesh.triangles[i]; NativeArray<VertexStruct> outVertices = outMesh.GetVertexData<VertexStruct>(); for (int i = 0; i < mCacheInVertices.Length; i++) { VertexStruct vert = mCacheInVertices[i]; vert.pos.x += math.sin(i + Time.time) * 0.03f; outVertices[i] = vert; } outMesh.subMeshCount = 1; SubMeshDescriptor subMeshDesc = new SubMeshDescriptor { indexStart = 0, indexCount = indexCount, topology = MeshTopology.Triangles, firstVertex = 0, vertexCount = vertexCount, bounds = new Bounds(Vector3.zero, Vector3.one * 100f) }; outMesh.SetSubMesh(0, subMeshDesc); Mesh.ApplyAndDisposeWritableMeshData(outMeshDataArray, mCacheMesh); mCacheMesh.RecalculateNormals(); mCacheMesh.RecalculateBounds(); mCacheInVertices.Dispose(); inMeshDataArray.Dispose(); } }
3.Graphics Buffer
在unity2021.2版本之后可以拿到Graphics Buffer类型:
mesh.GetVertexBuffer()
该类型可直接作为Buffer参数传入ComputeShader中。具体在github上有示例:
https://github.com/Unity-Technologies/MeshApiExamples