日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

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

發布時間:2023/12/15 编程问答 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 三维空间的三角剖分( 3D Delaunay Triangulated graph)第二部分:剖分三维空间 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

對三維空間進行三角剖分的大體邏輯思路和流程同二維空間的三角剖分是一樣的,二維的代碼實踐過程可以前往本專欄的第一篇文章:二維 二維空間的三角剖分。

在理清楚二維平面下三角剖分的思路之后,我們嘗試將其中的一些概念升級一個維度。

我們先來分析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)第二部分:剖分三维空间的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。