2013年6月30日 星期日

[Note] 遊戲數學 - 座標 向量 矩陣 in XNA (C#)

不管在空間資訊或是3D製作、遊戲設計等。

必免不了的是遊戲數學,這裡做一些簡單的筆記。




[座標系統]

在2D座標系統上 ,我們通常已X軸與Y軸來定義一個2D空間資訊。

通常X軸向右,Y軸向下,角度為 sita

如下圖 , (筆者用小畫家畫,請見諒 :D )

















但是在程式撰寫時,電腦世界通常為如下圖















此外,我們在定義3D 座標系統的時候,有分為左手、右手系統

下圖即為右手系統的例子。












(圖參考至: 點我)

現在市面上應用於左手、右手系統舉例如下

右手系統 : OpenGL , Microsoft XNA , Unity

左手系統 : Microsoft DirectX

所以在撰寫程式或是空間設計的時候必須先了解清楚是哪一個系統。


[向量基礎]

在XNA程式語法上,點座標 和 向量的資料結構是共用的都定義為一個Vector

2D 系統 : Vector2 (2D 點座標 和 向量) - 資料結構定義

3D 系統 : Vector3 (3D 點座標 和 向量) - 資料結構定義

定義方式如 (以3D座標舉例) :

Vector3 X ; 

特別注意,XNA是右手系統,所以Z軸的

向前forward 是指朝向螢幕內部 , Z軸的反向 -- Vector3.Backward (0,0,-1)

向後backward 是指朝向使用者  , Z軸的正向 -- Vector3.Backward (0,0,-1)

[向量運算]

加法可以用Add() 方法或是運算元 +

減法可以用Subtract() 方法或是運算元 -

在XNA裡面,要計算長度可以用以下兩種做法

a. 傳統作法

Vector2 vector_2d = new Vector (3,4) ; //2D

double length_vector2d = Math.Sqrt (vector_2d.X * vector_2d.X + vector_2d.Y * vector_2d.Y)  ; // 5

Vector3 vector_3d = new Vector3(1,2,2) ; //3D

double length_vector3d = Math.Sqrt (vector_3d.X * vector_3d.X + vector_3d.Y * vector_3d.Y + vector_3d.Z * vector_3d.Z) ;  // 3


b. XNA 方法

Vector2 vector_2d = new Vector (3,4) ; //2D

double length_vector2d = vector_2d.Length();

Vector3 vector_3d = new Vector3(1,2,2) ; //3D

 double length_vector3d = vector_3d.Length();


通常必須將向量單位化,也就是將長度調整為1

Vector2 vector_2d = new Vector (3,4) ; //2D

vector_2d.Normalize();

 Vector3 vector_3d = new Vector3(1,2,2) ; //3D

vector_3d.Normalize() ;


~~~ (PS)以下皆以單一2D 或 3D 舉例,請依例類推 ~~~

當然求兩點距離,則可以用以下方法

Vector2 A = new Vector2 (1, 0);

Vector2 B = new Vector2 (0,1);

float C = Vector2.Distance(A,B);  // C  為距離

float D = Vector2.DistanceSquared(A,B);  // 在平方

 
[向量內積]

公式如下 :

v1 ● v2 = |v1| * |v2| * cos(角度 )

通常我們運用向量內積可以來判斷角度以及是否為平行、垂直、反向

Vector3 v1 = new Vector3(1,2,3);

Vecotr3 v2 = new Vector3(2,4,6);

v1.Normalize();  //單位化

v2.Normalize();  //單位化

float dot = Vector.Dot(v1,v2); //兩向量取內積

float angle = (float) Math.Acos (dot) ; // 兩向量取夾角


當然,判斷夾角 如下

如果角度 > 90 度 , 則 v1● v2 < 0

如果角度 = 90 度 , 則 v1● v2 = 0

如果角度 < 90 度 , 則 v1● v2 > 0

在XNA裡面,先將向量單位化之後,再套用公式則兩向量的內積 = 夾角 cos 值

在3D程式通常應用於決定光線對頂點的打光程度。

在遊戲裡面通常判斷是否為同方向,或是反方向

if (dot == 1)
     //v1, v2 平行

else if ( dot == 0)
    // v1,v2 垂直

else if ( dot == -1 )
    //v1,v2 反方向


[向量外積]

在3D向量之中,求其外積表示要求其原來兩個3D向量所形成的平面。

數學式子這邊就不多解釋了..

我們直接看XNA 程式部分。

Vector3 v1 = new Vector3 (1,0,0);

Vector3 v2 = new Vector3 (0,1,0);

Vector3 v3 = Vector3.Cross( v1, v2);  // 外積


[矩陣]

在3D程式之中,往往需要將3X3 矩陣轉換在4X4矩陣空間作運算。

為什麼呢!? ->  因為便於計算,在矩陣相乘過程之中可以加入動作。

如:  旋轉、平移、縮放等

在數學的意義上是乘上一個轉移矩陣做改變

但是在三維座標上必須一一去乘上轉移矩陣做轉換

在4X4矩陣空間可以涵蓋平移、旋轉、縮放、投影等轉換。

(e.g) 如(2,3,4 ) 要往X軸移動2 ,Y軸移動1 則,

                [ 1 0 0 0 ]
[ 2 3 4 1 ] [ 0 1 0 0 ]
                [ 0 0 1 0 ]
                [ 1 2 0 0 ]

[XNA Matrix]

Matrix 結構的相對位置

Right           [ M11 M12 M13 M14 ]
Up               [ M21 M22 M23 M24 ]
Backward    [ M31 M32 M33 M34 ]
Translation  [ M41 M42 M43 M44 ]

反之 left , down ,forward 則 乘上 (-1 )

宣告方式如下 :

Matrix M = Matrix.Identity ;  // 宣告單位矩陣

M.M41 = 1.0f ; // 做平移

M.Right = new Vector3 ( 5 , 6, 7);  // 設定 right (5,6,7)

Vector3 V = M.Right ; // 取值 (5,6,7)

M = Matrix.Invert(M ); //取反矩陣 ,有時可以做picking

M = Matrix.Transpose(M) ; //取轉至矩陣,顧慮效率時使用



然而矩陣相乘,可以直接使用以下相乘

Matrix M1 = Matrix.Identity ;

Matrix M2 = Matrix.Identity ;

Matrix M3 = Matrix.Identity ;

Matrix M4 = M1 * M2 * M3 ;


也可以使用以下 方法

M = Matrix.Multiply ( M1 , M2 );

M4 = Matrix.Multiply( M , M3) ;


特別注意矩陣相乘有順序問題,順序不同結果會不一樣。

[平移、旋轉、縮放]

平移的用法如 :

Matrix.CreateTranslation (1.0f , 1.0f , .0.0f) ; //會將所有座標移動(1,1,0)

如果以針對有基準點的平移,(-1,2) - > (5,4)

Matrix m1 ;

m1 = Matrix.CreateTranslation(1.0f , -2.0f , 0.0f); // 先將之移動到原點

Matrix m2;

m2 = Matrox.CreateTranslation(5.0f , 4.0f , 0.0f); //平移到 (5,4)


則最後做相乘動作,並且設定世界矩陣

Matrix world ;

world = m1 * m2;

Effect.world = world;


縮放兩倍呢!?  (將上列程式碼平移部分改成縮放部分)

Matrix s;

s = Matrix.CreateScale(2);


旋轉呢!?

Matrix r;

float theta;

r = Matrix.CreateRotationZ(theta) ; //依照Z軸旋轉


然而XNA 有提供輔助計算 MathHelper class 可以運用 。

MathHelper (說明請點我)


[攝影機、投影]

根據攝影機位置看到的點,相機上方向量得到視覺矩陣

Vector3 cameraPosition;

Vector3 cameraLookat;

Matrix view = Mareix.CreateLooakAt(cameraPostion , cameraLookat , new Vector3(0.0f , 1.0f , 0.0f));


根據攝影機視角寬高比得到投影矩陣

float viewAngle ;

float aspectRatio ;

Matrix projection = Matrix.CreatePerspectiveFieldOfView(viewAngle , aspectRatio, 1.0f , 1000.0f );

Plane p1 = new Plane (v17 , 15.0f);

Plane p2 = new Plane (v15 , v16 , v17) ;



參考資料來源 : XNA 遊戲程式設計
(上述部分程式碼,皆參考自此書)

沒有留言:

張貼留言