Unity中Mesh重叠顶点合并参考及其应用

在Unity中,如果将一个模型文件(比如从max里面导出一个fbx文件)导入到编辑器中之后,Unity会把所有在原来在面列表中公用的顶点复制一份,保证每个三角形使用的顶点都是单独的,不与其它三角形共用顶点,Unityh这么做应该有他的道理,但我尚未想明白Unity这么做的原因是什么。

但是考虑到在有些应用中使用共用顶点能够更好的处理一些问题,这里写了一个将Unity中Mesh重叠顶点合并成公用顶点的代码,目的是将位置重叠的顶点去掉,同时也将面列表的内容更新,由一个原来有重叠顶点的Mesh获得一个新的使用公用顶点的Mesh,参考如下:

cs 复制代码
Mesh GetWeldMesh(Mesh mesh)
{
	Vector3[] vs = mesh.vertices;
	int[] ts = mesh.triangles;
	Debug.Log(ts.Length);

	Dictionary<Vector3, int> dicVert = new();
	for (int i = 0; i < ts.Length; i++)
	{
		bool hasSamePos = false;
		Vector3 vert = vs[ts[i]];
		foreach (var kv in dicVert)
		{
			Vector3 key = kv.Key;
			if (vert == key)
			{
				if (kv.Value < ts[i])
				{
					ts[i] = kv.Value;
				}
				else
				{
					dicVert[kv.Key] = ts[i];
				}
				hasSamePos = true;
				break;
			}
		}
		if (!hasSamePos)
		{
			dicVert.Add(vert, ts[i]);
		}
	}

	List<Vector3> listVertice = new();
	foreach (var kv in dicVert)
	{
		listVertice.Add(kv.Key);
	}

	Mesh meshNew = new()
	{
		vertices = vs,
		triangles = ts
	};
	meshNew.RecalculateNormals();
	return meshNew;
}

目前我能想到的这么做的用处有两个,第一个是在一种描边方法中,Shader需要将要描边的物体增加一次渲染,在这次渲染中,Shader会将网格的每个顶点沿着该顶点的法线方向向外挤出一点点,形成一个更大的轮廓,然后反面渲染,这样就形成了原物体的勾边效果,不过这里有个问题,如果使用的Unity默认的Mesh,当物体的光滑组是连续的时候,挤出的效果是没问题的,当光滑组不连续(例如一个Box),顶点挤出后会出现分离问题。你当然可以在类似max这种软件中将Box的光滑组强行编成一组,这样导入Untiy中之后挤出效果倒是没问题了,但是本身显示效果就不是硬边的效果了,这也不对啊。但是如果在使用Shader渲染Mesh的时候,使用的不是原始的Mesh,而是通过上述代码生成的新的Mesh,由于顶点是共用的,就不存在出现挤出分离的问题。关于具体的Shader就不说了,玩过Shader的都懂。

第二个是查找物体的边界,一般来说查找物体边界的方法是找到不被两个三角形共用的边,但是Unity中Mesh默认所有的顶点都是单独被面列表使用的,根本没有所谓被两个三角形共用的边的情况,这中情况会发现所有的三角形的边都是边界,貌似行不通,所以还是需要上述方法将Unity中Mesh重叠顶点合并再去查找边界。其查找边界的代码参考如下(由于Mesh的编辑可能不止一组,所以用的返回值是List<int\[\]>,是顶点编号数组的列表):

