先看效果:
再看代码:
using System.Collections.Generic;
using UnityEngine;
public class WireMesh : MonoBehaviour
{
[SerializeField]
Material material;
void Start()
{
Mesh mesh = OptimizeMesh(GetComponent<MeshFilter>().mesh);
GameObject objWire = new(gameObject.name + "_wire");
objWire.layer = LayerMask.NameToLayer("Wire");
Mesh meshWird = new() { vertices = mesh.vertices };
int[] edges = GetEdges(mesh);
meshWird.SetIndices(edges, MeshTopology.Lines, 0);
//-------------------------------------------------------------------------------------------
MeshFilter meshFilter = objWire.AddComponent<MeshFilter>();
meshFilter.mesh = meshWird;
MeshRenderer meshRenderer = objWire.AddComponent<MeshRenderer>();
meshRenderer.material = material;
objWire.transform.SetParent(transform);
objWire.transform.localPosition = Vector3.zero;
objWire.transform.localRotation = Quaternion.identity;
objWire.transform.localScale = Vector3.one;
int[] GetEdges(Mesh mesh)
{
var hashEdges = new HashSet<(int, int)>();
var triangles = mesh.triangles;
for (int i = 0; i < triangles.Length; i += 3)
{
AddEdge(triangles[i], triangles[i + 1]);
AddEdge(triangles[i + 1], triangles[i + 2]);
AddEdge(triangles[i + 2], triangles[i]);
}
List<int> listTriangle = new();
foreach (var val in hashEdges)
{
listTriangle.Add(val.Item1);
listTriangle.Add(val.Item2);
}
return listTriangle.ToArray();
void AddEdge(int id1, int id2)
{
var edge = (Mathf.Min(id1, id2), Mathf.Max(id1, id2));
hashEdges.Add(edge); // 只添加唯一组合
}
}
}
Mesh OptimizeMesh(Mesh originalMesh, bool recalculate = false)
{
Vector3[] originalVertices = originalMesh.vertices;
int[] originalTriangles = originalMesh.triangles;
Dictionary<Vector3, int> uniqueVertices = new Dictionary<Vector3, int>();
List<Vector3> newVertices = new List<Vector3>();
List<int> newTriangles = new List<int>();
// Re-index vertices
for (int i = 0; i < originalVertices.Length; i++)
{
Vector3 vertex = originalVertices[i];
if (!uniqueVertices.ContainsKey(vertex))
{
uniqueVertices[vertex] = newVertices.Count;
newVertices.Add(vertex);
}
}
// Map old indices to new ones
for (int i = 0; i < originalTriangles.Length; i++)
{
int oldIndex = originalTriangles[i];
int newIndex = uniqueVertices[originalVertices[oldIndex]];
newTriangles.Add(newIndex);
}
// Create new optimized mesh
Mesh optimizedMesh = new()
{
vertices = newVertices.ToArray(),
triangles = newTriangles.ToArray()
};
//
if (recalculate)
{
optimizedMesh.RecalculateNormals();
optimizedMesh.RecalculateBounds();
}
return optimizedMesh;
}
}
这个代码给一个物体添加一个与原始物体重叠的网格渲染的物体。
这里面涉及到了一些知识点和操作方法,逐条说一下。
首先是网格内容优化。这里说的优化只是Mesh记录数据量的优化,并不是说能在渲染速度上的优化。为什么这么说呢?当我们把一个模型文件导入到unity中后,不管原来的网格数据是怎样的,unity总是会让顶点数组中的顶点(Vector3)复制出多个来,假设一个fbx格式的立方体有8个顶点,但Unity在使用时会显示Mesh包含24个顶点,不知道是否是出于提升渲染速度的考虑。但我们要尽可能优化这个点数,这样索引的最大值也会变小,也能让我们渲染的wire的边变少,否则同样的边会重复渲染多次。这里OptimizeMesh方法将Mesh中位置重叠的顶点去掉,同时也重置了面索引。在GetEdges方法中使用HashSet,这保证了不出现重叠的边。最后我们让Mesh使用了MeshTopology.Lines方式渲染,就得到了最终的结果。