navmesh 生成網格信息 總 (更新中 )

The general process is as follows:

Voxelization - Create a solid heightfield from the source geometry.

體素化,根據幾何體創建solid 高度場

 

Generate Regions - Detect the top surface area of the solid heightfield and divide it up into regions of contiguous spans.

生成區域,決定哪一部分表面是可通過的。然後把這些可通過區域劃分爲contiguous regions of spans,最終可以用它們形成凸多邊形。

 

第一步,把solid heightfiled轉成open heightfield,也就是表示上表面開闊區域的heightfiled

創建Neighbor Links

比如上面這些,桌子下面這些,在生成solid heightfiled的過程就已經被排除了,因爲一個體素中有東西被認爲是solid的。

然而在這個時刻桌子上面還是被認爲是可行走的。

第二步,進一步排除 un-walkable spans ,排除條件:1 距離阻擋夠遠 2 上面能走人而不會碰撞到物體

 

爲所有的open spans會產生鄰居信息,算法:

所有鄰居columns 爲候選對象,只考慮四個方向的,而不考慮diagonal方向的,滿足以下兩條的span會被認爲是neibour span:

1 兩個spanfloor的高度差夠小

2 agent通過的時候不會撞到頭

如上圖,我們每一個獨立的span是能放下一個人的,但是在鄰居間移動的過程中沒辦法保證gap足夠大。

 

生成distance field

distance field是什麼:包含每個span距離最近的border span的距離。在生成regions的過程中這個算法會用到。

Border span:沒有8neighborspan。是用來表示原始geometry的可行走表面與阻擋或者empty space之間的boundary

 

 

生成regions

Regions的很重要一點是他們在映射到xz平面的時候會是簡單多邊形。

初始的生成region的算法:分水嶺算法。

分水嶺算法會從最低點的span開始慢慢地開始“floods”,進行迭代,每次迭代有一個當前的水平線,當前水平線以下的spans會被增加到現有的regions或者創建出新的regions。在region擴張的過程中如果一個被flood到的span臨接一個已經存在的region,那麼它會被加入這個region

然後引入一些算法:

去掉非常小的regions。(以及 CleanNullRegionBorders 

非常小的region要合併。

有時候分水嶺算法會導致一個region圍繞着一個null region進行flow,這個時候這個region會在這個null region的地方分開。

 

在這個過程正還會出現的問題如圖:

Null region的邊上的span可能會出問題:比如上面的一個region只圍繞着一個span,那麼在後續的生成輪廓的過程中可能會生成自交的polygon。算法會將其變成深藍色的。

 

Generate Contours - Detect the contours of the regions and form them into simple polygons.

生成輪廓,contours of the regions “走”出來的。這是從體素空間到向量空間的第一步,但是它仍然是以voxel space的單位來表示的。

這個部分的輸入是open heightfield,是從關注span的表面到關注span的邊的一個轉換。

兩種edges: region and internal. 

Region edges:邊的兩側是不同的region。

區分這兩種edges的算法:

遍歷所有spans,檢查每一個的軸上的鄰居,如果一個軸的鄰居與當前span的region不一樣,則被標記爲region edge。

 

接下來“走”出輪廓來~

Start by facing the known region edge. Add it to the contour.

首先面向一個已知的region edge,加入contour。

Large

Rotate in 90 degree increments, adding region edges to the contour, until we find an internal edge. Step forward into the neighbor span.

順時針旋轉90度,直到找到internal edge,進入下一個span。

Large

Rotate 90 degrees counter-clockwise and re-perform the previous step. Continue until we return to the starting span, facing the starting direction.

逆時針旋轉90度並重復前一個step。直到回到了第一個span,並且面向開始的方向。

 

接下來把邊信息轉成頂點信息。

首先確認x,z值,對於每一個邊,我們沿着從span內部來看,順時針的防線,獲取邊上的點的x,z值,如下:

確認y軸的值比較難,這也正是爲什麼我們返回3D空間。

上圖中,不知道選哪個點的y軸的值,我們用的是最高的!!

 

Note that there are two types of contour sections. Sections that represent the portal between two adjoining regions, and sections that border on "empty" space. In the code documentation empty space is referred to as the "null region". I will use the same term here.

 

有兩種輪廓的片段,一種是連接兩個regions,稱作portal,另一種是連接null region。

真的需要下圖這麼多的頂點嗎?不是的,真正必要的頂點是:region發生變化的地方,比如region portals的頂點。

所以可以進行如下簡化:

但是這樣切掉的部分就太大了,與原來的輪廓不一致,所以會定義一個MaxDeviation來確保誤差不會太大,Douglas-Peucker算法用來做到這一點:

It starts off with the mandatory vertices, then adds vertices back such that none of the original vertices are farther than edgeMaxDeviation distance from the simplified edges.

從一個必須的頂點開始增加頂點直到所有的頂點到簡化後的邊的距離都不超過MaxDeviation。

但是這個算法會生成很長的線段,最終會在生成網格的過程中生成非常長而細的三角形。

所以會使用第二個算法,設定最長的邊長,將超過長度的邊切成兩半,直到沒有超過最長邊長的邊。

到了這一步雖然還是在體素空間,但是已經爲返回vector space做好了準備。

 

 

regions生成細節多邊形(highly detailed polygons

其次,用一些算法來實現:

1 簡化兩個多邊形之間的邊(regions之間的portals

2 確保border 符合可行走表面的border???

Boarder就是連接空的或者阻擋區域的輪廓的邊

3 優化border邊的長度

 

到了這個階段,可行走表面被表示爲簡單多邊形

 

簡單多邊形可定義爲:

1.周界不自交的多邊形。

https://gss2.bdstatic.com/-fo3dSag_xI4khGkpoWK1HF6hhy/baike/s%3D250/sign=c741ad68a3014c081d3b2fa03a7a025b/8644ebf81a4c510ff79cc2db6a59252dd52aa592.jpg


  2.滿足條件:
  1)頂點與頂點不重合。
  2)頂點不在邊上。
  3)邊與邊不相交的多邊形。

 

 

