從上篇向量的文章中,我們主要分析出了向量需要包含的資訊和主要的操作。我們總結如下:
- 對分量值x,y,z的存取。
- 向量間的賦值操作。
- 比較兩個向量是否相同。
- 得到零向量。
- 向量求負。
- 向量求模。
- 向量與標量的乘除法。
- 向量標準化。
- 向量加減法。
- 兩點(點用向量表示)的距離。
- 向量點乘。
- 向量叉乘。
以上的這些功能能夠完成我們99%的日常工作,但XNA中也定義了一些其他比較方面的操作,例如直接可以得到向上的單位向量等。那下面我們就看一下XNA中的Vector3是怎麼提供這些功能的。
對分量x,y,z的存取
Vector3類提供了x,y,z的公用欄位,可以直接存取。而且整個類中只有這三個欄位儲存資料,也就是說系統中存在一個Vector3對象時,整個對象只佔用了3個float資料佔用的空間。而且Vector2被定義成了結構,在進行運算時,結構會儲存在棧中,這也會對向量運算等效能的提升帶來協助。而三個分量定義也是沒有用到微軟推崇的屬性,而是直接定義成公用欄位。我想這也有可能是針對效能上的考慮吧。看來,在定義Vertor3在定義時已經把能提高效能的地方都考慮到了,儘管有些執行改善很有限的效能。下面為三個分量在類中的定義:
// 摘要:
// Gets or sets the x-component of the vector.
public float X;
//
// 摘要:
// Gets or sets the y-component of the vector.
public float Y;
//
// 摘要:
// Gets or sets the z-component of the vector.
public float Z;
向量間的賦值操作
向量間的賦值操作就相當於把這個向量的值賦值給另外一個向量。我們知道,在C#中,參考型別賦值,只是賦值控制代碼,也就是我們平常說的引用。但Vector的要求是把另外一個向量的值都取出來。還好,我們定義的Vector3是結構,也就是實值型別,在使用=操作符賦值時,會產生一個拷貝,能夠滿足我們賦值的要求。所以我們不用再取複寫=操作符了。
比較兩個向量是否相等
在比較兩個向量是否相等部分,Vector3繼承了IEquatable<Vector3>介面,所以在判斷是否相等方面,大家應該知道怎麼用了吧。而且Vector3也複寫了==操作符。在判斷是否相等的演算法上,XNA認為只要向量的三個分量值都分別都相等的話,兩個向量就相等。相關的代碼定義如下:
public struct Vector3 : IEquatable<Vector3>
//
// 摘要:
// Tests vectors for inequality.
//
// 參數:
// value1:
// Vector to compare.
//
// value2:
// Vector to compare.
public static bool operator !=(Vector3 value1, Vector3 value2);
//
// 摘要:
// Tests vectors for equality.
//
// 參數:
// value1:
// Source vector.
//
// value2:
// Source vector.
public static bool operator ==(Vector3 value1, Vector3 value2);
//
// 摘要:
// Returns a value that indicates whether the current instance is equal to a
// specified object.
//
// 參數:
// obj:
// Object to make the comparison with.
public override bool Equals(object obj);
//
// 摘要:
// Determines whether the specified Object is equal to the Vector3.
//
//參數:
// other:
// The Vector3 to compare with the current Vector3.
public bool Equals(Vector3 other);
//
// 摘要:
// Gets the hash code of the vector object.
public override int GetHashCode();
得到零向量
得到零向量按照邏輯講,應該是一個靜態函數。該方法和已經存在記憶體裡面的Vector3對象應該是沒有什麼關係的。如果程式需要先定義一個向量,然後再調用這個向量的歸零函數,豈不是很麻煩。所以得到零向量的函數定義如下:
//
// 摘要:
// Returns a Vector3 with all of its components set to zero.
public static Vector3 Zero { get; }
向量求負
向量求負在XNA中用-號操作符定義,例如有一向量A,對向量A求負的話就可以進行下面操作。B=-A;B就是A求負後的向量。
// 摘要:
// Returns a vector pointing in the opposite direction.
//
// 參數:
// value:
// Source vector.
public static Vector3 operator -(Vector3 value);
向量求模
向量求模就是求一個向量的長度,得到標量。這個應該是定義成Vector3對象的函數,而不是靜態。計算模時,會和Object Storage Service的資料有關。XNA中代碼定義如下:
//
// 摘要:
// Calculates the length of the vector.
public float Length();
向量與標量的乘除法
在XNA中,向量與標量的乘除法都是以複寫*和/操作符實現的。代碼定義如下:
//
// 摘要:
// Multiplies a vector by a scalar value.
//
// 參數:
// scaleFactor:
// Scalar value.
//
// value:
// Source vector.
public static Vector3 operator *(float scaleFactor, Vector3 value);
//
// 摘要:
// Multiplies a vector by a scalar value.
//
// 參數:
// value:
// Source vector.
//
// scaleFactor:
// Scalar value.
public static Vector3 operator *(Vector3 value, float scaleFactor);
//
// 摘要:
// Divides a vector by a scalar value.
//
// 參數:
// value:
// Source vector.
//
// divider:
// The divisor.
public static Vector3 operator /(Vector3 value, float divider);
而且在Vector3中還定義了乘除的靜態函數,但我感覺用處不是太大,既然已經定義了重載操作符,就沒有必要再定義那些靜態函數了。也有可能我現在還沒有用到,所以覺得那些函數沒用。但那些函數實現的功能和重載操作符是一樣的,就不在列舉了。
向量標準化
向量標準化就是在已有向量對象的基礎上,得到該向量方向的單位向量。因此該函數也應該是一個附屬在類上的普通函數。定義如下:
//
// 摘要:
// Turns the current vector into a unit vector. The result is a vector one unit
// in length pointing in the same direction as the original vector.
public void Normalize();
//
// 摘要:
// Creates a unit vector from the specified vector. The result is a vector one
// unit in length pointing in the same direction as the original vector.
//
// 參數:
// value:
// The source Vector3.
public static Vector3 Normalize(Vector3 value);
//
// 摘要:
// Creates a unit vector from the specified vector, writing the result to a
// user-specified variable. The result is a vector one unit in length pointing
// in the same direction as the original vector.
//
// 參數:
// value:
// Source vector.
//
// result:
// [OutAttribute] The normalized vector.
public static void Normalize(ref Vector3 value, out Vector3 result);
在標準化的功能上,XNA提供了三個函數,一個函數是把向量自己標準化。第二個函數是靜態函數,標準化傳入的參數向量。第三個也是靜態函數,傳入一個向量,返回一個新的向量,不在傳入參數的基礎上操作。
感覺XNA在定義標準化功能上搞的太複雜了,我覺得只要留一個就可以了,留第一個或者是第二個靜態函數,就能解決所有的問題。
向量加減法
向量加減法和向量乘除法的實現方式很類似,主要是通過重載運算子,和實現一些靜態函數。當然,我還是覺得那些靜態函數用處不大。定義如下:
//
// 摘要:
// Adds two vectors.
//
// 參數:
// value1:
// Source vector.
//
// value2:
// Source vector.
public static Vector3 operator +(Vector3 value1, Vector3 value2);
//
// 摘要:
// Subtracts a vector from a vector.
//
// 參數:
// value1:
// Source vector.
//
// value2:
// Source vector.
public static Vector3 operator -(Vector3 value1, Vector3 value2);
兩點(點用向量表示)的距離
XNA中不存在Point3的定義,所以在表示某個點時,也是用Vector3來表示。即使有Point3的定義,其內容也只是Vector3的子集。所以一般的三維繫統中都會以Vector3來定義三維點。定義如下:
//
// 摘要:
// Calculates the distance between two vectors.
//
// 參數:
// value1:
// Source vector.
//
// value2:
// Source vector.
public static float Distance(Vector3 value1, Vector3 value2);
//
// 摘要:
// Calculates the distance between two vectors.
//
// 參數:
// value1:
// Source vector.
//
// value2:
// Source vector.
//
// result:
// [OutAttribute] The distance between the vectors.
public static void Distance(ref Vector3 value1, ref Vector3 value2, out float result);
我認為第二個函數也沒有必要。
向量點乘
向量點乘也是採用了複寫*號操作符的方法。定義如下:
//
// 摘要:
// Multiplies the components of two vectors by each other.
//
// 參數:
// value1:
// Source vector.
//
// value2:
// Source vector.
public static Vector3 operator *(Vector3 value1, Vector3 value2);
兩個向量的運算有兩種乘法,點乘和內積。但作為表示乘的操作符只有一個*號,所以我們只能選擇一個,捨棄一個。但似乎*號更像點,而不像叉。所以XNA和大部分的三維繫統都會以*號表示點乘。
向量叉乘
向量叉乘,也就是我們說的向量叉積了。其實我們也可以複寫其他動作符來完成叉乘操作。但誰能想到用這個操作符呢?這回造成晦澀難懂的代碼,不如用函數定義。在這個方面,很多三維平台的想法都是一致的。
//
// 摘要:
// Calculates the cross product of two vectors.
//
// 參數:
// vector1:
// Source vector.
//
// vector2:
// Source vector.
public static Vector3 Cross(Vector3 vector1, Vector3 vector2);
//
// 摘要:
// Calculates the cross product of two vectors.
//
// 參數:
// vector1:
// Source vector.
//
// vector2:
// Source vector.
//
// result:
// [OutAttribute] The cross product of the vectors.
public static void Cross(ref Vector3 vector1, ref Vector3 vector2, out Vector3 result);
其他的功能
除了以上的這些功能,XNA中的Vector3類也提供了一些其他比較方便的功能。例如:
- 得到各個方向的單位向量
// 摘要:
// Returns a unit Vector3 designating backward in a right-handed coordinate
// system (0, 0, 1).
public static Vector3 Backward { get; }
//
//摘要:
// Returns a unit Vector3 designating down (0, −1, 0).
public static Vector3 Down { get; }
//
//摘要:
// Returns a unit Vector3 designating forward in a right-handed coordinate system(0,
// 0, −1).
public static Vector3 Forward { get; }
//
//摘要:
// Returns a unit Vector3 designating left (−1, 0, 0).
public static Vector3 Left { get; }
//
//摘要:
// Returns a Vector3 with ones in all of its components.
public static Vector3 One { get; }
//
//摘要:
// Returns a unit Vector3 pointing to the right (1, 0, 0).
public static Vector3 Right { get; }
//
//摘要:
// Returns the x unit Vector3 (1, 0, 0).
public static Vector3 UnitX { get; }
//
//摘要:
// Returns the y unit Vector3 (0, 1, 0).
public static Vector3 UnitY { get; }
//
//摘要:
// Returns the z unit Vector3 (0, 0, 1).
public static Vector3 UnitZ { get; }
//
//摘要:
// Returns a unit vector designating up (0, 1, 0).
public static Vector3 Up { get; }
- 得到三個點組成的三角面的中心點,當然點還是用向量表示。
//
// 摘要:
// Returns a Vector3 containing the 3D Cartesian coordinates of a point specified
// in Barycentric coordinates relative to a 3D triangle.
//
// 參數:
// value1:
// A Vector3 containing the 3D Cartesian coordinates of vertex 1 of the triangle.
//
// value2:
// A Vector3 containing the 3D Cartesian coordinates of vertex 2 of the triangle.
//
// value3:
// A Vector3 containing the 3D Cartesian coordinates of vertex 3 of the triangle.
//
// amount1:
// Barycentric coordinate b2, which expresses the weighting factor toward vertex
// 2 (specified in value2).
//
// amount2:
// Barycentric coordinate b3, which expresses the weighting factor toward vertex
// 3 (specified in value3).
public static Vector3 Barycentric(Vector3 value1, Vector3 value2, Vector3 value3, float amount1, float amount2);