Real-Time-Rendering 向量

        Real-Time-Rendering從第四章開始講解圖形學中常用的數學知識。由於線性代數、幾何等相關知識等還給老師了。。。後面開始根據第四章的知識點並參考大學線性代數課本、3D數學基礎:圖形與遊戲開發等書籍記下圖形學中常用的幾種工具及相關原理。

        首先肯定是向量了,對應unity中Vector2結構體與Vector3結構體。下面通過C#重新實現一個Vector3的結構體來梳理向量相關的知識點。

        在三維座標中三個點可以表示一個空間中確定的點,同時也可以表示一個方向確定的向量。在unityVector3中寫好的靜態Up、Down、Back、Forward、Left、Right分別爲三維座標系中長度爲1的單位向上、向下、向後、向前、向左、向右的6條向量。同時也可以理解爲6個距離原點爲1的點。

        新建腳本Vector3。這裏類似unity使用結構體。定義x、y、z分量代表向量在x軸、y軸、z軸的值。

        Vector3.Magnitude用來算出當前向量的長度。假設一個向量A(x, y, z),向量長度爲√(x² + y² + z²)。Vector3.sqrMagnitude爲長度不開根號的結果。在比較兩個向量長度時等價於比較長度的平方,並且效率較高。代碼如下:

public float Magnitude {
            get {
                return (float)Math.Sqrt(Math.Pow(this.x, 2) + Math.Pow(this.y, 2) + Math.Pow(this.z, 2));
            }
        }

        Vector3.Normalize用來獲得當前向量的標準化向量。即與原向量方向相同的長度爲1的向量。僅需當前向量各分量分別除以當前向量的長度即可。

public Vector3 Normalized {
            get {
                return this / Magnitude;
            }
        }

        Vector3.Equals用來比較兩個向量是否相同。這裏重寫C#中的Equals方法。同時需要重寫Object基類要求重寫的GetHashCode方法。只要三個分量完全相同。兩條向量則一定朝向一致。即爲同一條向量。

public override bool Equals(object other)
        {
            if (!(other is Vector3))
            {
                return false;
            }
            Vector3 vec = (Vector3)other;
            return vec.x == this.x && vec.y == this.y && vec.z == this.z;
        }


        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        然後是向量運算,這裏我們將重寫運算符方法中的+、-、*、/、==、!=。

        向量允許相加,只需將各向量的分量相加即可。

        同樣向量允許相減,各分量依次相減即可。同時向量允許獲取負值,即可分量取負數。

        一般*在向量中的運算叫做點乘或點積。

        向量點乘公式:a·b = [a1 a2 ... an] * [b1 b2 ... bn] = a1b1 + a2b2 + ... + anbn。

        點乘等於向量大小與向量夾角的cos值的積:a·b = |a| |b| cosθ。

        根據上述公式顯然能看出點乘的集合意義在於表示兩個向量的相似程度。值域在-1到1之間。越接近1,兩條向量越相似。越接近-1,兩條向量越相反。當乘積在0時,說明此時夾角爲90度。即兩向量正交。

        同時乘法可用於向量與變量運算,只需把各分量與標量分別相乘即可。

       

public static Vector3 operator *(Vector3 a, float d)
        {
            return new Vector3(a.x * d, a.y * d, a.z * d);
        }


        public static Vector3 operator *(float d, Vector3 a)
        {
            return new Vector3(a.x * d, a.y * d, a.z * d);
        }


        /// <summary>
        /// dot product
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public static float operator *(Vector3 a, Vector3 b) {
            return a.x * b.x + a.y * b.y + a.z * b.z;
        }

        在數學中x運算符對於向量代表的運算稱爲叉乘。

        向量叉乘公式:a x b = [a1 b1 c1] x [a2 b2 c2] = [(b1c2 - c1b2) (c1a2 - a1c2) (a1b2 - b1a2)]

        注意上式僅用於3維向量相叉乘,並且不滿足交換律。

        向量叉乘得到的向量垂直於原來兩個向量,該向量的長度等於向量的大小與向量夾角sin值的積。

        |a x b| = |a| |b| sinθ。

        

public static Vector3 Cross(Vector3 a, Vector3 b) {
            return new Vector3(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x);
        }

        Vector3.Angle可獲得兩個向量的夾角。由上面點乘公式很容易得出:

        θ = arccos(a · b / |a| |b|) = a1b1 + a2b2 + ... + anbn / |a| |b|

public static float Angle(Vector3 a, Vector3 b) {
            double radian = Math.Acos(a * b / (a.Magnitude * b.Magnitude));
            return (float)(radian * 180 / Math.PI);
        }

        Vector3.Product可獲取一個向量相對另一個向量的投影。投影公式的推導如下:

        投影公式

public static Vector3 Project(Vector3 vector, Vector3 onNormal)
        {
            return onNormal * (onNormal * vector / (float)(Math.Pow(onNormal.x, 2) + Math.Pow(onNormal.y, 2) + Math.Pow(onNormal.z, 2)));
        }

       

Vector3.Reflect可獲得一個向量相對於另一個向量的反射向量。推導如下:

反射公式

public static Vector3 Reflect(Vector3 inDirection, Vector3 inNormal) {
            Vector3 res = inDirection + 2 * (-inDirection) * inNormal * inNormal;
            return res;
        } 

        Vector3.Lerp提供線性插值功能,可獲得兩個向量各分量相對於t的線性插值。

public static Vector3 Lerp(Vector3 a, Vector3 b, float t) {
            t = Math.Max(0, t);
            t = Math.Min(1, t);
            return new Vector3(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t, a.z + (b.z - a.z) * t);
        }

        Vector3.Slerp提供球面插值功能。暫時先放一下。因爲球面插值需要用到矩陣或四元數相關的知識。在後面的文章中會補齊。

        

        源碼地址:https://github.com/guishengshi/Real-Time-Rendering

        在工程中Scene/Mathlib文件夾可找到Vector3.unity場景文件。這個就是上述實例了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章