(注:【D3D11游戲編程】學習筆記系列由CSDN作者BonChoix所寫,轉載請注明出處:http://blog.csdn.net/BonChoix,謝謝~)
一、XNA Math簡介
在D3D10及之前的版本中,3D數學庫是伴隨在D3DX庫中的。在D3D11版中,3D數學庫被單獨隔離出來,為XNA Math庫,功能和之前基本一樣,但是建立在SIMD指令上,以更好地利用Windows及XBox360上特殊的硬件寄存器(128位,可以同時操作4個32位數)。
二、向量類型
在XNA數學庫中,核心的向量類型為XMVECTOR,它正好映射為SIMD硬件上的128位寄存器,以利用一個指令同時操作4個32位的數。當SEE2指令集可用時,可以定義為:
- typedef __m128 XMVECTOR;
在操作向量時,XMVECTOR除了用於4維向量外,還可以用於操作2維、三維向量,以於多出的幾維默認為0,不關心即可。
此外要注意的是,XMVECTOR在內存中是16位對齊的,當作為局部或全局變量使用時,由硬件自動實現對齊。對於類中的成員變量,則推薦使用XMFLOAT2(2D), XMFLOAT3(3D), XMFLOAT4(4D)來代替XMVECTOR,定義分別如下:
- typedef struct _XMFLOAT2
- {
- FLOAT x;
- FLOAT y;
- } XMFLOAT2;
- typedef struct _XMFLOAT3
- {
- FLOAT x;
- FLOAT y;
- FLOAT z;
- } XMFLOAT3;
- typedef struct _XMFLOAT4
- {
- FLOAT x;
- FLOAT y;
- FLOAT z;
- FLOAT w;
- } XMFLOAT4;
但是,如果直接使用XMFLOAT2、XMFLOAT3等這些類型進行計算,是不會利用到SIMD指令的加速效果的,因此在計算前要把這些向量轉換為XMVECTOR,然后再進行各種向量運算。這幾種類型與XMVECTOR之間的相互轉換函數主要分為Store和Load兩種,Store型用來把一個XMVECTOR存儲到指定的XMFLOATx中,Load型用來從一個XMFLOATx讀取內容到XMVECTOR中。總結下這段話內容,即:
1. 在局部、全局變量中使用XMVECTOR類型;
2. 在類中定義向量成員時,使用類型XMFLOAT2,XMFLOAT3,XMFLOAT4;
3. 對類中的向量進行運算時,用Load型函數把相應向量讀取到XMVECTOR中,再進行運算;
4. 運算完后把相應的結果XMVECTOR通過Store型函數存儲到相應的XMFLOATx向量中。
三、 Load型和Store型函數
Load型函數用來從一個XMFLOATx向量中讀取內容到XMVECTOR中。如下幾種:
- XMVECTOR XMLoadFloat2(CONST XMFLOAT2 *pSource); //從XMFLOAT2中讀取內容到一個XMVECTOR中
- XMVECTOR XMLoadFloat3(CONST XMFLOAT3 *pSource); //從XMFLOAT3中讀取內容到一個XMVECTOR中
- XMVECTOR XMLoadFloat4(CONST XMFLOAT4 *pSource); //從XMFLOAT4中讀取內容到一個XMVECTOR中
此外除了XMFLOATx類型,還可以Load到其他類型的向量數據中,如:
- XMVECTOR XMLoadInt2(CONST UINT *pSource); //從二維UINT向量中讀取內容到一個XMVECTOR向量中
- XMVECTOR XMLoadColor(CONST XMCOLOR *pSource); //從XMCOLOR中讀取內容到一個XMVECTOR中
- XMVECTOR XMLoadByte4(CONST XMBYTE4 *pSource); //從XMBYTE4中讀取內容到一個XMVECTOR中
Store型函數用來把一個XMVECTOR存儲到一個XMFLOATx向量中。如下幾種:
- void XMStoreFloat2(XMFLOAT2 *pDest, FXMVECTOR V); //把XMVECTOR存儲到一個XMFLOAT2向量中
- void XMStoreFloat3(XMFLOAT3 *pDest, FXMVECTOR V); //把XMVECTOR存儲到一個XMFLOAT3向量中
- void XMStoreFloat4(XMFLOAT4 *pDest, FXMVECTOR V); //把XMVECTOR存儲到一個XMFLOAT4向量中
同理,除了XMFLOATx類型,還可以把XMVECTOR存儲到其他類型向量中,如:
- void XMStoreInt3(UINT *pDest, FXMVECTOR V); //把XMVECTOR存儲到一個二維UINT向量中
- void XMStoreColor(XMCOLOR *pDest, FXMVECTOR V); //把XMVECTOR存儲到一個XMCOLOR向量中
- void XMStoreByte4(XMBYTE4 *pDest, FXMVECTOR V); //把XMVECTOR存儲到一個XMBYTE4向量中
由於XMVECTOR映射為一個特殊的SIMD寄存器,而不是一個struct,因此我們不能直接操作其x,y,z,w維數據,因此提供了以下幾個Set和Get函數:
- FLOAT XMVectorGetX(FXMVECTOR V); //得到XMVECTOR的X維數據
- FLOAT XMVectorGetY(FXMVECTOR V); //得到XMVECTOR的Y維數據
- FLOAT XMVectorGetZ(FXMVECTOR V); //得到XMVECTOR的Z維數據
- FLOAT XMVectorGetW(FXMVECTOR V); //得到XMVECTOR的W維數據
- XMVECTOR XMVectorSetX(FXMVECTOR V, FLOAT x); //設置一個XMVECTOR的X維數據,並返回新XMVECTOR
- XMVECTOR XMVectorSetY(FXMVECTOR V, FLOAT y); //設置一個XMVECTOR的Y維數據,並返回新XMVECTOR
- XMVECTOR XMVectorSetZ(FXMVECTOR V, FLOAT z); //設置一個XMVECTOR的Z維數據,並返回新XMVECTOR
- XMVECTOR XMVectorSetW(FXMVECTOR V, FLOAT w); //設置一個XMVECTOR的W維數據,並返回新XMVECTOR
你可能注意到,在上面所有的函數中,有好多FXMVECTOR類型的參數,而不是XMVECTOR類型。別急,這正是我們下一步要說的。
四、 XMVECTOR類型參數傳遞規定
為了更好地利用SIMD,當XMVECTOR作為函數參數類型時,有特殊的規定。這些規則與平台相關,比如在32位和64位的Windows及XBox360下規則就不一樣。為了在寫代碼時與平台無關,XNA Math專門針對函數參數類型定義了這兩個類型:CXMVECTOR和FXMVECTOR。在不同的平台下這兩個類型有着相應的不同的定義,對於程序員在寫代碼時則無需考慮這些,只要服從相應的規則即可。規則如下:
在一個函數中,前三個使用到XMVECTOR類型的參數,必須為FXMVECTOR;之后所有其他的XMVECTOR類型參數要為CXMVECTOR。
- XMINLINE XMMATRIX XMMatrixTransformation(
- FXMVECTOR ScalingOrigin, //第1個XMVECTOR類型參數
- FXMVECTOR ScalingOrientationQuaternion, //第2個XMVECTOR類型參數
- FXMVECTOR Scaling, //第3個XMVECTOR類型參數
- CXMVECTOR RotationOrigin, //第4個開始,后面的全部為CXMVECTOR類型
- CXMVECTOR RotationQuaternion,
- CXMVECTOR Translation);
當然,各參數之間也可以夾雜其他非XMVECTOR類型的參數,但只要是前三個類型為XMVECTOR的參數使用FXMVECTOR,后面所有類型為XMVECTOR的參數使用CXMVECTOR即可。例如:
- XMINLINE XMMATRIX XMMatrixTransformation2D(
- FXMVECTOR ScalingOrigin, //第1個XMVECTOR類型參數
- FLOAT ScalingOrientation,
- FXMVECTOR Scaling, //第2個XMVECTOR類型參數
- FXMVECTOR RotationOrigin, //第3個XMVECTOR類型參數
- FLOAT Rotation,
- CXMVECTOR Translation); //第4個,必須使用CXMVECTOR
是不是感覺這規則有點奇怪?反正我感覺很奇怪,不過無所謂,其實也很簡單,我們平時操作XMVECTOR類型進行運算時不用關心這個,僅僅是在定義函數時,如果參數類型為XMVECTOR,才需要考慮這個規則。
五、 常量向量
當用到常量型的XMVECTOR時,應該使用XMVECTORF32類型,尤其是當使用初始化式時。例如:
- const XMVECTORF32 g_Zero = {0.f,0.f,0.f,0.f};
- static const XMVECTORF32 g_tmp = {x,y,z,w};
XMVECTORF32是16字節對齊的數據結構,可以轉換為XMVECTOR類型。定義如下:
- typedef _DECLSPEC_ALIGN_16_ struct XMVECTORF32
- {
- union
- {
- float v[4];
- XMVECTOR v;
- };
- //如果是在C++環境下,則還有其他成員函數
- //比如重載()操作符,轉換為XMVECTOR類型
- };
因此,如果想事先定義一些常量型的XMVECTOR,則可以用const XMVECTORF32類型來定義,在程序中用到XMVECTOR類型時,再利用類型轉換(static_cast)轉換為XMVECTOR類型即可。比如我們可能想事先定義好各種常見的顏色,像白色可以這樣定義:const XMFLOAT32 White = {1.f, 1.f, 1.f, 1.f};
六、 針對XMVECTOR類型重載的操作符
在C++環境下,針對各種常見的向量操作,對XMVECTOR重載了相應的操作符。比如:向量加減+、-、+=、-=,向量與數字相乘:*、/、*=、/=,向量點積:*,*=等。
七、 一些Setter函數
- XMVECTOR XMVectorZero(); //[0,0,0,0]
- XMVECTOR XMVectorSplatOne(); //[1.f, 1.f, 1.f, 1.f]
- XVMECTOR XMVectorSet(FLOAT x, FLOAT y, FLOAT z, FLOAT w); //[x,y,z,w]
- XMVECTOR XMVectorReplicate(FLOAT s); //[s, s, s, s]
- XMVECTOR XMVectorSplatX(FXMVECTOR v); //[v.x, v.x, v.x, v.x]
- XMVECTOR XMVectorSplatY(FXMVECTOR v); //[v.y, v.y, v.y, v.y]
- XMVECTOR XMVectorSplatZ(FXMVECTOR v); //[v.y, v.y, v.y, v.y]
八、 向量操作函數
- XMVECTOR XMVector3Length(FXMVECTOR v); //長度L,結果為[L,L,L,L],效率起見,全部為XMVECTOR類型,我們只取一個維即可。
- XMVECTOR XMVector3LengthSq(FXMVECTOR v); //長度L平方根,結果存放同上。其他任何返回scalar數的同理
- XMVECTOR XMVector3Dot(FXMVECTOR v1, FXMVECTOR v2); //點積dot,[dot, dot, dot, dot]
- XMVECTOR XMVector3Cross(FXMVECTOR v1, FXMVECTOR v2); //叉乘
- XMVECTOR XMVector3Normalize(FXMVECTOR v); //歸一化
- XMVECTOR XMVector3Orthogonal(FXMVECTOR v); //返回一個垂直的向量
- XMVECTOR XMVector3AngleBetweenVectors(FXMVECTOR v1, FXMVECTOR v2); //返回兩向量間角度angle,[angle, angle, angle, angle]
學習XNA Math庫的最好方法就是參考SDK,在安裝好最新DirectX SDK后都會有個文檔《DirectX Documentation for C++》,里面有XNA Math詳細介紹,遇到不懂的函數可以直接在上面查找。以上只是對XMVECTOR的一些基本介紹,其他所有的XNA Math函數、類型都可以參考SDK。
最后,附加作者Luna的源代碼,通過示例來熟悉XMVECTOR的用法。