版本:Unity 6 (6000.0)
语言:英语
使用网格 API 创建径向进度指示器
数据绑定

并行细分

为了在细分大型网格Unity 的主要图形基元。网格构成 3D 世界的很大一部分。Unity 支持三角形或四边形多边形网格。NURBS、NURMS、细分曲面必须转换为多边形。 更多信息
查看 术语表
时提高性能,请使用作业系统。这可以防止主线程被长时间阻塞。创建细分作业时,请使用 MeshGenerationContext.AddMeshGenerationJob API 向 UI(用户界面) 允许用户与您的应用程序交互。Unity 目前支持三种 UI 系统。 更多信息
查看 术语表
工具包提供作业句柄。这可确保 UI 工具包在访问网格之前等待作业完成。

在主线程上分配的作业化细分

如果可以快速计算顶点数或索引数,请使用主线程分配网格并将其添加到绘制序列中。您可以使用以下 API 来执行此操作

注意:您可以在调用 generateVisualContent 期间的任何时间点将网格添加到绘制序列中。

以下示例演示了在主线程上分配的作业化细分,其中作业执行实际的细分。

using System;
using Unity.Collections;
using Unity.Jobs;
using UnityEngine;
using UnityEngine.UIElements;

class CheckerboardElement : VisualElement
{
    struct CheckerboardJob : IJob
    {
        [WriteOnly] public NativeSlice<Vertex> vertices;
        [WriteOnly] public NativeSlice<ushort> indices;

        public int horizontalCount;
        public int verticalCount;
        public float divisions;
        public Color32 color1;
        public Color32 color2;

        public void Execute()
        {
            Span<Color32> colors = stackalloc Color32[2];
            colors[0] = color1;
            colors[1] = color2;
            int colorIndex = 0;

            int vCount = 0;
            int iCount = 0;

            float left = 0;
            float right = divisions;
            for (int i = 0; i < horizontalCount; ++i)
            {
                float top = 0;
                float bottom = divisions;
                for (int j = 0; j < verticalCount; ++j)
                {
                    colorIndex = (i ^ j) & 1;

                    Color32 color = colors[colorIndex];

                    vertices[vCount + 0] = new Vertex { position = new Vector3(left, bottom), tint = color };
                    vertices[vCount + 1] = new Vertex { position = new Vector3(left, top), tint = color };
                    vertices[vCount + 2] = new Vertex { position = new Vector3(right, top), tint = color };
                    vertices[vCount + 3] = new Vertex { position = new Vector3(right, bottom), tint = color };

                    indices[iCount + 0] = (ushort)(vCount + 0);
                    indices[iCount + 1] = (ushort)(vCount + 1);
                    indices[iCount + 2] = (ushort)(vCount + 2);
                    indices[iCount + 3] = (ushort)(vCount + 2);
                    indices[iCount + 4] = (ushort)(vCount + 3);
                    indices[iCount + 5] = (ushort)(vCount + 0);

                    vCount += 4;
                    iCount += 6;
                    top += divisions;
                    bottom += divisions;
                }

                left += divisions;
                right += divisions;
            }
        }
    }

    int m_Divisions;
    Color32 m_Color1;
    Color32 m_Color2;

    public CheckerboardElement(int divisions, Color32 color1, Color32 color2)
    {
        generateVisualContent = OnGenerateVisualContent;
        m_Divisions = divisions;
        m_Color1 = color1;
        m_Color2 = color2;
    }

    void OnGenerateVisualContent(MeshGenerationContext mgc)
    {
        int horizontalCount = Mathf.FloorToInt(layout.width / m_Divisions);
        int verticalCount = Mathf.FloorToInt(layout.height / m_Divisions);

        if (horizontalCount == 0 || verticalCount == 0)
            return;

        var job = new CheckerboardJob
        {
            horizontalCount = horizontalCount,
            verticalCount = verticalCount,
            divisions = m_Divisions,
            color1 = m_Color1,
            color2 = m_Color2
        };

        int quads = horizontalCount * verticalCount;

        mgc.AllocateTempMesh(quads * 4, quads * 6, out job.vertices, out job.indices);
        mgc.DrawMesh(job.vertices, job.indices);

        JobHandle jobHandle = job.Schedule();
        mgc.AddMeshGenerationJob(jobHandle);
    }
}

