Version: Unity 6.0 (6000.0)
语言 : 中文
使用网格 API 创建径向进度指示器
数据绑定

平行曲面细分

要在曲面细分大型网格时提高性能,请使用作业系统。这样可以防止主线程被长时间阻塞。创建曲面细分作业时,请使用 MeshGenerationContext.AddMeshGenerationJob API 为__ UI__(即用户界面,User Interface)让用户能够与您的应用程序进行交互。Unity 目前支持三种 UI 系统。更多信息
See in Glossary
工具包提供作业句柄。这可确保 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 创建径向进度指示器
数据绑定