Tag DirectX下的博客主要用於記錄DirectX的學習過程,主要參考《DirectX 12 3D 遊戲實戰開發》。函數
Shader的編寫離不開數學運算,尤爲是向量矩陣類型的運算,DirectX爲咱們準備了一個完備的數學庫DirectXMath.h
,裏面封裝了經常使用的向量矩陣類型,而且重載了許多方法,如點積、叉積、矩陣乘法等。DirectXMath是Windows SDK的一部分,是爲D3D打造的3D數學庫,採用SIMD指令集加速向量運算,如:單條SIMD加法指令能夠直接計算4D向量的加法結果。性能
添加#include <DirectXMath.h>
便可使用DirectXMath庫,其中的代碼都位於namespace DirectX
,使用相關數據類型則需添加#include <DirectXPackedVector.h>
,其中的代碼位於namespace DirectX::PackedVector
。在DirectXMath庫中的核心類型爲XMVECTOR
,它須要按16字節對齊,其中向量類型爲XMFLOAT2
、XMFLOAT3
、XMFLOAT4
。而在開啓SSE2以後,在x86和x64平臺中,XMVECTOR
被定義爲一個共用體_m128
,經過這個類型,SIMD技術才得以實現。學習
// _m128在xmmintrin.h下被定義 typedef union __declspec(intrin_type) __declspec(align(16)) __m128 { float m128_f32[4]; unsigned __int64 m128_u64[2]; __int8 m128_i8[16]; __int16 m128_i16[8]; __int32 m128_i32[4]; __int64 m128_i64[2]; unsigned __int8 m128_u8[16]; unsigned __int16 m128_u16[8]; unsigned __int32 m128_u32[4]; } __m128;
DirectXMath庫提供了設置XMVECTOR
類型中數據的函數。spa
// return zero vector XMVECTOR XM_CALLCONV XMVectorZero(); // return unit vector XMVECTOR XM_CALLCONV XMVectorSplatOne(); // return vector(x, y, z, w) XMVECTOR XM_CALLCONV XMVectorSet(float x, float y, float z, float w); // return vector(Value, Value, Value, Value) XMVECTOR XM_CALLCONV XMVectorReplicate(float Value); // return vector(V.x, V.x, V.x, V.x) XMVECTOR XM_CALLCONV XMVectorSplatX(FXMVECTOR V); // return vector(V.y, V.y, V.y, V.y) XMVECTOR XM_CALLCONV XMVectorSplatY(FXMVECTOR V); // return vector(V.z, V.z, V.z, V.z) XMVECTOR XM_CALLCONV XMVectorSplatZ(FXMVECTOR V);
XMVECTOR
類型的常量應使用XMVECTORF32
或者XMVECTORU32
類型表示,即便用XMVECTORF32
初始化浮點型向量,使用XMVECTORU32
初始化整型向量。code
爲了發揮SIMD特性,在構建向量實例時還應使用庫提供的loading function把實例轉換爲XMVECTOR
類型,同時,庫還提供了逆向轉換的storage function,用於把XMVECTOR
轉換爲XMFLOATn
類型。總結就是:變量統一爲XMVECTOR
類型,類內成員用XMFLOATn
類型,運算前轉換爲XMVECTOR
類型,運算後轉換回XMFLOATn
類型。orm
// loading function(n爲向量維度) XMVECTOR XM_CALLCONV XMLoadFloatn(const XMFLOATn *pSource); // storage function(n爲向量維度) void XM_CALLCONV XMStoreFloatn(XMFLOATn *pDestination, FXMVECTOR V); // 獲取or存儲XMVECTOR中的某個份量(以x份量爲例) float XM_CALLCONV XMVectorGetX(FXMVECTOR V); XMVECTOR XM_CALLCONV XMVectorSetX(FXMVECTOR V, float x);
爲了進一步提升效率,DirectX還容許把XMVECTOR
類型的值做爲函數參數直接送入SSE寄存器中,可是經過這種方法傳遞的參數有數量限制,數量的限制也因平臺而異。爲了讓代碼更具通用性,DirectX爲咱們提供了一種約定註解XM_CALLCONV
。這個註解添加在有XMVECTOR
類型參與的函數定義以前,編譯器會根據平臺轉化爲特定的調用約定。調用約定則會把特定數量的XMVECTOR
類型直接送往寄存器。遊戲
// 在32位的Windows系統上,XM_CALLCONV轉化爲__fastcall調用約定 // 把前三個XMVECTOR類型直接傳入寄存器時,將會出現以下類型定義 typedef const XMVECTOR FXMVECTOR; typedef const XMVECTOR& GXMVECTOR; typedef const XMVECTOR& HXMVECTOR; typedef const XMVECTOR& CXMVECTOR;
DirectX給咱們的建議是,在同一個函數調用中,前三個XMVECTOR
參數應使用類型FXMVECTOR
,第四個XMVECTOR
參數應使用GXMVECTOR
,第五、6個XMVECTOR
應使用HXMVECTOR
,其他的使用CXMVECTOR
。而在定義構造函數時,規則又有所不一樣。定義構造函數時,前三個XMVECTOR
類型應使用FXMVECTOR
類型,其他的使用CXMVECTOR
,並且不要對構造函數使用XM_CALLCONV註解。而在參數輸出時並不會輸出到SSE寄存器內,故處理方式和普通類型一致。在DirectXMath庫某些函數的定義中就能夠體會到XMVECTOR
的參數傳遞法則。開發
// DirectXMath庫中的XMMatrixTransformation函數 inline XMMATRIX XM_CALLCONV XMMatrixTransformation( FXMVECTOR ScalingOrigin, FXMVECTOR ScalingOrientationQuaternion, FXMVECTOR Scaling, GXMVECTOR RotationOrigin, HXMVECTOR RotationQuaternion, HXMVECTOR Translation);
XMVECTOR
類型提供了有關向量間運算、向量與標量運算的運算符重載。編譯器
XMVECTOR XM_CALLCONV operator+ (FXMVECTOR V); XMVECTOR XM_CALLCONV operator- (FXMVECTOR V); XMVECTOR& XM_CALLCONV operator+= (XMVECTOR& V1, FXMVECTOR V2); XMVECTOR& XM_CALLCONV operator-= (XMVECTOR& V1, FXMVECTOR V2); XMVECTOR& XM_CALLCONV operator*= (XMVECTOR& V1, FXMVECTOR V2); XMVECTOR& XM_CALLCONV operator/= (XMVECTOR& V1, FXMVECTOR V2); XMVECTOR& operator*= (XMVECTOR& V, float S); XMVECTOR& operator/= (XMVECTOR& V, float S); XMVECTOR XM_CALLCONV operator+ (FXMVECTOR V1, FXMVECTOR V2); XMVECTOR XM_CALLCONV operator- (FXMVECTOR V1, FXMVECTOR V2); XMVECTOR XM_CALLCONV operator* (FXMVECTOR V1, FXMVECTOR V2); XMVECTOR XM_CALLCONV operator/ (FXMVECTOR V1, FXMVECTOR V2); XMVECTOR XM_CALLCONV operator* (FXMVECTOR V, float S); XMVECTOR XM_CALLCONV operator* (float S, FXMVECTOR V); XMVECTOR XM_CALLCONV operator/ (FXMVECTOR V, float S);
DirectXMath庫提供了封裝好的函數執行向量運算,下面給出其中部分的3D向量版本,對應的有2D和4D版本,函數名形式相同。每一個函數的命名都已經很好地體現了函數的功能。能夠注意到,即便運算在數學上的計算結果是標量,例如點積,對應函數的返回類型仍然是XMVECTOR
,而這個結果標量會被複制到各個份量中。這麼作的緣由之一是儘可能減小SIMD向量和標量的混合運算,提升效率。DirectXMath庫還提供了一些性能開銷交稿的函數的估算版本,這些函數精度低,速度快。博客
XMVECTOR XM_CALLCONV XMVector3Length(FXMVECTOR V); // 計算V的模長 XMVECTOR XM_CALLCONV XMVector3LengthSq(FXMVECTOR V); // 計算V模長的平方 XMVECTOR XM_CALLCONV XMVector3Dot(FXMVECTOR V1, FXMVECTOR V2); // 點積 XMVECTOR XM_CALLCONV XMVector3Cross(FXMVECTOR V1, FXMVECTOR V2); // 叉積 XMVECTOR XM_CALLCONV XMVector3Normalize(FXMVECTOR V); // 規範化 XMVECTOR XM_CALLCONV XMVector3Orthogonal(FXMVECTOR V); // 返回一個與V正交的向量 XMVECTOR XM_CALLCONV XMVector3AngleBetweenVectors(FXMVECTOR V1, FXMVECTOR V2); // 計算向量間的夾角 // 計算V關於Normal的平行份量和正交份量,輸入的Normal必須是已經normalize的 // pParallel存儲平行份量,pPerpendicular存儲正交份量 void XM_CALLCONV XMVector3ComponentsFromNormal( XMVECTOR* pParallel, XMVECTOR* pPerpendicular, FXMVECTOR V, FXMVECTOR Normal ); bool XM_CALLCONV XMVector3Equal(FXMVECTOR V1, FXMVECTOR V2); // 判斷V1和V2是否相同 bool XM_CALLCONV XMVector3NotEqual(FXMVECTOR V1, FXMVECTOR V2); // 判斷V1和V2是否不一樣 // 判斷V1和V2是否近似相等,容差值爲Epsilon,這是爲了彌補浮點偏差 XMFINLINE bool XM_CALLCONV XMVector3NearEquals(FXMVECTOR V1, FXMVECTOR V2, FXMVECTOR Epsilon); XMVECTOR XM_CALLCONV XMVector3LengthEst(FXMVECTOR V); // 估算V的模長 XMVECTOR XM_CALLCONV XMVector3NormalizeEst(FXMVECTOR V); // 估算V的規範化向量
DirectXMath庫也提供了一些數學常量的近似值,以及弧度角度轉化、比較值的函數。
const float XM_PI = 3.141592654f; const float XM_2PI = 6.283185307f; const float XM_1DIVPI = 0.318309886f; const float XM_1DIV2PI = 0.159154943f; const float XM_PIDIV2 = 1.570796327f; const float XM_PIDIV4 = 0.785398163f; inline float XMConvertToRadians(float fDegrees) { return fDegrees * (XM_PI / 180.0f); } inline float XMConvertToDegrees(float fRadians) { return fRadians * (180.0f / XM_PI); } template<class T> inline T XMMin(T a, T b) { return (a < b) ? a : b; } template<class T> inline T XMMax(T a, T b) { return (a > b) ? a : b; }