Generate Polygon Mesh - Sub-divide the contours into convex polygons.

生成多邊形網格,把輪廓劃分成凸多邊形。

這個過程是先切分簡單多邊形爲三角形,然後把三角形合成爲儘可能打的凸多邊形。

 

這個大步驟要做的事情:

1 voxel space:座標是整數格式的並且代表的是到heightfield的原點的距離。

所以我們要把多邊形的頂點信息轉成原始地圖的vector space的座標。

2 把所有輪廓信息合成一個單個的mesh信息

3 把多邊形切成凸多邊形

4 計算出多邊形的鄰居信息(Polygon neighbor information.)

 

切分爲凸多邊形的算法:

  1. Triangulate each contour.給每個多邊形三角化
  2. Merge triangles to form the largest possible convex polygons.和成爲最大的可能的凸多邊形

 

三角化:

沿着輪廓的邊走,對於每三個頂點,看是否可以組成內部的三角形。對於所有的候選者選擇最短的邊。新的邊被稱作切分邊(partitions)。一直進行這個過程知道三角化完成。三角化是在輪廓的xz平面投影上完成的。

 

Step-by-Step: Find potential partitions:

接下來找到最短的分割並且生成三角形,去掉無效分割(https://blog.csdn.net/icebergliu1234/article/details/87933454),生成新的potential partitions:

 

一直這樣直到三角形化完畢。

 

合併到凸多邊形:

只在一個輪廓所生成的多邊形合併。

流程:

  1. 找到可以合併的多邊形。
  2. 從這個列表中找到兩個多邊形,共同的邊是最長的,並且合併它們。
  3. 重複直到沒有可以再合併的了。

可以合併的多邊形滿足:

  • 貢獻一條邊。
  • 合併之後仍然是凸多邊形。
  • 合併之後的多邊形邊數不會大於最大頂點數(maxVertsPerPoly )。

 

It is best to use visualizations. The first example demonstrates a valid merge.

檢測合併之後的多邊形是否是凸的:關鍵點在於共享的頂點。如果兩個頂點都可以組成內部的銳角,則合成之後的多邊形就仍然是凸多邊形。我們如何判斷這一點呢:

1 創建一條從當前頂點的前一個頂點到後一個頂點的向量。

2 如果頂點A在向量的左邊則形成的是銳角。

 

最後一步就是遍歷整個mesh的所有多邊形並且創建連接信息。

到了這一步我們終於回到了向量空間,並且擁有了一個凸多邊形網格。

 

 

Generate Detailed Mesh - Triangulate the polygon mesh and add height detail.

生成細節網格,把多邊形網格三角化,並且添加高度細節。

這個三角化的過程是 Delaunay triangulation

多邊形的邊的裏面會被添加頂點,以保證原始的geometry會被足夠好地近似。

如下圖:

 

在三維空間中,多邊形網格可能沒有足夠地近似原始mesh的輪廓(比如上圖樓梯在Y軸上面近似的高度與實際高度相差過大)。所以就有了這個步驟。這個步驟增加了高度細節,這樣細節網格就可以很好地在所有的軸上面匹配原始mesh的表面。

爲了完成這一點,我們遍歷所有的多邊形並且當多邊形偏離原始mesh超過一定值的時候,沿着多邊形的邊增加頂點。

 

從尋路的角度來講,這一步是不必要的,有上一步創建的凸多邊形網格就夠了。這個步驟的必要性在於當需要一個agent被放置在源網格表面是用到物理或者ray-casting的時候。當然了體素化過程決定了精確的位置是不可能的。而且過於精確的高度信息會大大增加查詢性能已經內存佔用。

 

 

 

 

 

=============================================================================================

相關概念

下圖中幾何體被 保守體素化 抽象爲heightfield 表示阻擋空間。

1 Heightfield:可以理解成是一個數據結構,由所有的solid的HeightSpan構成。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

2 HeightSpan

 

這就是一個HeightSpan,簡稱Span

 

3 Open height spans

因爲我們更關注solid space的上表面,我們用Open height span來表示這些上表面的開闊區域,圖中黃色部分:

 

solid heightfiled 轉成open heightfield的算法:

遍歷所有的solid spans,如果這個span被標記爲traversable,則看這個column上的下一個span的最小值和當前span的最大值之間的高度差就可以定義一個solid span

 

 

 

 

 

 

 

 

 

Neighbour的表示:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章