对三维空间进行三角剖分的大体逻辑思路和流程同二维空间的三角剖分是一样的,二维的代码实践过程可以前往本专栏的第一篇文章:二维 二维空间的三角剖分。

在理清楚二维平面下三角剖分的思路之后,我们尝试将其中的一些概念升级一个维度。

我们先来分析Delaunay三角剖分从二维空间到三维空间过程中的一些变化:

1.剖分成若干个三角形单元——剖分成若干个四面体单元

2.根据x、y排序点集——根据x、y、z排序点集

3.根据排序好的点集构建超级三角形——根据排序好的点集构建超级四面体

4.构造用于缓存边数据的列表——构造用于缓存面数据的列表

5.计算每个三角形的外接圆,判断该三角形与点之间的空间关系——计算每个四面体的外接球,判断该四面体与点之间的空间关系

6.最终得到一个三角形列表——最终得到一个四面体列表

先上效果图(图为Unity中用Gizmos将每个四面体单元顶点连线画出来的预览图):

尝试加入障碍物AABB包围盒后进行剖分,并用Mesh网格渲染显现:

一、输入与输出

输入:

    /// <summary>/// 点集/// </summary>public List<Vector3> _vertices = new List<Vector3>();

输出:

    /// <summary>/// 四面体列表/// </summary>List<Tetrahedron> _tetrahedron = new List<Tetrahedron>();

二、所需要自定义的类:

1.Surface类:存放四面体每一个面的数据

用三个Vector3来定义一个三角形面:

public class Surface{public Vector3 P1;public Vector3 P2;public Vector3 P3;public Surface(Vector3 P1,Vector3 P2,Vector3 P3){this.P1 = P1;this.P2 = P2;this.P3 = P3;}}

2.Tetrahedron类:存放每个四面体单元的数据

首先是参数和构造函数:

参数包括四面体的四个顶点和四个三角形面、外接球球心、外接球半径以及一个判断是否为可用四面体的bool值;

构造函数传入的参数为一个顶点和一个三角形面,即以一个顶点和一个已存在的三角形面生成一个新的四面体;

public class Tetrahedron{public Vector3 P1;public Vector3 P2;public Vector3 P3;public Vector3 P4;public Vector3 Center;public double R;public Surface E1;public Surface E2;public Surface E3;public Surface E4;public bool isBad;public Tetrahedron(Vector3 V, Surface P){P1 = V;P2 = P.P1;P3 = P.P2;P4 = P.P3;GetTetrahedronExcenterRadius();SurfaceValue();}
}

其次是四面体类中的一些计算函数:

(1).计算该四面体的外接球:

        /// <summary>/// 计算四面体的外接球/// </summary>public void GetTetrahedronExcenterRadius(){float x1 = P1.x; float x2 = P2.x; float x3 = P3.x; float x4 = P4.x;float y1 = P1.y; float y2 = P2.y; float y3 = P3.y; float y4 = P4.y;float z1 = P1.z; float z2 = P2.z; float z3 = P3.z; float z4 = P4.z;float a11 = x2 - x1;float a12 = y2 - y1;float a13 = z2 - z1;float b1 = (float)0.5 * ((x2 - x1) * (x2 + x1) + (y2 - y1) * (y2 + y1) + (z2 - z1) * (z2 + z1));float a21 = x3 - x1;float a22 = y3 - y1;float a23 = z3 - z1;float b2 = (float)0.5 * ((x3 - x1) * (x3 + x1) + (y3 - y1) * (y3 + y1) + (z3 - z1) * (z3 + z1));float a31 = x4 - x1;float a32 = y4 - y1;float a33 = z4 - z1;float b3 = (float)0.5 * ((x4 - x1) * (x4 + x1) + (y4 - y1) * (y4 + y1) + (z4 - z1) * (z4 + z1));float temp = a11 * (a22 * a33 - a23 * a32) + a12 * (a23 * a31 - a21 * a33) + a13 * (a21 * a32 - a22 * a31);float x0 = ((a12 * a23 - a13 * a22) * b3 + (a13 * a32 - a12 * a33) * b2 + (a22 * a33 - a23 * a32) * b1) / temp;float y0 = -((a11 * a23 - a13 * a21) * b3 + (a13 * a31 - a11 * a33) * b2 + (a21 * a33 - a23 * a31) * b1) / temp;float z0 = ((a11 * a22 - a12 * a21) * b3 + (a12 * a31 - a11 * a32) * b2 + (a21 * a32 - a22 * a31) * b1) / temp;float radius = (float)Math.Sqrt((x0 - x1) *2 + (y0 - y1) * 2 + (z0 - z1) * 2);Center = new Vector3(x0,y0,z0);R = GetDistance(P1, Center);}private double GetDistance(Vector3 A, Vector3 B){return Math.Sqrt(Math.Pow((A.x - B.x), 2) + Math.Pow((A.y - B.y), 2) + Math.Pow((A.z - B.z), 2));}

