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
相关产品推荐
相关产品推荐

