幾種方法判斷平面點在三角形內


最近在做一個Unity實現的3D建模軟件,其中需要在模型表面進行操作的時候,需要用到點和三角形位置關系的判定算法。由於一個模型往往是幾千個三角片,所以這個判定算法必須高效,否則會影響最終程序的整體性能。這里記錄一下一些算法,如有誤請指出,謝謝!

首先假設點和三角形在同一平面內,如果不在同一平面,需要用其它方法先篩選。

常用的幾種平面點-三角形位置關系判定方法有(以下算法執行必須先保證點和三角形位於同平面)

1.順時針/逆時針判定法

該方法要求點的順序是順時針或逆時針的,如果是順時針的點,沿着3條邊走,如果目標點P在三角形內,那么P始終在邊的右側。

同理,如果是逆時針的話,目標點p應該始終在邊的左側。

例:逆時針的三個點abc,判斷abXap , bcXbp , caXcp,如果這三個向量叉積的Z值都同向,並且都為負的話(左手系),說明p點在三角形內部。

向量叉積較為簡單,這里不再贅述。

2.面積法

已知三角形的三點坐標,由海倫公式可以直接得到面積,只需要比較Spab+Spbc+Spca和Sabc的大小關系即可。但是由於面積公式涉及開平方根,會產生浮點數的精度問題。

3.角度法

同面積法類似,如果點在三角形內,則pa,pb,pc與ab,bc,ca,形成6個角,這6個角的和應該為180度。同樣由於求角度涉及三角函數運算,效率較慢,並且有浮點數的精度問題,所以這個方法一般是不被采用的。

4.斜坐標系法

這個方法正是我主要想介紹的方法,思路來源於網上,我整理了一下,感覺效率應該是這幾種方法里最快的了。

首先上圖:

首先以AC、AB為i、j基向量,建立斜坐標系。首先得知道斜坐標系有兩個性質:

1)平面向量中的結論在斜坐標系中成立。

2)直線方程求法和直角坐標系相同。

其中2)可以用直線的幾何性質在斜坐標系中推導得到,具體方法和直角坐標系中推導直線方程類似,只不過多了個斜坐標系的夾角三角函數而已。

 

下面記AB、AP、AC、i、j均為向量,並且i==AC,j==AB,是斜坐標系的基向量。

且BC的直線方程為i+j=1;

設P坐標為(p_i,p_j),若要P點在三角形內,必滿足  1) p_i>=0,  2) p_j>=0,  3)p_i+p_j<=1。

這里可以用向量表示為: AP=p_i*AC+p_j*AB

左右兩邊同乘AC、AB。可以得到兩個方程。

AP•AC=p_i*(AC•AC)+p_j(AB•AC)    -----1

AP•AB=p_i*(AC•AB)+p_j*(AB•AB)  -----2

 由這兩個方程可以得到:

p_i=((AP•AC)*(AB•AB)-(AP•AB)*(AC•AB))/((AC•AC)*(AB•AB)-(AC•AB)*(AC•AB));

p_j=((AP•AB)*(AC•AC)-(AP•AC)*(AB•AC))/((AC•AC)*(AB•AB)-(AC•AB)*(AC•AB));

--------------------------------------------------

至此已經能夠判斷點是否在三角形內,下面可以借助柯西不等式,避免作浮點數除法,改為減法。

由柯西不等式的向量形式可以得到:

(AC•AC)*(AB•AB)>=(AC•AB)*(AC•AB)恆成立,所以分母不會為負,我們只需要判斷分子的正負即可。

即,判斷p_i>=0 , p_j>=0,等價於:

(AP•AC)*(AB•AB)-(AP•AB)*(AC•AB)>=0  ,(AP•AB)*(AC•AC)-(AP•AC)*(AB•AC)>=0,

並且p_i+p_j<=1,可以兩邊同乘以分母,避免了浮點數的除法運算,即如下式:

((AP•AC)*(AB•AB)-(AP•AB)*(AC•AB))+((AP•AB)*(AC•AC)-(AP•AC)*(AB•AC))-((AC•AC)*(AB•AB)-(AC•AB)*(AC•AB))<=0

 

代碼如下:

 1     static public bool pointInTriangle(Vector3 p,Vector3 a,Vector3 b,Vector3 c)
 2     {
 3         if(pointInTrianglePlane(p,a,b,c)==false)
 4             return false;
 5         Vector3 AC=c-a;
 6         Vector3 AB=b-a;
 7         Vector3 AP=p-a;
 8 
 9         float f_i=Vector3.Dot(AP,AC)*Vector3.Dot(AB,AB)-Vector3.Dot(AP,AB)*Vector3.Dot(AC,AB);
10         float f_j=Vector3.Dot(AP,AB)*Vector3.Dot(AC,AC)-Vector3.Dot(AP,AC)*Vector3.Dot(AB,AC);
11         float f_d=Vector3.Dot(AC,AC)*Vector3.Dot(AB,AB)-Vector3.Dot(AC,AB)*Vector3.Dot(AC,AB);
12         if(f_d<0) Debug.Log("erro f_d<0");
13         //p_i==f_i/f_d
14         //p_j==f_j/f_d
15         if(f_i>=0 && f_j>=0 && f_i+f_j-f_d<=0)
16             return true;
17         else
18             return false;
19     }

 附上一個判斷四點共面的算法,這里四個點不能有浮點精度誤差,,否則算法不成立。

 1     static public bool pointInTrianglePlane(Vector3 p,Vector3 a,Vector3 b,Vector3 c)
 2     {
 3         Vector3 pa=a-p;
 4         Vector3 pb=b-p;
 5         Vector3 pc=c-p;
 6 
 7         Vector3 normal1=Vector3.Cross(pa,pb);
 8         Vector3 normal2=Vector3.Cross(pa,pc);
 9         Vector3 result=Vector3.Cross(normal1,normal2);
10         //證明:若pab平面的法向量平行於pac平面的法向量,則說明平面pab和pac平行或重合,
11         //且p點為兩平面公共點,所以pab、pac平面重合,pabc四點共面。
12         if(result==Vector3.zero)
13         {
14             Debug.Log(result);
15             return true;
16         }
17         else
18             return false;
19     }

 

 


免責聲明!

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



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