1. 程式人生 > >向量叉乘求三維空間中兩直線(或線段)的交點

向量叉乘求三維空間中兩直線(或線段)的交點

1.2D空間的直線相交

在二維空間中,利用兩個直線方程y = kx + b我們可以直接計算出交點,但是這種方法麻煩了些,並且套用到三維空間用公式就更麻煩了,接下來介紹的是如何利用向量叉乘求出直線交點。並且由於利用叉乘最後可以的到一個比例值,這個值的大小還可以判斷四個點所得到的兩個線段是延長線相交還是線段相交。

2.向量叉乘

三維空間中,兩個向量叉乘得到的是一個垂直於兩向量組成的平面的向量,方向可利用右手螺旋法則獲取,這一點百度谷歌一搜一大把,不細說了大小可由下面的公式得到,注意和點乘的區別。

向量叉乘的幾何意義是得到一個三角形的有向面積,如下圖所示,向量OA和OB叉乘的到的向量大小就等於三角形OAB的面積

有了以上基礎,我們就可以開始計算三維空間中的直線交點了 

3.三維空間中的兩直線交點 

下圖CE和AB是平行線且長度相等。

首先確定兩條直線是否平行,利用向量點乘結果是否等於0來判斷,等於0垂直,等於1則平行。

接著我們需要確定兩條直線在一個平面內,否則無論如何也無法相交,這個用向量叉乘來判斷,即判斷向量CA和向量AB叉乘得到的向量是否垂直於向量CD。

然後明確一個目標,在四個點ABCD已知的情況下,求交點O我們只需要知道CO/CD就可以了。通過觀察發現,三角形ACD的面積比三角形CDE的面積等於線段CO和CD的比值,我們來證明一下,步驟很簡單。

證明 :S三角形ACD/S三角形CDE  =  AO比AB

三角形AFO和三角形EGC相似
AF / EG = AO/CE;
CE = AB  所以等式成立

問題轉化成要計算兩個三角形的面積,那麼我們只需要  向量AB,CD和CA就可以了,開始寫程式碼

3.程式碼實現 

利用叉乘求交點少了很多if else的判斷,並且可以做到二維和三維的通用,傳遞引數的時候只要將所有點和向量y軸的值設為0就可以當作二維來使用了。

利用程式碼中得到的比例值num2的大小還可以判斷是延長線相交還是線段相交

注意下述方法所傳入的引數是兩個點和兩個方向,可以改寫成傳入四個點。

        /// <summary>
        /// 判斷線與線之間的相交
        /// </summary>
        /// <param name="intersection">交點</param>
        /// <param name="p1">直線1上一點</param>
        /// <param name="v1">直線1方向</param>
        /// <param name="p2">直線2上一點</param>
        /// <param name="v2">直線2方向</param>
        /// <returns>是否相交</returns>
        public static bool LineLineIntersection(out Vector3 intersection, Vector3 p1, Vector3 v1, Vector3 p2, Vector3 v2)
        {
            intersection = Vector3.zero;
            if (Vector3.Dot(v1, v2) == 1)
            {
                // 兩線平行
                return false;
            }

            Vector3 startPointSeg = p2 - p1;
            Vector3 vecS1 = Vector3.Cross(v1, v2);            // 有向面積1
            Vector3 vecS2 = Vector3.Cross(startPointSeg, v2); // 有向面積2
            float num = Vector3.Dot(startPointSeg, vecS1);

            // 開啟可以在場景中觀察向量
            //Debug.DrawLine(p1, p1 + v1, Color.white, 20000);
            //Debug.DrawLine(p2, p2 + v2, Color.black, 20000);

            //Debug.DrawLine(p1, p1 + startPointSeg, Color.red, 20000);
            //Debug.DrawLine(p1, p1 + vecS1, Color.blue, 20000);
            //Debug.DrawLine(p1, p1 + vecS2, Color.yellow, 20000);

            // 判斷兩這直線是否共面
            if (num >= 1E-05f || num <= -1E-05f)
            {
                return false;
            }

            // 有向面積比值,利用點乘是因為結果可能是正數或者負數
            float num2 = Vector3.Dot(vecS2, vecS1) / vecS1.sqrMagnitude;

            intersection = p1 + v1 * num2;
            return true;
        }