Unity新的MeshData API学习

在新版本的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轴移动测试:

Unity新的MeshData API学习

 

代码如下:

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

上一篇:TLSR8250 APP Mesh组网教程


下一篇:如何用 Nacos 构建服务网格生态