在作业中分配的作业化细分

如果计算顶点数或索引数的成本很高,请在绘制序列中插入一个 MeshGenerationNode。然后将节点与 TempMeshAllocator 一同提供给您的作业。您可以使用以下 API 来执行此操作

在这种方法中,请从作业中执行分配。使用 TempMeshAllocator.AllocateTempMesh 从作业中安全地分配临时网格。 MeshGenerationNode.DrawMesh 充当由作业填充的嵌套绘制序列的容器。

以先前的示例为基础,以下示例演示了如何从作业中执行分配和细分

using System;
using Unity.Collections;
using Unity.Jobs;
using UnityEngine;
using UnityEngine.UIElements;

class CheckerboardElement_AllocFromJob : VisualElement
{
    struct CheckerboardJob : IJob
    {
        [ReadOnly]
        public TempMeshAllocator allocator;
        public MeshGenerationNode node;

        public int horizontalCount;
        public int verticalCount;
        public float divisions;
        public Color32 color1;
        public Color32 color2;

        public void Execute()
        {
            // Let's pretend that the number of vertices/indices is difficult to compute...
            int quads = horizontalCount * verticalCount;
            int totalVertexCount = quads * 4;
            int totalIndexCount = quads * 6;

            // Allocate the mesh from the job
            allocator.AllocateTempMesh(totalVertexCount, totalIndexCount, out NativeSlice<Vertex> vertices, out NativeSlice<ushort> indices);

            Span<Color32> colors = stackalloc Color32[2];
            colors[0] = color1;
            colors[1] = color2;
            int colorIndex = 0;

            int vCount = 0;
            int iCount = 0;

            float left = 0;
            float right = divisions;
            for (int i = 0; i < horizontalCount; ++i)
            {
                float top = 0;
                float bottom = divisions;
                for (int j = 0; j < verticalCount; ++j)
                {
                    colorIndex = (i ^ j) & 1;

                    Color32 color = colors[colorIndex];

                    vertices[vCount + 0] = new Vertex { position = new Vector3(left, bottom), tint = color };
                    vertices[vCount + 1] = new Vertex { position = new Vector3(left, top), tint = color };
                    vertices[vCount + 2] = new Vertex { position = new Vector3(right, top), tint = color };
                    vertices[vCount + 3] = new Vertex { position = new Vector3(right, bottom), tint = color };

                    indices[iCount + 0] = (ushort)(vCount + 0);
                    indices[iCount + 1] = (ushort)(vCount + 1);
                    indices[iCount + 2] = (ushort)(vCount + 2);
                    indices[iCount + 3] = (ushort)(vCount + 2);
                    indices[iCount + 4] = (ushort)(vCount + 3);
                    indices[iCount + 5] = (ushort)(vCount + 0);

                    vCount += 4;
                    iCount += 6;
                    top += divisions;
                    bottom += divisions;
                }

                left += divisions;
                right += divisions;
            }

            // Add the mesh to the draw sequence of the node
            node.DrawMesh(vertices, indices);
        }
    }

    int m_Divisions;
    Color32 m_Color1;
    Color32 m_Color2;

    public CheckerboardElement_AllocFromJob(int divisions, Color32 color1, Color32 color2)
    {
        generateVisualContent = OnGenerateVisualContent;
        m_Divisions = divisions;
        m_Color1 = color1;
        m_Color2 = color2;
    }

    void OnGenerateVisualContent(MeshGenerationContext mgc)
    {
        int horizontalCount = Mathf.FloorToInt(layout.width / m_Divisions);
        int verticalCount = Mathf.FloorToInt(layout.height / m_Divisions);

        if (horizontalCount == 0 || verticalCount == 0)
            return;

        var job = new CheckerboardJob
        {
            horizontalCount = horizontalCount,
            verticalCount = verticalCount,
            divisions = m_Divisions,
            color1 = m_Color1,
            color2 = m_Color2
        };

        mgc.GetTempMeshAllocator(out job.allocator); // Provide the job with the allocator
        mgc.InsertMeshGenerationNode(out job.node); // Insert a node in the element draw sequence

        JobHandle jobHandle = job.Schedule();
        mgc.AddMeshGenerationJob(jobHandle);
    }
}

其他资源

使用网格 API 创建径向进度指示器
数据绑定