(2).判断该四面体与一个点之间的空间关系:

public bool isComtain(Vector3 node){GetTetrahedronExcenterRadius();if ((node - Center).sqrMagnitude <= R * R)return true;elsereturn false;}

(3).通过一个点与一个面构造出四面体后,对该四面体中的面参数进行赋值:

public void SurfaceValue(){E1 = new Surface(P1, P2,P3);E2 = new Surface(P1,P2, P4);E3 = new Surface(P1, P3,P4);E4 = new Surface(P2, P3,P4);}

(4).检查该四面体是否包含超级四面体中的顶点(若包含的话最后要从四面体列表中删去)

public void Check(Tetrahedron s){Vector3[] su = new Vector3[4];su[0] = s.P1;su[1] = s.P2;su[2] = s.P3;su[3] = s.P4;if (su.Contains(P1) || su.Contains(P2) || su.Contains(P3)|| su.Contains(P4))isBad = true;int i;float ans;Vector3 s1, s2, s3;for (i = 0; i < 4; i++){s1.x = su[1].x - su[0].x; s1.y = su[1].y - su[0].y; s1.z = su[1].z - su[0].z;s2.x = su[2].x - su[0].x; s2.y = su[2].y - su[0].y; s2.z = su[2].z - su[0].z;s3.x = su[3].x - su[0].x; s3.y = su[3].y - su[0].y; s3.z = su[3].z - su[0].z;ans = s1.x * s2.y * s3.z + s1.y * s2.z * s3.x + s1.z * s2.x * s3.y - s1.z * s2.y * s3.x - s1.x * s2.z * s3.y - s1.y * s2.x * s3.z;if (ans == 0)isBad = true;}}

三、Delaunay 二维三角剖分过程

1.变量

    #region 3D三角剖分参数/// <summary>/// 点集/// </summary>public List<Vector3> _vertices = new List<Vector3>();/// <summary>/// 超级四面体/// </summary>Tetrahedron SuperTetrahedron;/// <summary>/// 面列表/// </summary>List<Surface> _surface = new List<Surface>();/// <summary>/// 三角形列表/// </summary>List<Tetrahedron> _tetrahedron = new List<Tetrahedron>();#endregion

2.剖分过程:

(1)第一步:对输入的点集进行排序(根据x,y,z依次进行排序):

 _vertices = _vertices.OrderBy(o => o.x).ThenBy(o => o.y).ThenBy(o => o.z).ToList();

(2)第二步:根据排序好的点集构建超级四面体(即找到并构造出包含点集中所有节点的四面体):

    SuperTetrahedron = FindSuper_Tetrahedron(_vertices);/// <summary>/// 找到超级四面体/// </summary>/// <returns></returns>public Tetrahedron FindSuper_Tetrahedron(List<Vector3> _vertices){Vector3 Circle;float Radius;float xmin = _vertices[0].x;float xmax = _vertices[_vertices.Count - 1].x;float ymin = _vertices[0].y;float ymax = _vertices[_vertices.Count - 1].y;float zmin = _vertices[0].z;float zmax = _vertices[_vertices.Count - 1].z;foreach (var dot in _vertices){if (ymin > dot.y)ymin = dot.y;if (ymax <= dot.y)ymax = dot.y;if (zmin > dot.z)zmin = dot.z;if (zmax <= dot.z)zmax = dot.z;}Vector3 P1 = new Vector3(xmin, ymin, zmin);Vector3 P3 = new Vector3(xmax, ymax, zmax);Circle = (P1 + P3) / 2;Radius = Mathf.Sqrt((P1 - P3).sqrMagnitude);Vector3 sP1 = Circle + new Vector3(0, Radius * 3, 0);Vector3 sP2 = Circle + new Vector3(0, -Radius, Radius * 2 * Mathf.Sqrt(2));Vector3 sP3 = Circle + new Vector3(Radius * Mathf.Sqrt(6), -Radius, -Radius * Mathf.Sqrt(2));Vector3 sP4 = Circle + new Vector3(-Radius * Mathf.Sqrt(6), -Radius, -Radius * Mathf.Sqrt(2));Tetrahedron super_Tetrahedron = new Tetrahedron(sP1, new Surface(sP2,sP3,sP4));//Debug.LogError((sP1+sP2+sP3+sP4)/4);return super_Tetrahedron;}

