更新日期:2020年5月13日。
Github源码:[点我获取源码]
索引
- Fragmentization
- 使用
- 参数
- 原理及算法
- 图像展示
Fragmentization
设置一个碎化起始点,使得网格从该点开始逐渐破碎。
使用
在Hierarchy界面选中一个带网格渲染组件(MeshRenderer或SkinnedMeshRenderer)的物体,在物体名字上单击右键,选择创建MeshEditor > Effects > Fragmentization。
调用Play播放特效,Pause暂停特效,UnPause恢复特效,Stop停止特效。
参数
Fragmentization参数面板:
1.Play On Start:是否在Start时自动播放特效。
2.Frag Point:碎化起点。
3.Frag Health:碎片生命力。
4.Frag Speed:碎片分离速度。
5.Frag Rate:碎化速率。
6.Fragment Type:碎片行为控制者。
原理及算法
碎化算法主要分为以下四个阶段:
1.计算离碎化起点最近的三角面,保存为起点三角面;
2.从起点三角面开始,计算碎化顺序;
3.根据碎化顺序逐渐分离三角面;
4.如果发现网格还有剩下的三角面,重新执行1。
计算起点三角面:计算起点三角面很简单,遍历所有三角面比一下距离就可以了,这点可以丢到子线程里面去,不过开销并不大,暂时没这么做。
//获取第一个三角面,碎化起点
private Triangle GetFirstTriangle(MeshData meshData)
{
Vector3 fragPoint = transform.worldToLocalMatrix.MultiplyPoint3x4(FragPoint);
Triangle triangle = meshData.Triangles[0];
float distance = Vector3.Distance(fragPoint, meshData.Triangles[0].Center);
for (int i = 1; i < meshData.Triangles.Count; i++)
{
float dis = Vector3.Distance(fragPoint, meshData.Triangles[i].Center);
if (dis < distance)
{
triangle = meshData.Triangles[i];
distance = dis;
}
}
return triangle;
}
计算碎化顺序:这里的算法稍微有点复杂,采用相邻三角面排序算法,一个开启列表、准备列表、关闭列表(HashSet)同时参与计算,思路:查询准备列表,只要准备列表中存在三角面,就将该三角面纳入开启列表,开启列表中的三角面会被立即加入关闭列表(完成排序),然后开启列表中的三角面的邻居会被纳入准备列表。目标是根据网格中三角面的相邻关系,生成一个列表。
//获取三角面碎化顺序算法
private void GetTrianglesOrder(Triangle first)
{
_trianglesOrder.Clear();
_triangleOpen.Clear();
_triangleReady.Clear();
_triangleClose.Clear();
//将第一个三角面加入准备列表
_triangleReady.Add(first);
//如果还有准备中的三角面
while (_triangleReady.Count > 0)
{
//将准备中的三角面纳入开启列表
_triangleOpen.UnionWith(_triangleReady);
_triangleReady.Clear();
//遍历开启列表,逐一关闭
foreach (var item in _triangleOpen)
{
CloseTriangle(item);
}
//遍历开启列表,逐一将其邻居纳入准备列表
foreach (var item in _triangleOpen)
{
ReadyNeighborTriangle(item);
}
_triangleOpen.Clear();
}
_triangleOpen.Clear();
_triangleReady.Clear();
_triangleClose.Clear();
}
//关闭三角面,三角面加入关闭列表
private void CloseTriangle(Triangle triangle)
{
if (!_triangleClose.Contains(triangle))
{
_triangleClose.Add(triangle);
_trianglesOrder.Add(triangle);
triangle.BrokenLinkVertex();
}
}
//准备相邻三角面,三角面加入准备列表
private void ReadyNeighborTriangle(Triangle triangle)
{
foreach (var item in triangle.Vertex1.Triangles)
{
_triangleReady.Add(item);
}
foreach (var item in triangle.Vertex2.Triangles)
{
_triangleReady.Add(item);
}
foreach (var item in triangle.Vertex3.Triangles)
{
_triangleReady.Add(item);
}
}
分离三角面:这里会根据碎化顺序弹出排在第一位的三角面,并生成实体碎片(实体碎片对象池),直到排序列表中不存在三角面,然后再次确认网格是否还有三角面残留(某些模型由几部分网格组成,网格之间不相邻,只通过执行一次计算顺序算法并不能找到所有三角面)。
//分离三角面
private void Fragmentization(MeshData meshData)
{
if (_trianglesOrder.Count > 0)
{
Triangle triangle = _trianglesOrder[0];
_trianglesOrder.RemoveAt(0);
meshData.RemoveTriangle(triangle);
GenerateFragment(triangle);
}
else
{
_timer = 0;
if (meshData.Triangles.Count > 0)
{
GetTrianglesOrder(GetFirstTriangle(meshData));
}
else
{
Stop();
}
}
}
图像展示