Unity3D实现模型随机切割
- 作者: 胖妞要瘦
- 来源: 51数据库
- 2021-07-04
本文实例为大家分享了unity3d实现模型随机切割的具体代码,供大家参考,具体内容如下
模型切割的效果图如下:


我们都知道,模型是由一个个小三角形面组成的,因此我们不妨将问题简化,先实现个小目标,完成单个三角形的切割,甚至继续细分成求一条线段与某个平面的交点。
三角形与切割平面的位置关系主要有以下三种:
1. 三角形与切割平面有两个交点,一个交点在顶点上,一个交点在边上。这时,原有的三角形将被分成两个三角形,分别为013、042。

2. 三角形与切割平面有两个交点,两个交点都在边上。这时,原有的三角形将被分成三个三角形,分别为:034、561、126。

3. 其它(无交点、三角形完全在切割平面上、一条边在切割平面上)

那么,我们如何求线段与平面的交点呢?
即已知平面abc,线段p0p1,求交点p。
故:
n为平面abc法向量,可得:n= ab x ac;
p在p0p1上,可得:p = p0 + t * l; l = p1 - p0;
又因p在平面abc上,可得: n * pa = 0;
代入得:
=> n * (a - p0 + t * l) = 0;
=> n * (a - p0) + t * n * l = 0;
=> t = (p0 - a) * n / (n * l);
=> t = (p0 - a) * (ab x ac) / (n * (p1 - p0));
最终求得p坐标,因为p0p1是线段而非直线,所以我们需要再做个判断,p是否在线段p0p1中间,用向量点乘可轻易实现。
具体代码如下,其中abc为切割平面上的三个顶点(确保必定构成一个平面):
public static gameobject[] split(gameobject obj, vector3 a, vector3 b, vector3 c)
{
if(obj == null)
{
return null;
}
meshfilter filter = obj.getcomponent<meshfilter>();
if(filter == null)
{
return null;
}
//切割面位置调整为相对于模型的本地坐标
a = a - obj.transform.position;
b = b - obj.transform.position;
c = c - obj.transform.position;
list<vector3> vertices = new list<vector3>(filter.mesh.vertices);
list<int> triangles = new list<int>(filter.mesh.triangles);
list<vector2> uvs = new list<vector2>(filter.mesh.uv);
for (int i = 0; i < filter.mesh.triangles.length; i = i + 3)
{
//取三角形;
vector3[] p = new vector3[3];
for (int m = 0; m < 3; m++)
{
p[m] = filter.mesh.vertices[filter.mesh.triangles[i + m]];
}
//0 1 2
//1 2 0 ==> 切割每条边,判断是否有交点,如有交点,在交点处生成两个新的顶点:l/r
//2 0 1
//凡是顶点与平面相交的,一律以新顶点替换
//判断以交点为其中一个顶点的三角形在面的哪一面
//指定:交点到其它顶点形成的向量与平面法向量方向一致,则使用l,否则使用r
//无交点
//其中一个顶点在平面上
//其中的一条边在平面上
//整个三角形都在平面上
list<point> cross = new list<point>();
for (int m = 0; m < 3; m++)
{
//求线段与面的交点-无交点返回null
point tpoint = mathfutils.linecrossplane(p[m], p[(m + 1) % 3], a, b, c);
//排除线段两个端点与平面相交的情况;
if (mathfutils.pointatplane(p[m], a, b, c) || mathfutils.pointatplane(p[(m + 1) % 3], a, b, c))
{
cross.add(null);
continue;
}
cross.add(tpoint);
}
int tcount = cross.findall(t => t != null).count;
if (tcount == 0)
{
//完全没交点;
continue;
}
if(tcount == 1)
{
//只与一条边有交点;
//012 tidx = 0 交点x在 0-1上,则有三角形 02x 12x
//012 tidx = 1 交点x在 1-2上,则有三角形 01x 02x
//012 tidx = 2 交点x在 2-3上,则有三角形 01x 12x
int tidx = cross.findindex(t => t != null);
if(tidx < 0)
{
continue;
}
vertices.add(cross[tidx].getvector3());
vertices.add(cross[tidx].getvector3());
vector2 tuv = (uvs[triangles[i + tidx]] + uvs[triangles[i + (tidx + 1) % 3]]) * 0.5f;
uvs.add(tuv);
uvs.add(tuv);
//计算法线,保证新三角形与原来的三角形法线保持一致;
vector3 nor0 = vector3.cross((p[1] - p[0]).normalized, (p[2] - p[0]).normalized);
//改一个
triangles[i + 0] = filter.mesh.triangles[i + tidx];
triangles[i + 1] = filter.mesh.triangles[i + (tidx + 2) % 3];
triangles[i + 2] = vertices.count - 2;
vector3 nor1 = vector3.cross((vertices[triangles[i + 1]] - vertices[triangles[i + 0]]).normalized,
(vertices[triangles[i + 2]] - vertices[triangles[i + 0]]).normalized);
if(vector3.dot(nor0, nor1) < 0)
{
//使用法线方向判断三角形顶点顺序是否与原来一致
int tpidx = triangles[i + 1];
triangles[i + 1] = triangles[i + 2];
triangles[i + 2] = tpidx;
}
//新增一个
triangles.add(filter.mesh.triangles[i + (tidx + 1) % 3]);
triangles.add(filter.mesh.triangles[i + (tidx + 2) % 3]);
triangles.add(vertices.count - 1);
vector3 nor2 = vector3.cross((vertices[triangles[triangles.count - 2]] - vertices[triangles[triangles.count - 3]]).normalized,
(vertices[triangles[triangles.count - 1]] - vertices[triangles[triangles.count - 3]]).normalized);
if (vector3.dot(nor0, nor2) < 0)
{
int tpidx = triangles[triangles.count - 1];
triangles[triangles.count - 1] = triangles[triangles.count - 2];
triangles[triangles.count - 2] = tpidx;
}
}
if(tcount == 2)
{
//与两条边有交点;
//012 tidx = 0 交点xy不在 0-1上,则有三角形 xy2 xy1 01y
//012 tidx = 1 交点xy不在 1-2上,则有三角形 xy0 xy2 12y
//012 tidx = 2 交点xy不在 2-3上,则有三角形 xy1 xy0 01y
// x-y-tidx+2 是独立三角形,使用一组顶点
int tidx = cross.findindex(t => t == null);
if (tidx < 0)
{
continue;
}
//计算法线,保证新三角形与原来的三角形法线保持一致;
vector3 nor0 = vector3.cross((p[1] - p[0]).normalized, (p[2] - p[0]).normalized);
//x
vertices.add(cross[(tidx + 1) % 3].getvector3());
vertices.add(cross[(tidx + 1) % 3].getvector3());
vector2 tuvx = (uvs[triangles[i + (tidx + 1) % 3]] + uvs[triangles[i + (tidx + 2) % 3]]) * 0.5f;
uvs.add(tuvx);
uvs.add(tuvx);
//y
vertices.add(cross[(tidx + 2) % 3].getvector3());
vertices.add(cross[(tidx + 2) % 3].getvector3());
vector2 tuvy = (uvs[triangles[i + tidx]] + uvs[triangles[i + (tidx + 2) % 3]]) * 0.5f;
uvs.add(tuvy);
uvs.add(tuvy);
//改一个
triangles[i + 0] = filter.mesh.triangles[i + (tidx + 2) % 3];
triangles[i + 1] = vertices.count - 4;
triangles[i + 2] = vertices.count - 2;
vector3 nor1 = vector3.cross((vertices[triangles[i + 1]] - vertices[triangles[i + 0]]).normalized,
(vertices[triangles[i + 2]] - vertices[triangles[i + 0]]).normalized);
if (vector3.dot(nor0, nor1) < 0)
{
int tpidx = triangles[i + 1];
triangles[i + 1] = triangles[i + 2];
triangles[i + 2] = tpidx;
}
//新增一个
triangles.add(filter.mesh.triangles[i + (tidx + 1) % 3]);
triangles.add(vertices.count - 3);
triangles.add(vertices.count - 1);
vector3 nor2 = vector3.cross((vertices[triangles[triangles.count - 2]] - vertices[triangles[triangles.count - 3]]).normalized,
(vertices[triangles[triangles.count - 1]] - vertices[triangles[triangles.count - 3]]).normalized);
if (vector3.dot(nor0, nor2) < 0)
{
int tpidx = triangles[triangles.count - 1];
triangles[triangles.count - 1] = triangles[triangles.count - 2];
triangles[triangles.count - 2] = tpidx;
}
//新增一个
triangles.add(filter.mesh.triangles[i + tidx % 3]);
triangles.add(filter.mesh.triangles[i + (tidx + 1) % 3]);
triangles.add(vertices.count - 1);
vector3 nor3 = vector3.cross((vertices[triangles[triangles.count - 2]] - vertices[triangles[triangles.count - 3]]).normalized,
(vertices[triangles[triangles.count - 1]] - vertices[triangles[triangles.count - 3]]).normalized);
if (vector3.dot(nor0, nor3) < 0)
{
int tpidx = triangles[triangles.count - 1];
triangles[triangles.count - 1] = triangles[triangles.count - 2];
triangles[triangles.count - 2] = tpidx;
}
}
}
//根据顶点索引数组确定mesh被分成了几份
//经实验:不可行;因为同一个位置的点在不同的面中是不同的点,无法判断这两个三角形是否是连接起来的
//故只能按方向将模型分成两个
list<list<int>> ntriangles = new list<list<int>>();
list<list<int>> temps = new list<list<int>>();
list<list<vector3>> nvertices = new list<list<vector3>>();
list<list<vector2>> nuvs = new list<list<vector2>>();
//切割面的法向量;
vector3 pnormal = vector3.cross((c - a).normalized, (b - a).normalized);
ntriangles.add(new list<int>());
ntriangles.add(new list<int>());
temps.add(new list<int>());
temps.add(new list<int>());
nuvs.add(new list<vector2>());
nuvs.add(new list<vector2>());
nvertices.add(new list<vector3>());
nvertices.add(new list<vector3>());
for (int i = 0; i < triangles.count; i = i + 3)
{
//判断新的三角形在面的哪一侧;
float t = 0;
for(int j = 0; j < 3; j++)
{
vector3 dir = (vertices[triangles[i + j]] - a).normalized;
float tt = vector3.dot(dir, pnormal);
t = mathf.abs(tt) > mathf.abs(t) ? tt : t;
}
int tidx = t >= 0 ? 0 : 1;
for (int j = 0; j < 3; j++)
{
int idx = temps[tidx].indexof(triangles[i + j]);
if (idx < 0)
{
ntriangles[tidx].add(nvertices[tidx].count);
nvertices[tidx].add(vertices[triangles[i + j]]);
temps[tidx].add(triangles[i + j]);
nuvs[tidx].add(uvs[triangles[i + j]]);
continue;
}
ntriangles[tidx].add(idx);
}
}
if(nvertices[0].count == 0 || nvertices[1].count == 0)
{
//没有切割到物体
return null;
}
//生成新的模型;
list<gameobject> items = new list<gameobject>();
meshrenderer render = obj.getcomponent<meshrenderer>();
for (int i = 0; i < ntriangles.count; i++)
{
gameobject tobj = new gameobject(i.tostring());
tobj.transform.position = obj.transform.position;
items.add(tobj);
meshfilter fi = tobj.addcomponent<meshfilter>();
meshrenderer mr = tobj.addcomponent<meshrenderer>();
if(render != null)
{
mr.material = render.material;
}
mesh mesh = new mesh();
mesh.vertices = nvertices[i].toarray();
mesh.triangles = ntriangles[i].toarray();
mesh.uv = nuvs[i].toarray();
mesh.recalculatenormals();
mesh.recalculatetangents();
mesh.recalculatebounds();
fi.mesh = mesh;
}
return items.toarray();
}
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
推荐阅读