同样是用笨笨的方法(逻辑类似于构造AABB包围盒的思路),这一步有待改进;

(3)第三步:剖分三维空间!

剖分的过程和二维的思路一样,从超级四面体开始,找到存在与四面体外接球内的点对该四面体进行分解重构:

首先将刚刚构造好的超级四面体的四个顶点加入点集列表:

        _vertices.Add(SuperTetrahedron.P1);_vertices.Add(SuperTetrahedron.P2);_vertices.Add(SuperTetrahedron.P3);_vertices.Add(SuperTetrahedron.P4);

先别着急剖分!记得先初始化四面体列表和面缓存列表,然后将超级四面体加入四面体列表:

        _tetrahedron.Clear();_surface.Clear();_tetrahedron.Add(SuperTetrahedron);

然后就可以大刀阔斧的进行剖分啦!循环走起!

 for (int i = 0; i < _vertices.Count; i++){_surface.Clear();int index = 0;while (index < _tetrahedron.Count){if (_tetrahedron[index].isComtain(_vertices[i])){AddSurface(_surface, _tetrahedron[index].E1);AddSurface(_surface, _tetrahedron[index].E2);AddSurface(_surface, _tetrahedron[index].E3);AddSurface(_surface, _tetrahedron[index].E4);_tetrahedron.Remove(_tetrahedron[index]);}else{index++;}}foreach (var e in _surface){Tetrahedron Ttemp = new Tetrahedron(_vertices[i], e);_tetrahedron.Add(Ttemp);}}

还记得二维剖分中的AddEdge()函数吗?三维剖分中的AddSurface()函数也是一样的:

public void AddSurface(List<Surface> _surface, Surface E){bool isAdd = true;int index = 0;while (index < _surface.Count){if (_surface[index].P1 == E.P1 && _surface[index].P2 == E.P2 && _surface[index].P3 == E.P3|| _surface[index].P1 == E.P1 && _surface[index].P2 == E.P3 && _surface[index].P3 == E.P2|| _surface[index].P1 == E.P3 && _surface[index].P2 == E.P2 && _surface[index].P3 == E.P1|| _surface[index].P1 == E.P2 && _surface[index].P2 == E.P1 && _surface[index].P3 == E.P3|| _surface[index].P1 == E.P2 && _surface[index].P2 == E.P3 && _surface[index].P3 == E.P1|| _surface[index].P1 == E.P3 && _surface[index].P2 == E.P1 && _surface[index].P3 == E.P2){_surface.Remove(_surface[index]);isAdd = false;}else{index++;}}if (isAdd){_surface.Add(E);}}

剖分完成后,我们开始筛查哪些四面体用了超级四面体的四个顶点,因为这四个顶点是我们构造出来的本来是不存在的,所以我们要将这些四面体删去:

        int Tindex = 0;while (Tindex < _tetrahedron.Count){_tetrahedron[Tindex].Check(SuperTetrahedron);if (_tetrahedron[Tindex].isBad)_tetrahedron.Remove(_tetrahedron[Tindex]);elseTindex++;}

最后,记得移去超级四面体的四个顶点和超级四面体本身:

        _tetrahedron.Remove(SuperTetrahedron);_vertices.Remove(SuperTetrahedron.P1);_vertices.Remove(SuperTetrahedron.P2);_vertices.Remove(SuperTetrahedron.P3);_vertices.Remove(SuperTetrahedron.P4);

三维空间的三角初步剖分就完成了:

不过目前的三维空间剖分是不完美的,因为在这样的剖分情况下会在很多情况下产生细而长的四面体,这是我们不希望得到的四面体,所以目前方案还需进行优化。

