在新版本的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獲取。這里需要注意,
要在編輯器下檢查Mesh的數據,結構體需要與其保持一致。
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上有示例: