unity模型制作:绘制一个凹多边形
Posted 左右...
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了unity模型制作:绘制一个凹多边形相关的知识,希望对你有一定的参考价值。
(不重要的前言:该博文为系列博文,每一篇有前后文关系,例如基类、组件的集成,如果发现有陌生组件和基类,请查看前面文章,本系列文章单纯应用unity的mesh来绘制模型,并未使用任何三方插件,文章内容、代码都是纯手打,望支持)
1.凹多边形相对凸多边形要复杂很多,画出一个凹多边形需要分两步:
1.1.在平面几何上拆解凹多边形,分成若干个三角形(耳朵),和一个凸多边形。
1.2.将所有耳朵视为一个三角面,加上最后一个凸多边形,合并成图形
2.名词说明
耳朵:一个点和前后两点相连构成的三角形不和多边形其他边线相交,且该点是凸点
凹多边形:包含凹点的多边形就是凹多边形
凹点:此点在(多边形删除此点后的)新多边形的内部
凸点:此点在(多边形删除此点后的)新多边形的外部
3.详解1.1拆解流程:
3.1.判断多边形是一个凹多边形,否则返回凸多边形算法
3.2.遍历多边形每一个点,判断该点是不是凸点,不是就判断下一个点
3.3.如果是凸点,且和前后相连是一个耳朵,则从多边形中去除该点,回到1
可以看到,此算法必然是一个递归,每一层会切掉一个耳朵,直到只剩下一个凸多边形
再按照1.2处理,就可以拼接出一个凹多边形。
4.举个例子:
如图12345是一个凹多边形,完成以下拆解步骤:
a. 12345是一个凹多边形,1号点是凸点,但15线会与34线相交,因此125不是一个耳朵
b. 2号点是凸点,且123是一个耳朵,删除2号点,剩余1345
c. 1345是一个凹多边形,1号点是凸点,且135是一个耳朵,删除1号点剩余345
d. 345是一个凸多边形,结束
ok,我们获得123、135两个耳朵和345一个凸多边形,将他们拼接起来就可以得到预期图形了
5.代码:
核心算法,在GetTriangles()中返回递归结果
public override List<int> GetTriangles()
triangles = new List<int>();
return GetTriangles(positions, triangles);
List<int> GetTriangles(List<Vector3> points, List<int> result)
Debug.Log("count = " + points.Count);
List<int> triangles = new List<int>();
//凹多边形
if (IsConcave(points))
Debug.Log("凹多边形");
for (int i = 0; i < points.Count; i++)
List<Vector3> list = new List<Vector3>(points);
list.RemoveAt(i);
int indexStart = i - 1;
int indexEnd = i + 1;
if (i == 0) indexStart = points.Count - 1;
if (i == points.Count - 1) indexEnd = 0;
Vector3 start = points[indexStart];
Vector3 end = points[indexEnd];
Vector3 point = points[i];
//该点是凸点,而且切割线不与剩下多边形相交,则取出该顶点
bool isConverx = !IsInsidePoint(point, list);
bool isIntersect = IsIntersect(start, end, list);
if (isConverx && !isIntersect)
int index0 = positions.IndexOf(point);
int index1 = positions.IndexOf(start);
int index2 = positions.IndexOf(end);
Debug.Log("triangle:" + index0 + "," + index1 + "," + index2);
triangles.Add(index0);
triangles.Add(index1);
triangles.Add(index2);
result.AddRange(triangles);
return GetTriangles(list, result);
else//凸多边形
Debug.Log("凸多边形");
result.AddRange(GetConverxTriangles(points));
return result;
Debug.LogError("error");
return null;
功能算法:
//凸多边形算法
public List<int> GetConverxTriangles(List<Vector3> points)
List<int> triangles = new List<int>();
for (int i = 1; i < points.Count - 1; i++)
int index0 = i;
int index1 = i + 1;
if (up)
triangles.Add(positions.IndexOf(points[0]));
triangles.Add(positions.IndexOf(points[index0]));
triangles.Add(positions.IndexOf(points[index1]));
else
triangles.Add(positions.IndexOf(points[index0]));
triangles.Add(positions.IndexOf(points[0]));
triangles.Add(positions.IndexOf(points[index1]));
return triangles;
//是否是凹多边形
bool IsConcave(List<Vector3> points)
for (int i = 0; i < points.Count; i++)
List<Vector3> list = new List<Vector3>(points);
list.RemoveAt(i);
if (IsInsidePoint(points[i], list)) return true;
return false;
//一个点是否在一个多边形内部
bool IsInsidePoint(Vector3 point,List<Vector3> points)
int count = 0;
for (int i = 1; i < points.Count; i++)
if (IsIntersect(point, point + Vector3.forward * 100, points[i - 1], points[i])) count++;
if (IsIntersect(point, point + Vector3.forward * 100, points[0], points[points.Count - 1])) count++;
bool r = count % 2 != 0;
return r;
//线段与多边形是否相交
bool IsIntersect(Vector3 start, Vector3 end,List<Vector3> points)
for (int i = 1; i < points.Count; i++)
if (IsIntersect(start, end, points[i - 1], points[i])) return true;
return false;
//两个线段是否相交
bool IsIntersect(Vector3 p11, Vector3 p12, Vector3 p21, Vector3 p22)
if (p11 == null || p12 == null || p21 == null || p22 == null) return false;
if (Mathf.Max(p21.x, p22.x) <= Mathf.Min(p11.x, p12.x)) return false;
if (Mathf.Max(p11.x, p12.x) <= Mathf.Min(p21.x, p22.x)) return false;
if (Mathf.Max(p21.z, p22.z) <= Mathf.Min(p11.z, p12.z)) return false;
if (Mathf.Max(p11.z, p12.z) <= Mathf.Min(p21.z, p22.z)) return false;
float y2221 = p22.z - p21.z;
float x2211 = p22.x - p11.x;
float y2211 = p22.z - p11.z;
float x2212 = p22.x - p12.x;
float y2212 = p22.z - p12.z;
float x2221 = p22.x - p21.x;
if ((x2211 * y2221 - y2211 * x2221) * (x2212 * y2221 - y2212 * x2221) >= 0) return false;
float x2111 = p21.x - p11.x;
float y1211 = p12.z - p11.z;
float y2111 = p21.z - p11.z;
float x1211 = p12.x - p11.x;
if ((x2111 * y1211 - y2111 * x1211) * (x2211 * y1211 - y2211 * x1211) >= 0) return false;
return true;
6.补充:
简单解释一下IsInsidePoint算法,如果判断一个点在多边形内部
如图,任意一点A,发出任意一条射线l,如果l与多边形边线的交点是奇数个,则该点在多边形内部,反之在外部。
通过这行代码可以看出来,我用的forward方向发射一跟100米的线段当l,跟多边形的每一个线段做相交计算,统计交点个数得出结果。
这里有两个小bug
1.如果l和多边形的交点恰巧再顶点上,会多计算一个交点
2.如果多边形比100米还要大,会算不到交点
大家可以根据实际需求相应的完善算法,由于是做demo我就不深入写了,当然欢迎同学们自行优化了算法私信给我。
以上是关于unity模型制作:绘制一个凹多边形的主要内容,如果未能解决你的问题,请参考以下文章