三维空间的三角剖分( 3D Delaunay Triangulated graph)第二部分:剖分三维空间相关推荐

  1. 三角剖分与Delaunay三角剖分

    本博客转载自 http://blog.csdn.net/raby_gyl/article/details/17409717 请其他同学转载时注明原始文章的出处! Delaunay三角剖分是1934年发 ...

  2. 攻克3D神器Blender的第二天-【挤出】

    目录 前言回顾 功能预览 挤出 挤出选区 挤出流形 沿法向挤出 挤出各个面 挤出至光标 前言回顾 不熟悉快捷键属性的可以点击传送门预览. 传送门: 官网地址 传送门: 攻克3D神器Blender的第一 ...

  3. 三角剖分与Delaunay三角剖分及带约束的Delaunay三角剖分

    本博客参考 http://blog.csdn.net/raby_gyl/article/details/17409717 http://baike.baidu.com/view/1691145.htm ...

  4. MATLAB实现三角剖分(Delaunay)算法

    三角剖分定义 [定义]三角剖分:假设V是二维实数域上的有限点集,边e是由点集中的点作为端点构成的封闭线段,E为e的集合.那么该点集V的一个三角剖分T = (V,E)是一个平面图G,该平面图满足条件: ...

  5. Delaunay 三角剖分3D(原理 + 源码)

    文章目录 Delaunay 三角剖分 二维与三维的区别 代码实现< Python版本 > 代码实现< Matlab 版本 > Delaunay 三角剖分 原理部分请参照我的上一 ...

  6. 火云开发课堂 - 《使用Cocos2d-x 开发3D游戏》系列 第二十五节: 3D项目优化方案

    <使用Cocos2d-x 开发3D游戏>系列在线课程 第二十五节:3D项目优化方案 视频地址:http://edu.csdn.net/course/detail/1330/20825?au ...

  7. 火云开发课堂 - 《使用Cocos2d-x 开发3D游戏》系列 第二节:Cocos引擎开发环境搭建与项目创建!

    <使用Cocos2d-x 开发3D游戏>系列在线课程 第二节:Cocos引擎开发环境搭建与项目创建! 视频地址:http://edu.csdn.net/course/detail/1330 ...

  8. 火云开发课堂 - 《使用Cocos2d-x 开发3D游戏》系列 第二十四节:小项目实训《绝命沙滩》

    <使用Cocos2d-x 开发3D游戏>系列在线课程 第二十四节:小项目实训<绝命沙滩> 视频地址:http://edu.csdn.net/course/detail/1330 ...

  9. 三角剖分(delaunay)拓扑结构 高维近邻

    在高维数据中,如果想要找到高维数据间的近邻,如果仅仅是使用k-nearnest采样点,由于没有考虑高维数据间的拓扑结构,这样得到的近邻是不准确的,尤其是在进行高维数据间的数据预测(线性插值)时.所以我 ...

最新文章

  1. 【OSX】OSX下采用MAMP的PHP替换系统自带PHP
  2. 用REDIS实现分布式缓存
  3. 集合 Subset Sums
  4. HTML5的touch事件
  5. flowable 多实例动态添加人
  6. python3导入ssl报错_python3中pip3安装出错,找不到SSL的解决方式
  7. 从新手到高手 c++全方位学习_股票新手怎样快速入门?关于散户学习炒股的几点建议...
  8. Instance2:login interface
  9. 创业始于自信 成功缘于诚信
  10. atitit。全局变量的设计与实现 java php的异同
  11. 软件测试思维总结(1)-----比较思维:利用好可参照的资源
  12. 点餐小程序购物车效果实现
  13. 【剑指Offer】输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。
  14. 对于LabVIEW操作者框架的理解
  15. FCM和Threshold
  16. 名词变复数,动词第三人称, 过去式 读音
  17. 英雄无敌6服务器在哪个文件夹,win7系统英雄无敌6无法运行的解决方法
  18. android paint 线宽_Android绘图:绘制直线的 drawLine方法
  19. 什么是“大数据新闻”? 大数据
  20. 学系统集成项目管理工程师(中项)系列17a_范围管理(上)

热门文章

  1. VC++QQ群,刚刚建立,欢迎加入,共同进步
  2. 由置灰引出的css滤镜filter是什么东西?
  3. 微信公众平台开发实例(琴岛学院校园之声) PHP开发 代码挂载SAE平台(五)琴院Siri功能
  4. mac强制退出程序的几种方法
  5. Java面向对象基础呕心沥血三千字
  6. DVWA-XSS(Reflected) 全级别教程
  7. javascript按照期望的顺序给对象数组排序,中文汉字大写数字排序
  8. kafka动态权限认证(SASL SCRAM + ACL)
  9. 利用pygame模块设计一个植物大战僵尸游戏初版设定
  10. 19n20c的参数_常用场效应管参数