Unity动态构建mesh绘制多边形算法流程分析和实践
前言
先说一下,写这篇博文的动机,原文的博主代码写的十分潇洒,以至于代码说明和注释都没有,最近恰逢看到,所以以此博文来分析其中的算法和流程
参考博文:https://blog.csdn.net/linxinfa/article/details/78816362
github网址:https://github.com/linxinfa/Unity-ArbitraryPolygonMesh
先复习一下线代
向量的混合积的数学意义是:两个向量叉乘的结果是一个新向量,这个新向量垂直于原向量组成的平面,并且新向量的长度等于原向量合成的平行四边形的面积
向量的混合积是三个向量组成的平行六面体的体积。叉乘可以看成高是单位长度的平行六面体的体积,也就是其平行四边形的面积
操作步骤
- 在场景中创建多个几何体作为mesh多边形的顶点
- 在父物体上,创建几个子物体,顺时针摆放这些物体
代码步骤
- 先通过叉乘计算多边形的面积,叉乘严格按照Inspector面板的摆放顺序进行计算,保证是同一顺序时针,通过判断计算面积结果正负性(Unity采用右手坐标系,但在此处还是加上判断),提前设置顶点(V[n])的顺序
// 计算多边形的面积,按照同一顺序进行叉乘
private float Area()
{
// n = 5
int n = m_points.Count;
float A = 0.0f;
Vector2 pval = Vector2.zero;
Vector2 qval = Vector2.zero;
for (int p = 0; p < n; p++)
{
pval = m_points[p];
qval = m_points[(p + 1) % n];
A += pval.x * qval.y - qval.x * pval.y;
}
return (A * 0.5f);
}
- 按照面积的正负性,改变新创建的顶点数组顺序
if (Area() > 0)
{
// 0 1 2 3 4
for (int v = 0; v < n; ++v)
V[v] = v;
}
else
{
// 将顶点顺序逆过来
// 4 3 2 1 0
for (int v = 0; v < n; ++v)
V[v] = (n - 1) - v;
}
- 此处改了一点参考博文的代码,因为n边形的一个顶点出发只能引出(n-2)条对角线,每次枚举三个连续顶点,判断是否能组成三角形(Snip),因为我们提前设置了每个顶点所能发射的最多对角线数量,所以此处无需考虑三个连续顶点重复采用,如果count为0了,可以理解有nv次机会判断是否形成三角形,如果没更新count(也就是没走if里面)则直接返回
int nv = n;
int count = nv;
int u, w;
// n边形的一个顶点出发只能引出(n-2)条对角线
for (int v = nv - 1; nv >= 3;)
{
// 形成不了三角形时会走这个return
if ((count--) <= 0)
return indices.ToArray();
// 连续三个顶点 不超过nv
u = v;
v = u + 1;
w = u + 2;
u %= nv;
v %= nv;
w %= nv;
// u v w连续三个顶点能组成三角形,且保证顺序是合理的
if (Snip(u, v, w, nv, V))
{
int a, b, c;
a = V[u];
b = V[v];
c = V[w];
// 将顶点按照顺序放入list中
indices.Add(a);
indices.Add(b);
indices.Add(c);
// 做了代码修改,原文写法比较麻烦,将V数组中的值从后往前挪一位
for (int s = v; s + 1 < nv; ++s)
V[s] = V[s + 1];
nv--;
// 原博文是 count = 2 * nv 但其实没必要
count = nv;
}
}
- 判断是否能形成三角形
private bool Snip(int u, int v, int w, int n, int[] V)
{
Vector2 A = m_points[V[u]];
Vector2 B = m_points[V[v]];
Vector2 C = m_points[V[w]];
// 面积如果小于Mathf.Epsilon(接近0的最小正浮点数),就为不能切分成三角形
// AB向量叉乘AC向量结果为ABC三顶点形成封闭图形的面积的两倍,判断是否能形成三角形
if (Mathf.Epsilon > ((B.x - A.x) * (C.y - A.y)) - ((B.y - A.y) * (C.x - A.x)))
return false;
for (int p = 0; p < n; ++p)
{
// 直到p为没选到的边
if ((p == u) || (p == v) || (p == w))
continue;
Vector2 P = m_points[V[p]];
// 防止线边交叉
if (InsideTriangle(A, B, C, P))
return false;
}
return true;
}
- 判断线边交叉情况,一般情况下返回值都是false(cCROSSap一般与aCROSSbp和bCROSScp正负性相反,因为选点的时候故意为之的),除非线边交叉,发生叉乘的正负性发生了改变
- 如下图这种情况,如果注释了InsideTriangle()函数,网格会生成不出来,因为出现了线边交叉情况,导致可能绘制顶点的顺序有顺时针有逆时针,从而导致只有绘制面到了背面或者直接就不出来效果
- 叉乘结果
CA×CP > 0\\
AB×AP > 0
\]
- 正常情况
CA×CP > 0\\
AB×AP < 0
\]
private bool InsideTriangle(Vector2 A, Vector2 B, Vector2 C, Vector2 P)
{
float ax, ay, bx, by, cx, cy, apx, apy, bpx, bpy, cpx, cpy;
float cCROSSap, bCROSScp, aCROSSbp;
ax = C.x - B.x; ay = C.y - B.y;
bx = A.x - C.x; by = A.y - C.y;
cx = B.x - A.x; cy = B.y - A.y;
apx = P.x - A.x; apy = P.y - A.y;
bpx = P.x - B.x; bpy = P.y - B.y;
cpx = P.x - C.x; cpy = P.y - C.y;
// BC向量与BP向量叉乘
aCROSSbp = ax * bpy - ay * bpx;
// CA向量与CP向量叉乘
bCROSScp = bx * cpy - by * cpx;
// AB向量与AP向量叉乘
cCROSSap = cx * apy - cy * apx;
// 保证叉乘的顺序都是一致的
// 此处算的结果一般都为false
// 画个图就可以得知
if (((aCROSSbp >= 0.0f) && (bCROSScp >= 0.0f) && (cCROSSap >= 0.0f)))
{
Debug.Log($"A.x = {A.x}, A.y = {A.y}");
Debug.Log($"B.x = {B.x}, B.y = {B.y}");
Debug.Log($"C.x = {C.x}, C.y = {C.y}");
Debug.Log($"P.x = {P.x}, P.y = {P.y}");
}
// Debug.Log(((aCROSSbp >= 0.0f) && (bCROSScp >= 0.0f) && (cCROSSap >= 0.0f)));
return ((aCROSSbp >= 0.0f) && (bCROSScp >= 0.0f) && (cCROSSap >= 0.0f));
}
效果
Unity动态构建mesh绘制多边形算法流程分析和实践的更多相关文章
- Gradient Boost 算法流程分析
我们在很多Gradient Boost相关的论文及分析文章中都可以看到下面的公式: 但是,对这个公式的理解,我一直也是一知半解,最近,终于下决心对其进行了深入理解. 步骤1:可以看作优化目标的损失函数 ...
- Android笔记--View绘制流程源码分析(一)
Android笔记--View绘制流程源码分析 View绘制之前框架流程分析 View绘制的分析始终是离不开Activity及其内部的Window的.在Activity的源码启动流程中,一并包含 着A ...
- Unity中动态创建Mesh
什么是Mesh? Mesh是指的模型的网格,3D模型是由多边形拼接而成,而多边形实际上又是由多个三角形拼接而成的.即一个3D模型的表面其实是由多个彼此相连的三角面构成.三维空间中,构成这些三角形的点和 ...
- Unity 绘制多边形
最近工程需要用到一个多边形用来查看角色属性,于是就研究了下Mesh用网格做了一个.遗憾的的 UGUI 渲染不了 3D 物体,然后又用了一段时间研究了下UGUI的网格绘制. 不过终于还是完成了,虽然有些 ...
- 在Unity中使用UGUI修改Mesh绘制几何图形
在商店看到这样一个例子,表示很有兴趣,他们说是用UGUI做的.我想,像这种可以随便变形的图形,我第一个就想到了网格变形. 做法1: 细心的朋友应该会发现,每个UGUI可见元素,都有一个‘Canvas ...
- [WebGL入门]十四,绘制多边形
注意:文章翻译http://wgld.org/.原作者杉本雅広(doxas),文章中假设有我的额外说明,我会加上[lufy:].另外,鄙人webgl研究还不够深入.一些专业词语,假设翻译有误,欢迎大家 ...
- Mesh绘制雷达图(UGUI)
参考资料:http://www.cnblogs.com/jeason1997/p/5130413.html ** 描述:雷达图 刷新 radarDate.SetVerticesDirty(); usi ...
- [C#] C# 与 Nessus 交互,动态构建扫描任务计划
C# 与 Nessus 交互,动态构建扫描任务计划 目录 什么是 Nessus? 创建会话类 NessusSession 登录测试 创建操作类 NessusManager 操作测试 什么是 Nessu ...
- 【Silverlight】Bing Maps学习系列(五):绘制多边形(Polygon)图形(转)
[Silverlight]Bing Maps学习系列(五):绘制多边形(Polygon)图形 Bing Maps Silverlight Control支持用户自定义绘制多边形(Polygon)图形, ...
随机推荐
- CodeForces - 1360C
C. Similar Pairs time limit per test 2 seconds memory limit per test 256 megabytes input standard in ...
- P1115_最大子段和(JAVA语言)
思路:贪心.累加求和,若和小于0则设置和为0,因为和小于0时对这段序列和无正作用,只会使整体变小,所以我们把小于0的段舍弃,从下一个数开始求序列和. 题目描述 给出一段序列,选出其中连续且非空的一段使 ...
- C# 输出一个字符串的前缀、后缀和它的子串(信息内容安全 实验一)
一.什么是前后缀 字符串的前缀:符号串左部的任意子串(或者说是字符串的任意首部) 字符串的后缀:符号串右部的任意子串(或者说是字符串的任意尾部) 举例:比如 101110 它的前缀就是空串.1.10. ...
- OMnet++ 初学者教程 第一节 入门
第1部分-入门 1.1模型 首先,让我们从一个包含两个节点的"network"开始.节点将做一些简单的事情:一个是节点将创建一个数据包,而两个节点将继续来回传递相同的数据包.我们将 ...
- Android Studio 待看博文
•前言 学习过程中找到的一些好的博文,有些可能当时就看完了并解决了我的问题,有些可能需要好几天的事件才能消化. 特此记录,方便查阅. •CSDN 给新人的一些基础常识 TextView的文字长度测量及 ...
- 使用Azure API Management, Functions, Power Apps和Logic App构建应用
ASP.NET OpenAPI 可以非常方便的将我们的Web API项目自动文档化,除了自动文档化以外,我们还可以利用Azure API Management将Open API自动文档化了的Web A ...
- 基于sklearn的波士顿房价预测_线性回归学习笔记
> 以下内容是我在学习https://blog.csdn.net/mingxiaod/article/details/85938251 教程时遇到不懂的问题自己查询并理解的笔记,由于sklear ...
- java.net.BindException: Problem binding to [hadoop103:8031] java.net.BindException
ResourceManger启动失败,Namenode启动成功,这个问题排查了好久 在hadoop-2.7.6/logs/yarn-root-resourcemanager-hadoop102.log ...
- 结对作业-stage_1
教学班 罗杰.任建班周五3.4节 gitlab项目地址 Here it is. 成员 周远航(3004) 李辰洋(3477) 结对编程体验 感受 在前期设计时,两人合作可以收集更多资料,提供更多想法, ...
- OO第四单元总结暨OO课程总结
一.第四单元作业总结 本单元的主要任务是对 Uml 图元素进行管理和查询,测试一开始会输入一个静态图,之后会对图中相关内容进行查询. 第13,14次作业 第14次作业新增内容很少,故与第13次作业放在 ...