cs 复制代码
	List<int[]> GetEdgeIndexes(Mesh mesh)
	{
		Vector3[] vs = mesh.vertices;
		int[] ts = mesh.triangles;

		Dictionary<Edge, int> dicEdge = new();

		for (int i = 0; i < ts.Length; i += 3)
		{
			int indexA = ts[i];
			int indexB = ts[i + 1];
			int indexC = ts[i + 2];

			bool hasSameEdge01 = false;
			bool hasSameEdge02 = false;
			bool hasSameEdge03 = false;
			KeyValuePair<Edge, int>[] kvs = dicEdge.ToArray();
			for (int j = 0; j < kvs.Length; j++)
			{
				Edge edge = kvs[j].Key;
				if (edge.IsSame(indexA, indexB)) { hasSameEdge01 = true; dicEdge[edge] += 1; continue; }
				if (edge.IsSame(indexB, indexC)) { hasSameEdge02 = true; dicEdge[edge] += 1; continue; }
				if (edge.IsSame(indexC, indexA)) { hasSameEdge03 = true; dicEdge[edge] += 1; continue; }
			}
			if (!hasSameEdge01) { dicEdge.Add(new Edge(indexA, indexB), 1); }
			if (!hasSameEdge02) { dicEdge.Add(new Edge(indexB, indexC), 1); }
			if (!hasSameEdge03) { dicEdge.Add(new Edge(indexC, indexA), 1); }
		}

		List<Edge> edges = new List<Edge>();
		foreach (var kv in dicEdge)
		{
			if (kv.Value == 1)
			{
				edges.Add(kv.Key);
			}
		}

		foreach (var item in edges)
		{
			Debug.Log(item.a + " , " + item.b);
		}

		List<int[]> listEdgeIndexes = new();

		while (edges.Count > 0)
		{
			List<int> listTriangle = new()
			{
				edges[0].a,
				edges[0].b
			};
			edges.RemoveAt(0);

			while (true)
			{
				int indexLast = listTriangle[^1];

				bool hasAddIndex = false;
				for (int i = 0; i < edges.Count; i++)
				{
					Edge edge = edges[i];
					if (indexLast == edge.a)
					{
						listTriangle.Add(edge.b);
						hasAddIndex = true;
						edges.RemoveAt(i);
						break;
					}
					if (indexLast == edge.b)
					{
						listTriangle.Add(edge.a);
						hasAddIndex = true;
						edges.RemoveAt(i);
						break;
					}
				}

				if (!hasAddIndex)
				{
					listEdgeIndexes.Add(listTriangle.ToArray());
					break;
				}
			}
		}

		return listEdgeIndexes;
	}

其中所用到的Edge类代码如下:

cs 复制代码
public class Edge
{
	public readonly int a;
	public readonly int b;
	public Edge(int a, int b)
	{
		this.a = a;
		this.b = b;
	}

	public bool IsSame(Edge edge) { return (a == edge.a && b == edge.b) || (a == edge.b && b == edge.a); }
	public bool IsSame(int a, int b) { return (a == this.a && b == this.b) || (a == this.b && b == this.a); }
}

下图将一个球体挖了三个洞洞,通过上述方法查找到边界,并用LineRenderer渲染了边界。

相关推荐
SmalBox12 小时前
【动态着色】Unity 实现消融效果
unity3d·游戏开发·图形学
zhchyun20081 天前
# 【Unity UI 进阶】仿 Element UI 打造企业级 Unity UI 组件库(06)
ui·unity·游戏引擎
SmalBox1 天前
【动态着色】Unity 实现全息投影着色器
unity3d·游戏开发·图形学
Behaviour2 天前
Unity游戏HybridCLR 热更新接入实战
游戏·unity·游戏引擎
LONGZETECH2 天前
新能源汽车动力电池实训教学痛点与虚拟仿真技术解决方案
c语言·3d·unity·架构·汽车·汽车教学软件
王维志2 天前
UiSplineRenderer
unity·游戏引擎
SmalBox2 天前
【动态着色】Unity 实现箭头图案
unity3d·游戏开发·图形学
FairGuard手游加固2 天前
iOS 游戏加固技术解析:代码混淆、资源加密、内存保护与重签名检测
安全·游戏·macos·unity·ios·objective-c
SmalBox3 天前
【扭曲着色器】Unity实现-黑洞扭曲效果
unity3d·游戏开发·图形学
UWA4 天前
GPM 2.0 WebGL 监测能力扩容|覆盖 Unity/Cocos/LayaAir 多引擎微信 & 抖音小游戏
unity·微信·性能优化·小游戏·webgl