凸多邊形碰撞檢測的分離軸算法(SAT)


  碰撞檢測可分為 Broad Phase (粗略檢測)與 Narrow Phase (精細檢測) 兩個階段。粗略檢測階段可直接比較兩個物體的AABB包圍框是否碰撞以節省計算量和時間。精細檢測中,SAT(Separating Axis Theorem,分離軸定理)碰撞檢測算法直觀且高效,它的原理清晰易懂,即若兩個物體沒有發生碰撞,則總會存在一條直線,能將兩個物體分離。分離軸適用的是凸多邊形之間的檢測,不適用於凹多邊形,凹多邊形的檢測,可以通過算法將凹多邊形分割成多個凸多邊形再進行計算。

  算法步驟如下:

  步驟一:從需要檢測的多邊形中取出一條邊,並找出它的法向量,這個向量將會是我們的一個“投影軸”。

  步驟二:循環獲取第一個多邊形的每個點,並將它們投影到這個軸上。

  步驟三:對第二個多邊形做同樣的處理。

  步驟四:分別得到這兩個多邊形的投影,並檢測這兩段投影是否重疊。

  如果你發現了這兩個投影到軸上的“陰影”有間隙,那么這兩個圖形一定沒有相交。但如果沒有間隙,那么它們則可能接觸,你需要繼續檢測直到把兩個多邊形的每條邊都檢測完。如果你檢測完每條邊后,都沒有發現任何間隙,那么它們是相互碰撞的。

 


  根據上面步驟兩個凸多邊形之間碰撞檢測的關鍵代碼如下(參考collision.py):

 1 def flatten_points_on(points, normal, result):
 2     minpoint = math.inf
 3     maxpoint = -math.inf
 4 
 5     for i in range(len(points)):
 6         dot = points[i].dot(normal)
 7         if dot < minpoint:
 8             minpoint = dot
 9         if dot > maxpoint:
10             maxpoint = dot
11 
12     result[0] = minpoint
13     result[1] = maxpoint
14 
15 
16 def is_separating_axis(a_pos, b_pos, a_points, b_points, axis):
17     range_a = [0, 0]
18     range_b = [0, 0]
19 
20     offset_v = b_pos-a_pos
21 
22     projected_offset = offset_v.dot(axis)
23 
24     flatten_points_on(a_points, axis, range_a)
25     flatten_points_on(b_points, axis, range_b)
26 
27     range_b[0] += projected_offset
28     range_b[1] += projected_offset
29 
30     if range_a[0] > range_b[1] or range_b[0] > range_a[1]:
31         return True
32 
33     return False
34 
35 
36 def test_aabb(b1,b2):
37     return b1[0][0] <= b2[1][0] and b2[0][0] <= b1[1][0] and b1[0][1] <= b2[2][1] and b2[0][1] <= b1[2][1]
38 
39 
40 def test_poly_poly(a, b):
41     a_points = a.rel_points
42     b_points = b.rel_points
43     a_pos = a.pos
44     b_pos = b.pos
45 
46     for n in a.normals:
47         if is_separating_axis(a_pos, b_pos, a_points, b_points, n):
48             return False
49 
50     for n in b.normals:
51         if is_separating_axis(a_pos, b_pos, a_points, b_points, n):
52             return False
53 
54     return True
55 
56 
57 def collide(a, b):
58     if not test_aabb(a.aabb, b.aabb):
59         return False
60 
61     return test_poly_poly(a, b)

   對於參數指定的兩個凸多邊形,碰撞檢測函數collide先調用test_aabb來判斷其AABB包圍框是否相交,若外包圍框不相交則證明兩個多邊形之間不可能發生碰撞,函數值返回False,反之則使用分離軸算法進行精細檢測。函數test_poly_poly按照算法流程遍歷兩個多邊形上的每一條邊,若在某一條邊上存在分離軸使得兩個多邊形在其上的投影不重合則證明多邊形不相交,直接返回False。函數is_separating_axis根據給定的兩個多邊形的位置、多邊形頂點的局部坐標以及分離軸向量,來計算多邊形在分離軸向量上投影是否重合。

  需要注意的是:對於很多圖形視圖和動畫框架來說,圖形項一般使用自己的局部坐標系來繪制,通常以其中心為原點,圖形項的位置是其原點在其父圖形項或者場景中的位置。分離軸判斷函數中的坐標基准應一致,假如以第一個輸入參數多邊形$a$的位置為參考原點,則多邊形$b$的頂點坐標在該參考系下,其局部坐標要加上$\overrightarrow{ab}$這個offset,因此多邊形$b$在向分離軸投影時也要加上$\overrightarrow{ab}$投影后的偏移量$ \overrightarrow{ab} \cdot \overrightarrow{n}$

 

 

 

參考:

collision.py

Hyperplane separation theorem

SAT (Separating Axis Theorem)

碰撞檢測之分離軸定理算法講解

2D凸多邊形碰撞檢測算法(一) - SAT


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM