Ray AABB交叉檢測演算法

2022-07-04 20:36:10 字數 3271 閱讀 4578

最近在解決三維問題時,需要判斷線段是否與立方體交叉,這個問題可以引申為:射線是否穿過立方體aabb。

在3d遊戲開發中碰撞檢測普遍採用的演算法是軸對齊矩形邊界框(axially aligned bounding box, aabb)包裝盒方法,其基本思想是用乙個立方體或者球體完全包裹住3d物體物件,然後根據包裝盒的距離、位置等相關資訊來計算是否發生碰撞。

本文接下來主要討論射線與aabb的關係,主要對box2d碰撞檢測使用的slab的碰撞檢測演算法(slabs method)進行介紹,然後使用python語言實現slab碰撞檢測方法,該方法可以用於3d物體拾取等應用場景。

slab英文翻譯是「平板」,本文是指兩個平行平面/直線之間的空間。在2d空間中slab可以理解為平行於座標軸的兩條直線間的區域,3d空間中為平行於xy平面(或者yz面,xz面)的兩個平面之間的區域。由此,我們可以把3d空間中的aabb盒子看做是由aabb的3組平行面形成的3個方向的slab的交集。

另外,引入候選面的概念:在3d空間中,我們先確定正對著射線的三個面,也就是說,我們可以通過某種方式將aabb相對於射線ray的背面給忽略掉,從而確定三個候選的面。這三個候選的面,就是有可能和射線ray發生交叉的最近的面。

根據這個定義,我們可以得到以下三個結論:

性質一:如果乙個點在aabb中,那麼這個點必定同時在這3個slab中。

性質二:如果一條射線和aabb相交,那麼這條射線和3個slab的相交部分必定有重合部分。

性質三:當射線與這三個候選麵中的乙個發生交叉之後,射線ray的原點到這個面的距離要比到其他幾個面的距離要長。

性質一和性質二比較容易理解,如果射線和3個slab的相交線段沒有重合,那麼這些線段就不可能同時存在於3個slab中,也就不可能在aabb盒子中。

為了方便理解性質三,使用2d圖形來講解:

在上圖中,我們的射線在右下角,向左上角發射,射線經過乙個a點,其中候選面是y1面和x2面。

根據上述性質,可以看到a點同時在2d空間中的2個slab中;此外,根據性質二,因為射線與平面相交,那麼這條射線與slab的相交部分必有重合部分,因為a點在射線上,且在平面中,那麼可以得到max(t1,t2)<=ta<=min(t3,t4);根據性質三:當交叉後,可以看出t2>t1。

同理,我們可以把上述的驗證過程推廣到三維中。在三維空間中,假設射線到3個候選面的距離分別是t1、t2、t3,到候選面對應的面的距離分別為t4、t5、t6,那麼根據性質二,射線與aabb碰撞的條件是max(t1,t2,t3)<=min(t4,t5,t6);如果發生交叉,那麼根據性質三,射線到最近的交叉面的距離是是max(t1,t2,t3)。

在上述性質基礎上,確定射線與aabb是否交叉需要三步驟:

如何確定候選面:只要將平面方程帶入射線ray的方程,求出這兩個平面的t值,然後t值較小的那個自然先與射線交叉,那麼就表示它是乙個候選面。射線可以用引數方程表示為r(t) = p0 + t·d, (其中p0為射線起點,d為射線的方向向量)

如何確定候選面的方程。平面由隱式定義方程x·n=d, (其中x為平面上的點,n為平面法向量,d為原點到平面的距離)給出。由於aabb的slab平面都分別和兩個座標軸平行,它的面的法線總是有兩個分量是0,而另外乙個分量總是為1,所以我們一致使用某個軸分量為1的法線。如果上面的方程表示的是aabb盒的左面的面,那麼公式中的n表示的就是(1,0,0),但上面的公式表示的是aabb盒的右邊的面的時候,n表示的值依然是(1,0,0)。

如何對交叉點是否在aabb盒上進行判斷。根據性質二判斷,即射線與aabb碰撞的條件是max(t1,t2,t3)<=min(t4,t5,t6)。

求取t值的公式推導如下:

最後,附上我的python**片段,**實時更新於github

# ray-aabb方法 相交返回true,否則返回false

# tdpoint = collections.namedtuple("tdpoint", ["x", "y","z"])

# aabb有最大和最小點組成,資料結構為{max=tdpoint,min=tdpoint}

# ray由原點和方向組成,其中方向向量為1,資料結構為{tdpoint,tdpoint}

def intersectwithaabb(aabb,ray):

tmin=0

tmax=10000

# if(math.fabs(ray[1].x)<0.000001):

if (ray[0].xaabb.max.x):

return false

else:

ood=1.0/ray[1].x

t1=(aabb.min.x-ray[0].x)*ood

t2=(aabb.max.x-ray[0].x)*ood

# t1做候選平面,t2做遠平面

if (t1>t2):

temp=t1

t1=t2

t2=temp

if t1>tmin:

tmin=t1

if t2tmax:

return false

# # if(math.fabs(ray[1].y)<0.000001):

if (ray[0].yaabb.max.y):

return false

else:

ood=1.0/ray[1].y

t1=(aabb.min.y-ray[0].y)*ood

t2=(aabb.max.y-ray[0].y)*ood

# t1做候選平面,t2做遠平面

if (t1>t2):

temp=t1

t1=t2

t2=temp

if t1>tmin:

tmin=t1

if t2tmax:

return false

# # if(math.fabs(ray[1].z)<0.000001):

if (ray[0].zaabb.max.z):

return false

else:

ood=1.0/ray[1].z

t1=(aabb.min.z-ray[0].z)*ood

t2=(aabb.max.z-ray[0].z)*ood

# t1做候選平面,t2做遠平面

if (t1>t2):

temp=t1

t1=t2

t2=temp

if t1>tmin:

tmin=t1

if t2tmax:

return false

# return true

from 3d空間中射線與軸向包圍盒aabb的交叉檢測演算法

from box2d 射線和aabb的碰撞檢測

日期不能交叉的檢測演算法

select top1 from demotab where start time start time and end time end time or 判斷新日期段與原日期段是否存在外包含 start time start time and end time end time or 判斷新日期段...

VIBE檢測演算法

一 vibe 獲取目標 其他演算法處理 最終目標 優點 記憶體占用少,處理速度快,計算量小,檢測效果好 無引數法 可直接應用在產品中,軟硬體相容性好 效能優於混合高斯,引數化方法,sacon等 背景模型及時初始化 具有較好的抗噪能力。缺點 ghost區域 挑戰 必須適應環境的變化 比如光照的變化造成...

素數檢測演算法

因為1既不是素數也不是合數,所以下面的實現 中不考慮小於2的情況。本文以c語言進行講解,建議對著完整的原始碼看。最原始 最粗暴的方法就是從頭到尾逐個進行檢測,一旦遇到可被整除的數馬上返回false bool is prime 1 int n return true 該演算法時間複雜度為n2 n 2對...