• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            life02

              C++博客 :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
              197 隨筆 :: 3 文章 :: 37 評(píng)論 :: 0 Trackbacks

            http://www.cnblogs.com/cgwolver/archive/2009/03/26/1257611.html
            假定在右手坐標(biāo)系中的三角形3點(diǎn)坐標(biāo)為A,B,C,判斷P是否在ABC之內(nèi)

            ( 主要來(lái)自 3D引擎研發(fā)QQ群(38224573 )的各位朋友的討論 ,我僅僅算做個(gè)總結(jié)吧,特別感謝各位朋友的熱情支持。 )

            方法1:三個(gè)Perplane的方法

                       設(shè)AB,BC,AC邊上的垂直平面為Perplane[3],垂直朝向內(nèi)側(cè)的法向?yàn)閚[3]

                      1)先根據(jù)任意兩邊叉出法向N

                           N = AB.CrossProduct(AC);

                           N.Normalize();

                           D = A.DotProduct( N );

                      2)如果P在三角形所在平面之外,可直接判定不在平面之內(nèi)( 假定方程為 ax+by+cz+d = 0 )

                           if( P.DotProduct( N ) + D > 0 ) return false;

                      3)然后法向和各邊叉出垂直平面的法向

                           n[0] = N.CrossProduct(AB); //朝向內(nèi)側(cè)

                           n[0].Normalize();

                           Perplane[0].dist = A.DotProduct(n[0]);

                           Perplane[0].normal = n[0];

                           同樣方法求得Perplane[1],Perlane[2];

                      3)因?yàn)槿齻€(gè)Perplane都朝向三角形內(nèi)側(cè),P在三角形內(nèi)的條件是同時(shí)在三個(gè)Perplane前面;如果給定點(diǎn)P在任意一個(gè)垂直平面之后,那么可判定P在三角形外部

                           for( int i = 0;i<3;j++ )

                           {

                                if( P.DotProduct( Perplane[i].normal ) + Perplane[i].dist < 0 )

                                     return false;

                           }

                           return true;//如果P沒(méi)有在任意一條邊的外面,可判斷定在三角形之內(nèi),當(dāng)然包括在邊上的情況

            方法2:三個(gè)部分面積與總面積相等的方法

                      S(PAB) + S(PAC) + S( PBC) = S(ABC) 則判定在三角形之內(nèi)

                      用矢量代數(shù)方法計(jì)算三角形的面積為

                           S = 1/2*|a|*|b|*sin(theta)

                              = 1/2*|a|*|b|*sqrt(1-cos^2(theta))

                              = 1/2*|a|*|b|*sqrt(1- (a.DotProduct(b)/(|a|*|b|))^2);

                           另一種計(jì)算面積的方法是 S = 1/2*|a.CrossProduct(b)|

                           比較一下,發(fā)現(xiàn)后者的精確度和效率都高于前者,因?yàn)榍罢咝枰_(kāi)方和求矢量長(zhǎng)度,矢量長(zhǎng)度相當(dāng)于一次點(diǎn)乘,三個(gè)點(diǎn)乘加一個(gè)開(kāi)方,顯然不如

                           后者一次叉乘加一次矢量長(zhǎng)度(注,一次叉乘計(jì)算相當(dāng)于2次點(diǎn)乘,一次矢量長(zhǎng)度計(jì)算相當(dāng)于一次點(diǎn)乘),后者又對(duì)又快。

                           

                           S(ABC)  = AB.CrossProduct(AC);//*0.5;

                           S(PAB)  = PA.CrossProduct(PB);//*0.5;

                           S(PBC)  = PB.CrossProduct(PC);//*0.5;

                           S(PAC)  = PC.CrossProduct(PA);//*0.5;

                           if( S(PAB) + S(PBC) + S(PAC) == S(ABC)  )

                                return true;

                           return false;

                     

                    另一種計(jì)算三角形面積的矢量方法是 1/2*a.CrossProdcuct(b) ,CrossProduct = ( y1*z2 - y2*z1 , x1*z2 - x2*z1, x1*y2 - x2*z1 )

                           可以看到CrossProduct 的計(jì)算要比DotProduct多3個(gè)乘法計(jì)算,效率沒(méi)有上面的方法高


            方法3:三個(gè)向量歸一化后相加為0

                    這個(gè)方法很怪異,發(fā)現(xiàn)自http://flipcode.spaces.live.com/blog/cns!8e578e7901a88369!903.entry 下面的一個(gè)回帖

                             
                          

                      如上圖三角形ABC,P為AB外側(cè)一點(diǎn),N1,N2,N3 分別為BP,AP,CP的歸一化矢量;NM為N1,N2夾角的角平分線

                      可以看出角A-P-B是三角形內(nèi)角,必然小于180度,那么角N1-P-N2等于A-P-B;NM是N1-P-N2的角平分線,那么角B-P-N等于角N-P-A,而CPN必然小于其中一個(gè),

                      即小于180/2 = 90度。結(jié)論是角N1,N2的合矢量方向與N3的夾角為銳角。所以N1,N2,N3的合向量模大于1.

                      這里注意,N3不一定在N1,N2之間,不能假定N2-P-N3 和N3-P-N1這兩個(gè)角一定是銳角

                      同樣可以推導(dǎo)出如果P在三角形內(nèi),N1+N2+N3必然小于0;若N1+N2+N3 = 0則P在三角形的邊上。

                      有沒(méi)有更簡(jiǎn)單的推導(dǎo)方法?

                     

                      這個(gè)方法看起來(lái)很精巧,但是善于優(yōu)化的朋友會(huì)立刻發(fā)現(xiàn),三個(gè)矢量歸一化,需要三個(gè)開(kāi)方。迭代式開(kāi)方太慢了,而快速開(kāi)方有的時(shí)候又不滿足精度要求。

                             

             方法4:重心坐標(biāo)之和為1

                     {

                           BaryCenter = ( S(PAB)/S(PABC),S(PBC)/S(PABC),S(PAC)/S(PABC)) // 點(diǎn)P在三角形內(nèi)的重心坐標(biāo)

                     

                           if( BaryCenter.x + BaryCenter.y + BaryCenter.z >0.f )

                                return false

                           return true;

                      }

                      其中S(PAB),S(ABC),S(PBC),S(PBC) 用上述的方法二種提到的計(jì)算三角形面積方法計(jì)算。

            綜合比較

                 方法1必須求叉乘,雖然可以通過(guò)首先排除不在平面內(nèi)的點(diǎn),但是后面仍要求三個(gè)叉乘和3個(gè)點(diǎn)乘(當(dāng)然還可排除法優(yōu)化)

                 方法2看起來(lái)之需要求4個(gè)點(diǎn)乘,如果用叉乘方法計(jì)算面積,可能會(huì)導(dǎo)致效率低下

                 方法3是看起來(lái)是最精巧的方法,但是效率也不能保證...3個(gè)開(kāi)方

                 方法4和方法2的效率差不多

             

            本文來(lái)自CSDN博客,轉(zhuǎn)載請(qǐng)標(biāo)明出處:http://blog.csdn.net/boyzk2008/archive/2009/08/07/4421106.aspx

            posted on 2009-09-25 10:22 life02 閱讀(1623) 評(píng)論(8)  編輯 收藏 引用 所屬分類(lèi): 游戲開(kāi)發(fā)

            評(píng)論

            # re: 如何判斷一點(diǎn)在三角形內(nèi)(轉(zhuǎn)) 2009-10-16 02:54 路人
            有一個(gè)經(jīng)典的封閉多邊形判斷是否內(nèi)部的方法,
            從該點(diǎn)引一條射線出來(lái), 與多邊形交點(diǎn)為奇數(shù)的為內(nèi)部, 偶數(shù)外部  回復(fù)  更多評(píng)論
              

            # re: 如何判斷一點(diǎn)在三角形內(nèi)(轉(zhuǎn)) 2009-10-16 14:29 溪流
            @路人
            這個(gè)說(shuō)法好不嚴(yán)謹(jǐn),這條射線與多邊形頂點(diǎn)相交、與邊重合的情況  回復(fù)  更多評(píng)論
              

            # re: 如何判斷一點(diǎn)在三角形內(nèi)(轉(zhuǎn)) 2009-10-16 23:30 路人
            當(dāng)然前面提出的射線法需要預(yù)先排除一些內(nèi)容, 這在計(jì)算交點(diǎn)的過(guò)程中可以巧妙達(dá)到:)

            因?yàn)槿切尾豢赡苁前级噙呅危?
            一般取三角形頂點(diǎn)到該點(diǎn)的方向做為射線方向,這樣交點(diǎn)如包括自己就表明該點(diǎn)在邊上,0個(gè)交點(diǎn), 在外部, 1個(gè)交點(diǎn), 在內(nèi)部   回復(fù)  更多評(píng)論
              

            # re: 如何判斷一點(diǎn)在三角形內(nèi)(轉(zhuǎn)) 2010-07-06 00:27 kita
            引用" if( P.DotProduct( N ) + D > 0 ) return false;

            這個(gè)應(yīng)該是 Dot(P,N) - D == 0 吧



            引用" n[0] = N.CrossProduct(AB); //朝向內(nèi)側(cè)

            這個(gè)得出來(lái)的成績(jī)應(yīng)該是外側(cè)吧!   回復(fù)  更多評(píng)論
              

            # 繼續(xù)研究下去發(fā)現(xiàn)更多問(wèn)題!!! 2010-07-06 00:53 kita
            S(ABC) = AB.CrossProduct(AC);//*0.5;

            這樣算是錯(cuò)的


            應(yīng)該是 S_ABC = |Coss(AB,AC)| 絕對(duì)值不能少
            也就是 S_ABC = Coss(AB,AC).Length(); //C#

            這樣算程序才得出正確結(jié)果.
              回復(fù)  更多評(píng)論
              

            # re: 如何判斷一點(diǎn)在三角形內(nèi)(轉(zhuǎn)) 2010-07-06 00:54 kita
            private Boolean InTriangleByS(Vector3 A, Vector3 B, Vector3 C, Vector3 P)
            {
            Vector3 AB = A - B;
            Vector3 CA = C - A;

            Vector3 PA = P - A;
            Vector3 PB = P - B;
            Vector3 PC = P - C;

            float SABC = Vector3.Cross(AB, CA).Length();//*0.5;

            float SPAB = Vector3.Cross(PA, PB).Length();//*0.5;

            float SPBC = Vector3.Cross(PB, PC).Length();//*0.5;

            float SPAC = Vector3.Cross(PC, PA).Length();//*0.5;

            if (SPAB + SPBC + SPAC == SABC)
            return true;

            return false;
            }

            private Boolean InTriangleByPlane(Vector3 A, Vector3 B, Vector3 C, Vector3 P)
            {
            Vector3 ab = A - B;
            Vector3 ca = C - A;
            Vector3 bc = B - C;

            Vector3 N = Vector3.Cross(ab, bc);
            N.Normalize();

            float D = Vector3.Dot(A, N);
            float D_PN = Vector3.Dot(P, N);


            if (Math.Round(D_PN - D, 2) != 0)
            {
            return false;
            }

            Vector3[] p_n = new Vector3[3];

            p_n[0] = Vector3.Cross(N, ab);
            //p_n[0].Normalize();

            p_n[1] = Vector3.Cross(N, bc);
            //p_n[1].Normalize();

            p_n[2] = Vector3.Cross(N, ca);
            //p_n[2].Normalize();

            Plane[] p = new Plane[3];

            p[0].D = Vector3.Dot(A, p_n[0]);
            //p[0].Normal = p_n[0];

            p[1].D = Vector3.Dot(B, p_n[1]);
            //p[1].Normal = p_n[1];

            p[2].D = Vector3.Dot(C, p_n[2]);
            //p[2].Normal = p_n[2];

            for (int i = 0; i < 3; i++)
            {
            float D_PpN = Vector3.Dot(P, p[i].Normal);
            if (D_PpN - p[i].D > 0)
            {
            return false;
            }
            }

            return true;
            }  回復(fù)  更多評(píng)論
              

            # re: 如何判斷一點(diǎn)在三角形內(nèi)(轉(zhuǎn)) 2010-07-06 01:03 kita
            關(guān)于效率 各執(zhí)行 10000次

            適用Plane 的求法使用時(shí)間是 面積的 1/2   回復(fù)  更多評(píng)論
              

            # re: 如何判斷一點(diǎn)在三角形內(nèi)(轉(zhuǎn)) 2010-07-06 01:35 kita
            引用 " 同樣可以推導(dǎo)出如果P在三角形內(nèi),N1+N2+N3必然小于0;若N1+N2+N3 = 0則P在三角形的邊上。

            按原圖
            假設(shè) P 在 三角形邊上

            則 N1 + N2 = (0,0,0)

            N3 的長(zhǎng)度為 1 (歸一化向量長(zhǎng)度必為1 )

            N1 + N2 + N3 怎么加也不可能得出 0 , 必然是 長(zhǎng)度為1 的向量。

            因此 N1+N2+N3 少于 1 為在三角內(nèi) 大于1 為三角外。

            但此方法的確是最慢的  回復(fù)  更多評(píng)論
              

            性做久久久久久久久| 久久久久久久亚洲Av无码| 久久性精品| 一本色道久久88—综合亚洲精品| 久久久久久久久久久久中文字幕| 久久精品国产精品青草 | 亚洲香蕉网久久综合影视 | 久久天天躁狠狠躁夜夜网站 | 少妇无套内谢久久久久| 久久精品国产亚洲AV高清热| 久久精品无码一区二区app| 亚洲精品无码久久久久去q| 久久男人中文字幕资源站| 久久午夜羞羞影院免费观看| 天堂无码久久综合东京热| 久久精品国内一区二区三区| 99精品国产99久久久久久97 | 九九99精品久久久久久| 色天使久久综合网天天| 国产高清美女一级a毛片久久w| 亚洲中文字幕无码一久久区| 国产精品日韩深夜福利久久| 久久久久人妻一区精品色| 狠狠色丁香久久婷婷综合| 久久久青草青青国产亚洲免观| 久久99中文字幕久久| 蜜臀av性久久久久蜜臀aⅴ| 久久综合九色综合网站| 合区精品久久久中文字幕一区| 国内精品久久久久久久影视麻豆| 精品久久久久久国产91| 国产综合久久久久久鬼色| 熟妇人妻久久中文字幕| 久久亚洲AV成人无码| 久久乐国产综合亚洲精品| 久久综合五月丁香久久激情| 久久99精品久久久久久野外 | 日韩人妻无码一区二区三区久久 | 久久国产精品-国产精品| 国产人久久人人人人爽| 国产美女久久久|