You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity中在地形/表面绘制动态多边形区域的方案咨询

最优实现方案:动态Mesh + 自定义Unlit Shader

针对WebGL环境下运行时动态顶点多边形的填充绘制需求,这是最灵活、性能最优的方案,适配你的编程和Shader基础,同时支持颜色快速更新。

1. 核心思路

Unity无法直接渲染非三角化的多边形,所以先通过三角化算法将任意顶点数的多边形转为三角面,再用动态Mesh承载数据,最后通过轻量化的Unlit Shader实现填充颜色的控制和更新。

1.1 多边形三角化实现

采用Ear Clipping算法(适合无自交的常规多边形),将顶点列表转为三角面索引。核心代码如下:

public static List<int> Triangulate(List<Vector2> vertices)
{
    List<int> indices = new List<int>();
    List<int> remainingVertices = Enumerable.Range(0, vertices.Count).ToList();

    while (remainingVertices.Count >= 3)
    {
        bool foundEar = false;
        for (int i = 0; i < remainingVertices.Count; i++)
        {
            int prev = remainingVertices[(i - 1 + remainingVertices.Count) % remainingVertices.Count];
            int curr = remainingVertices[i];
            int next = remainingVertices[(i + 1) % remainingVertices.Count];

            Vector2 vPrev = vertices[prev];
            Vector2 vCurr = vertices[curr];
            Vector2 vNext = vertices[next];

            // 判断凸角(顺时针顶点叉积<=0)
            if (IsConvex(vPrev, vCurr, vNext))
            {
                // 检查三角形内是否包含其他顶点
                bool hasInnerVertex = false;
                foreach (int idx in remainingVertices)
                {
                    if (idx == prev || idx == curr || idx == next) continue;
                    if (PointInTriangle(vertices[idx], vPrev, vCurr, vNext))
                    {
                        hasInnerVertex = true;
                        break;
                    }
                }
                if (!hasInnerVertex)
                {
                    indices.Add(prev);
                    indices.Add(curr);
                    indices.Add(next);
                    remainingVertices.RemoveAt(i);
                    foundEar = true;
                    break;
                }
            }
        }
        if (!foundEar) break; // 处理失败,可能是自交多边形
    }
    return indices;
}

private static bool IsConvex(Vector2 a, Vector2 b, Vector2 c)
{
    float cross = (b.x - a.x) * (c.y - b.y) - (b.y - a.y) * (c.x - b.x);
    return cross <= 0;
}

private static bool PointInTriangle(Vector2 p, Vector2 a, Vector2 b, Vector2 c)
{
    float w1 = (a.x * (c.y - a.y) + (p.y - a.y) * (c.x - a.x) - p.x * (c.y - a.y)) / ((b.y - a.y) * (c.x - a.x) - (b.x - a.x) * (c.y - a.y));
    float w2 = (p.y - a.y - w1 * (b.y - a.y)) / (c.y - a.y);
    return w1 >= 0 && w2 >= 0 && (w1 + w2) <= 1;
}

1.2 动态Mesh创建与颜色控制

每个多边形对应一个挂载MeshFilter和MeshRenderer的GameObject,通过代码动态生成Mesh并控制颜色:

public class PolygonDrawer : MonoBehaviour
{
    private Mesh _dynamicMesh;
    private MeshRenderer _renderer;

    void Awake()
    {
        _dynamicMesh = new Mesh();
        _dynamicMesh.name = "RuntimePolygon";
        _dynamicMesh.MarkDynamic(); // 标记为动态Mesh,优化WebGL内存分配
        GetComponent<MeshFilter>().mesh = _dynamicMesh;
        
        _renderer = GetComponent<MeshRenderer>();
        // 使用自定义Unlit Shader
        _renderer.material = new Material(Shader.Find("Custom/UnlitPolygonColor"));
    }

    // 初始化多边形顶点与颜色
    public void InitializePolygon(List<Vector2> vertices, Color fillColor)
    {
        Vector3[] meshVertices = vertices.Select(v => new Vector3(v.x, v.y, 0f)).ToArray();
        List<int> triangleIndices = Triangulate(vertices);

        _dynamicMesh.Clear();
        _dynamicMesh.vertices = meshVertices;
        _dynamicMesh.triangles = triangleIndices.ToArray();
        _dynamicMesh.RecalculateBounds();
        
        UpdatePolygonColor(fillColor);
    }

    // 快速更新颜色
    public void UpdatePolygonColor(Color newColor)
    {
        _renderer.material.color = newColor;
    }
}

1.3 自定义轻量化Unlit Shader

针对WebGL环境优化,编写无光照的简单Shader,仅处理填充颜色:

Shader "Custom/UnlitPolygonColor"
{
    Properties
    {
        _Color ("Fill Color", Color) = (1,1,1,1)
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" "Queue"="Geometry" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
            };

            struct v2f
            {
                float4 pos : SV_POSITION;
            };

            float4 _Color;

            v2f vert (appdata v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                return _Color;
            }
            ENDCG
        }
    }
}

2. WebGL专属优化

  • 批量合并Mesh:如果多边形数量超过50个,建议将多个多边形合并到一个Mesh中(复用顶点和索引数组),通过SubMesh或顶点颜色区分不同多边形,减少Draw Call。
  • Material复用:提前创建Material实例池,避免频繁创建销毁Material,降低WebGL内存开销。
  • 层级控制:通过调整transform.position.z或Shader的Queue标签,控制重叠多边形的显示层级。

3. 替代方案(适合简单场景)

如果多边形数量极少(<10个),可以用RenderTexture在CPU端绘制填充:通过Texture2D.SetPixels实现多边形填充逻辑,再将纹理赋值给UI Image或Plane的Material。但该方案性能低于Mesh方案,不适合大量多边形场景。

4. 拓展建议

  • 边框绘制:给多边形GameObject添加LineRenderer组件,传入顶点列表即可实现边框,调整宽度和颜色即可。
  • 自交多边形支持:若需处理自交多边形,替换Ear Clipping算法为Sweep Line算法,或使用开源的三角化实现。
  • 批量颜色更新:使用GPU Instancing,将颜色属性改为数组,一次性更新多个多边形的颜色,进一步优化性能。

拓展学习方向

  • Unity官方文档:Mesh类、MeshRenderer类、WebGL性能优化指南
  • 计算机图形学基础:多边形三角化算法原理、2D图形渲染管线
  • Shader进阶:WebGL Shader兼容性、透明度混合处理

内容的提问来源于stack exchange,提问作者Tifufu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 04:35:35