DirectX11 With Windows SDK--05 DirectXMath數學庫


前言

xnamath.h原本是位於DirectX SDK的一個數學庫,但是現在Windows SDK包含的數學庫已經拋棄掉原來的xnamath.h,並演變成了現在的DirectXMath.h。其實本質上並沒有多大區別,只是將原來的xna數學函數移植到了這里,並多了一層名稱空間DirectX

DirectX11 With Windows SDK完整目錄

Github項目源碼

歡迎加入QQ群: 727623616 可以一起探討DX11,以及有什么問題也可以在這里匯報。

SIMD與SSE2指令集加速

SIMD(單指令多數據)可以僅使用一條指令就同時完成多個數據的運算或處理。

其中Intel處理器支持SSE2(SIMD流擴展2)指令集,提供了128位的寄存器,在硬件層面上可以做到同時進行4個32位float或者uint的運算,特別適合用於表示4D向量或者4x4的矩陣。而xna數學庫正是利用了SSE2指令集來實現硬件加速,在運算性能上有所提升。

默認情況下,VS的項目會直接支持SSE2指令集。

向量和矩陣

向量

在xna數學庫中,用於運算的向量類型為XMVECTOR,可以看到:

typedef __m128 XMVECTOR;

而__m128是一個共用體:

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;

可以發現,__m128是一種固有類型,並且在內存上嚴格要求按16字節對齊,即在內存上的地址最后一個十六進制值必須從0開始。除此之外,它還可以被表示成各種類型。在這里,內存要求對齊是因為寄存器從內存中讀取或者寫入數據也是直接按對齊的16字節進行的,確保快速讀寫。

如果需要存儲向量,則應該用下面的這些類型來進行存儲:

2D向量: XMFLOAT2(常用), XMINT2, XMUINT2
3D向量: XMFLOAT3(常用), XMINT3, XMUINT3
4D向量: XMFLOAT4(常用), XMINT4, XMUINT4

這些類型可以比較方便進行賦值修改,但不支持運算。

向量的存取

由於XMFLOAT3等這些用於存儲的類型是不能直接用到指令集加速的。要想進行向量的運算,就需要從用於存儲的變量,通過讀取函數,將數據讀入到XMVECTOR下面這些函數都是用於向量的讀取:

// 2D向量讀取
XMVECTOR XMLoadFloat2(const XMFLOAT2* pSource);
XMVECTOR XMLoadSInt2(const XMINT2* pSource);
XMVECTOR XMLoadUInt2(const XMUINT2* pSource);
// 3D向量讀取
XMVECTOR XMLoadFloat3(const XMFLOAT3* pSource);
XMVECTOR XMLoadSInt3(const XMINT3* pSource);
XMVECTOR XMLoadUInt3(const XMUINT3* pSource);
// 4D向量讀取
XMVECTOR XMLoadFloat4(const XMFLOAT4* pSource);
XMVECTOR XMLoadSInt4(const XMINT4* pSource);
XMVECTOR XMLoadUInt4(const XMUINT4* pSource);

調用了對向量的一些操作、運算函數后,我們需要使用存儲函數將結果保存起來

// 2D向量存儲
void XMStoreFloat2(XMFLOAT2* pDestination, FXMVECTOR V);
void XMStoreSInt2(XMINT2* pDestination, FXMVECTOR V);
void XMStoreUInt2(XMUINT2* pDestination, FXMVECTOR V);
// 3D向量存儲
void XMStoreFloat3(XMFLOAT3* pDestination, FXMVECTOR V);
void XMStoreSInt3(XMINT3* pDestination, FXMVECTOR V);
void XMStoreUInt3(XMUINT3* pDestination, FXMVECTOR V);
// 4D向量存儲
void XMStoreFloat4(XMFLOAT4* pDestination, FXMVECTOR V);
void XMStoreSInt4(XMINT4* pDestination, FXMVECTOR V);
void XMStoreUInt4(XMUINT4* pDestination, FXMVECTOR V);

向量間的運算

在轉成了XMVECTOR后,就可以使用xna的數學庫函數了。首先是向量重載的一些運算符:

// 單目運算符
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& XM_CALLCONV operator*= (XMVECTOR& V, float S);
XMVECTOR& XM_CALLCONV 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);

注意到這里有FXMVECTOR,可以查看具體的含義:

// Fix-up for (1st-3rd) XMVECTOR parameters that are pass-in-register for x86, ARM, ARM64, and vector call; by reference otherwise
#if ( defined(_M_IX86) || defined(_M_ARM) || defined(_M_ARM64) || _XM_VECTORCALL_ ) && !defined(_XM_NO_INTRINSICS_)
typedef const XMVECTOR FXMVECTOR;
#else
typedef const XMVECTOR& FXMVECTOR;
#endif

// Fix-up for (4th) XMVECTOR parameter to pass in-register for ARM, ARM64, and x64 vector call; by reference otherwise
#if ( defined(_M_ARM) || defined(_M_ARM64) || defined(_M_HYBRID_X86_ARM64) || (_XM_VECTORCALL_ && !defined(_M_IX86) ) ) && !defined(_XM_NO_INTRINSICS_)
typedef const XMVECTOR GXMVECTOR;
#else
typedef const XMVECTOR& GXMVECTOR;
#endif

// Fix-up for (5th & 6th) XMVECTOR parameter to pass in-register for ARM64 and vector call; by reference otherwise
#if ( defined(_M_ARM64) || defined(_M_HYBRID_X86_ARM64) || _XM_VECTORCALL_ ) && !defined(_XM_NO_INTRINSICS_)
typedef const XMVECTOR HXMVECTOR;
#else
typedef const XMVECTOR& HXMVECTOR;
#endif

// Fix-up for (7th+) XMVECTOR parameters to pass by reference
typedef const XMVECTOR& CXMVECTOR;

在龍書里面僅提到了FXMVECTORCXMVECTOR兩種變體,但現在居然又多出了GXMVECTORHXMVECTOR兩種變體。。。

經過一番分析,實際上是不同平台架構的寄存器數目是不一樣的,如果沒有被解釋成引用類型,則是按值直接傳遞給寄存器,提升傳輸速度;如果被解釋成引用類型的話,則實際上是需要通過地址間接的傳遞到寄存器上。

對於x86平台,或使用__fastcall約定,最多支持3個寄存器

對於ARM平台,最多支持4個寄存器

對於ARM64或平台,或使用__vectorcall約定,最多支持6個寄存器

經過測試,本人的電腦在Win32模式下使用了__fastcall約定,可以支持3個寄存器,在x64模式下則使用了__vectorcall約定,支持6個寄存器。

因此,對於自定義函數,如果需要傳入XMVECTOR的話,前3個向量需要使用FXMVECTOR,第4個向量需要使用GXMVECTOR,第5-6個需要使用HXMVECTOR,從第7個開始則使用CXMVECTOR。 可以說還是非常奇葩的約定了。

而如果要傳入XMMATRIX的話,第1個矩陣需要使用FXMMATRIX,其余矩陣需要使用CXMMATRIX

除此之外上面的函數還使用了XM_CALLCONV宏,觀察該宏的定義:

#if _XM_VECTORCALL_
#define XM_CALLCONV __vectorcall
#else
#define XM_CALLCONV __fastcall

對於x86/Win32平台,使用的是__fastcall,而x64平台則使用的是__vectorcall。它們的目的都是為了能把盡可能多的變量直接傳入寄存器,只不過__vectorcall能夠比__fastcall傳入更多的變量到寄存器上。

因此,對於自定義函數,只要是使用了XMVECTOR或者XMMATRIX作為形參,則必須在函數名前加上XM_CALLCONV,否則在x86模式可能會出現下述錯誤:
formal parameter with requested alignment of 16 won't be aligned

接下來列出一些可能比較常用的向量相關函數:

// 用於獲取向量的函數
XMVECTOR XM_CALLCONV XMVectorZero();	// 返回向量(0.0f, 0.0f, 0.0f, 0.0f)
XMVECTOR XM_CALLCONV XMVectorSet(float x, float y, float z, float w);	// 返回向量(x, y, z, w)
XMVECTOR XM_CALLCONV XMVectorReplicate(float Value);	// 返回向量(Value, Value, Value, Value)
XMVECTOR XM_CALLCONV XMVectorSplatX(FXMVECTOR V);		// 返回向量(V.x, V.x, V.x, V.x)
XMVECTOR XM_CALLCONV XMVectorSplatY(FXMVECTOR V);		// 返回向量(V.y, V.y, V.y, V.y)
XMVECTOR XM_CALLCONV XMVectorSplatZ(FXMVECTOR V);		// 返回向量(V.z, V.z, V.z, V.z)
XMVECTOR XM_CALLCONV XMVectorSplatW(FXMVECTOR V);		// 返回向量(V.w, V.w, V.w, V.w)
XMVECTOR XM_CALLCONV XMVectorTrueInt();					// 返回128位全1的向量
XMVECTOR XM_CALLCONV XMVectorFalseInt();				// 返回128位全0的向量

// 用於獲取向量分量的函數
float XM_CALLCONV XMVectorGetX(FXMVECTOR V);	// 獲取分量V.x
float XM_CALLCONV XMVectorGetY(FXMVECTOR V);	// 獲取分量V.y
float XM_CALLCONV XMVectorGetZ(FXMVECTOR V);	// 獲取分量V.z
float XM_CALLCONV XMVectorGetW(FXMVECTOR V);	// 獲取分量V.w

// 用於設置向量分量的函數
XMVECTOR XM_CALLCONV XMVectorSetX(FXMVECTOR V, float x);	// 返回向量(x, V.y, V.z, V.w)
XMVECTOR XM_CALLCONV XMVectorSetY(FXMVECTOR V, float y);	// 返回向量(V.x, y, V.z, V.w)
XMVECTOR XM_CALLCONV XMVectorSetZ(FXMVECTOR V, float z);	// 返回向量(V.x, V.y, z, V.w)
XMVECTOR XM_CALLCONV XMVectorSetW(FXMVECTOR V, float w);	// 返回向量(V.x, V.y, V.z, w)
XMVECTOR XM_CALLCONV XMVectorSwizzle(FXMVECTOR V, uint32_t E0, uint32_t E1, uint32_t E2, uint32_t E3);	// 返回向量(V[E0], V[E1], V[E2], V[E3])

// 用於向量比較的函數
// 下面這些函數若為真,返回128位全1,否則返回128位全0
XMVECTOR XM_CALLCONV XMVectorEqual(FXMVECTOR V1, FXMVECTOR V2);			// 對比兩個向量128位是否都相同
XMVECTOR XM_CALLCONV XMVectorNotEqual(FXMVECTOR V1, FXMVECTOR V2);		// 對比兩個向量128位是否存在不同
XMVECTOR XM_CALLCONV XMVectorGreater(FXMVECTOR V1, FXMVECTOR V2);		// 對比V1四個分量是否都比V2的大
XMVECTOR XM_CALLCONV XMVectorGreaterOrEqual(FXMVECTOR V1, FXMVECTOR V2);// 對比V1四個分量是否都比V2的大或相等
XMVECTOR XM_CALLCONV XMVectorLess(FXMVECTOR V1, FXMVECTOR V2);			// 對比V1四個分量是否都比V2的小
XMVECTOR XM_CALLCONV XMVectorLessOrEqual(FXMVECTOR V1, FXMVECTOR V2);	// 對比V1四個分量是否都比V2的小或相等

// 用於向量分量操作的函數
XMVECTOR XM_CALLCONV XMVectorMin(FXMVECTOR V1, FXMVECTOR V2);	// 返回向量的每一個分量對應V1和V2分量的最小值
XMVECTOR XM_CALLCONV XMVectorMax(FXMVECTOR V1, FXMVECTOR V2);	// 返回向量的每一個分量對應V1和V2分量的最大值
XMVECTOR XM_CALLCONV XMVectorRound(FXMVECTOR V);				// 對每個分量四舍五入
XMVECTOR XM_CALLCONV XMVectorFloor(FXMVECTOR V);				// 對每個分量向下取整
XMVECTOR XM_CALLCONV XMVectorCeiling(FXMVECTOR V);				// 對每個分量向上取整
XMVECTOR XM_CALLCONV XMVectorClamp(FXMVECTOR V, FXMVECTOR Min, FXMVECTOR Max);	// 對每個分量限定在[Min, Max]范圍
XMVECTOR XM_CALLCONV XMVectorSaturate(FXMVECTOR V);				// 對每個分量限定在[0.0f, 1.0f]范圍
XMVECTOR XM_CALLCONV XMVectorReciprocal(FXMVECTOR V);				// 返回(1/V.x, 1/V.y, 1/V.z, 1/V.w)

// 2D向量的函數
XMVECTOR XM_CALLCONV XMVector2Dot(FXMVECTOR V1, FXMVECTOR V2);		// 每個分量都是V1.x * V2.x + V1.y * V2.y
XMVECTOR XM_CALLCONV XMVector2Cross(FXMVECTOR V1, FXMVECTOR V2);	// 每個分量都是V1.x * V2.y - V2.x * V1.y
XMVECTOR XM_CALLCONV XMVector2LengthSq(FXMVECTOR V);				// 每個分量都是V.x * V.x + V.y * V.y
XMVECTOR XM_CALLCONV XMVector2Length(FXMVECTOR V);					// 每個分量都是sqrt(V.x * V.x + V.y * V.y)
XMVECTOR XM_CALLCONV XMVector2Normalize(FXMVECTOR V);				// 標准化2D向量(單位向量化)
XMVECTOR XM_CALLCONV XMVector2Reflect(FXMVECTOR Incident, FXMVECTOR Normal);	// 鏡面反射向量
XMVECTOR XM_CALLCONV XMVector2LinePointDistance(FXMVECTOR LinePoint1, FXMVECTOR LinePoint2, FXMVECTOR Point);	// 每個分量都是點到直線的距離

// 3D向量的函數
XMVECTOR XM_CALLCONV XMVector3Dot(FXMVECTOR V1, FXMVECTOR V2);		// 每個分量都是V1.x * V2.x + V1.y * V2.y + V1.z * V2.z
XMVECTOR XM_CALLCONV XMVector3Cross(FXMVECTOR V1, FXMVECTOR V2);	// 返回(V1.y * V2.z - V1.z * V2.y, V1.z * V2.x - V1.x * V2.z, V1.x * V2.y - V1.y * V2.x, 0.0f)
XMVECTOR XM_CALLCONV XMVector3LengthSq(FXMVECTOR V);				// 每個分量都是V.x * V.x + V.y * V.y + V.z * V.z
XMVECTOR XM_CALLCONV XMVector3Length(FXMVECTOR V);					// 每個分量都是sqrt(V.x * V.x + V.y * V.y + V.z * V.z)
XMVECTOR XM_CALLCONV XMVector3Normalize(FXMVECTOR V);				// 標准化3D向量(單位向量化)
XMVECTOR XM_CALLCONV XMVector3Reflect(FXMVECTOR Incident, FXMVECTOR Normal);	// 鏡面反射向量
XMVECTOR XM_CALLCONV XMVector3LinePointDistance(FXMVECTOR LinePoint1, FXMVECTOR LinePoint2, FXMVECTOR Point);	// 每個分量都是點到直線的距離

// 4D向量的函數
XMVECTOR XM_CALLCONV XMVector4Dot(FXMVECTOR V1, FXMVECTOR V2);		// 每個分量都是V1.x * V2.x + V1.y * V2.y + V1.z * V2.z + V1.w * V2.w
XMVECTOR XM_CALLCONV XMVector4LengthSq(FXMVECTOR V);				// 每個分量都是V.x * V.x + V.y * V.y + V.z * V.z + V.w * V.w
XMVECTOR XM_CALLCONV XMVector4Length(FXMVECTOR V);					// 每個分量都是sqrt(V.x * V.x + V.y * V.y + V.z * V.z + V.w * V.w)
XMVECTOR XM_CALLCONV XMVector4Normalize(FXMVECTOR V);				// 標准化4D向量(單位向量化)
XMVECTOR XM_CALLCONV XMVector4Reflect(FXMVECTOR Incident, FXMVECTOR Normal);	// 鏡面反射向量

矩陣

在xna數學庫中,用於運算的矩陣類型為XMMATRIX,實際上里面是由4個XMVECTOR的數組構成的結構體。

如果需要存儲矩陣,則可以使用下面這些類型:

XMFLOAT3X3
XMFLOAT4X3
XMFLOAT4X4

矩陣的存取

要想進行矩陣的運算,就需要從用於存儲的變量,通過讀取函數,將數據讀入到XMMATRIX。下面這些函數都是用於矩陣的讀取:

XMMATRIX XM_CALLCONV XMLoadFloat3x3(const XMFLOAT3X3* pSource);
XMMATRIX XM_CALLCONV XMLoadFloat4x3(const XMFLOAT4X3* pSource);
XMMATRIX XM_CALLCONV XMLoadFloat4x4(const XMFLOAT4X4* pSource);

如果需要存儲運算得到的矩陣,則可以使用下面的函數:

void XM_CALLCONV XMStoreFloat3x3(XMFLOAT3X3* pDestination, FXMMATRIX M);
void XM_CALLCONV XMStoreFloat4x3(XMFLOAT4X3* pDestination, FXMMATRIX M);
void XM_CALLCONV XMStoreFloat4x4(XMFLOAT4X4* pDestination, FXMMATRIX M);

矩陣間的運算

在轉成了XMMATRIX后,就可以使用xna的數學庫函數了。首先是矩陣重載的一些運算符,這些都是矩陣類內定義的函數:

// 賦值
XMMATRIX& operator= (const XMMATRIX& M);
// 單目符號運算符
XMMATRIX operator+ () const;
XMMATRIX operator- () const;
// 運算並賦值
XMMATRIX& XM_CALLCONV operator+= (FXMMATRIX M);
XMMATRIX& XM_CALLCONV operator-= (FXMMATRIX M);
XMMATRIX& XM_CALLCONV operator*= (FXMMATRIX M);
XMMATRIX& operator*= (float S);
XMMATRIX& operator/= (float S);
// 矩陣運算,注意矩陣與矩陣的乘法不是各分量相乘的
XMMATRIX XM_CALLCONV operator+ (FXMMATRIX M) const;
XMMATRIX XM_CALLCONV operator- (FXMMATRIX M) const;
XMMATRIX XM_CALLCONV operator* (FXMMATRIX M) const;
XMMATRIX operator* (float S) const;
XMMATRIX operator/ (float S) const;

friend XMMATRIX XM_CALLCONV operator* (float S, FXMMATRIX M);

然后是一些常用的矩陣函數:

bool XM_CALLCONV XMMatrixIsNaN(FXMMATRIX M);			// 矩陣的每個分量都不是一個數(NaN)
bool XM_CALLCONV XMMatrixIsInfinite(FXMMATRIX M);		// 矩陣的每個分量都是無窮大
bool XM_CALLCONV XMMatrixIsIdentity(FXMMATRIX M);		// 矩陣是否為單位向量

XMMATRIX XM_CALLCONV XMMatrixMultiply(FXMMATRIX M1, CXMMATRIX M2);				// 矩陣乘法
XMMATRIX XM_CALLCONV XMMatrixMultiplyTranspose(FXMMATRIX M1, CXMMATRIX M2);		// 矩陣乘法后轉置
XMMATRIX XM_CALLCONV XMMatrixTranspose(FXMMATRIX M);							// 矩陣轉置
XMMATRIX XM_CALLCONV XMMatrixInverse(_Out_opt_ XMVECTOR* pDeterminant, _In_ FXMMATRIX M);	// 矩陣求逆,可選輸出行列式
XMVECTOR XM_CALLCONV XMMatrixDeterminant(FXMMATRIX M);							// 矩陣求行列式,每個分量都是

// 將矩陣的縮放、旋轉、平移分量拆出來,其中旋轉分量是四元數
bool XM_CALLCONV XMMatrixDecompose(_Out_ XMVECTOR *outScale, _Out_ XMVECTOR *outRotQuat, _Out_ XMVECTOR *outTrans, _In_ FXMMATRIX M);	

XMMATRIX XM_CALLCONV XMMatrixIdentity();		// 獲取單位向量
XMMATRIX XM_CALLCONV XMMatrixSet(float m00, float m01, float m02, float m03,	// 設置每個分量並獲取一個矩陣
	float m10, float m11, float m12, float m13,
	float m20, float m21, float m22, float m23,
	float m30, float m31, float m32, float m33);
XMMATRIX XM_CALLCONV XMMatrixTranslation(float OffsetX, float OffsetY, float OffsetZ);	// 平移矩陣
XMMATRIX XM_CALLCONV XMMatrixTranslationFromVector(FXMVECTOR Offset);					// 使用向量來獲取平移矩陣
XMMATRIX XM_CALLCONV XMMatrixScaling(float ScaleX, float ScaleY, float ScaleZ);			// 縮放矩陣
XMMATRIX XM_CALLCONV XMMatrixScalingFromVector(FXMVECTOR Scale);						// 使用向量來獲取縮放矩陣
XMMATRIX XM_CALLCONV XMMatrixRotationX(float Angle);									// 繞X軸旋轉(弧度,從X軸正方向朝原點看順時針)矩陣							
XMMATRIX XM_CALLCONV XMMatrixRotationY(float Angle);									// 繞Y軸旋轉(弧度,從Y軸正方向朝原點看順時針)矩陣
XMMATRIX XM_CALLCONV XMMatrixRotationZ(float Angle);									// 繞Z軸旋轉(弧度,從Z軸正方向朝原點看順時針)矩陣
XMMATRIX XM_CALLCONV XMMatrixRotationRollPitchYaw(float Pitch, float Yaw, float Roll);	// 按照先繞Z軸,然后X軸,最后Y軸的順序得到旋轉矩陣(弧度,逆時針)
XMMATRIX XM_CALLCONV XMMatrixRotationRollPitchYawFromVector(FXMVECTOR Angles);			// 使用向量來獲取旋轉矩陣(弧度,逆時針)
XMMATRIX XM_CALLCONV XMMatrixRotationNormal(FXMVECTOR NormalAxis, float Angle);			// 繞經過標准化的向量軸旋轉(弧度,逆時針)矩陣
XMMATRIX XM_CALLCONV XMMatrixRotationAxis(FXMVECTOR Axis, float Angle);					// 繞向量軸旋轉(弧度,逆時針)矩陣,若軸已經標准化,應該用上面的函數
XMMATRIX XM_CALLCONV XMMatrixRotationQuaternion(FXMVECTOR Quaternion);					// 用旋轉四元數構造旋轉矩陣
XMMATRIX XM_CALLCONV XMMatrixReflect(FXMVECTOR ReflectionPlane);						// 平面反射矩陣
XMMATRIX XM_CALLCONV XMMatrixShadow(FXMVECTOR ShadowPlane, FXMVECTOR LightPosition);	// 陰影矩陣

XMMATRIX XM_CALLCONV XMMatrixLookAtLH(FXMVECTOR EyePosition, FXMVECTOR FocusPosition, FXMVECTOR UpDirection);	// 觀察矩陣
XMMATRIX XM_CALLCONV XMMatrixPerspectiveFovLH(float FovAngleY, float AspectRatio, float NearZ, float FarZ);		// 透視投影矩陣

向量與矩陣的運算

接下來是常用的向量與矩陣的運算:

// 2D向量與矩陣的函數
XMVECTOR XM_CALLCONV XMVector2Transform(FXMVECTOR V, FXMMATRIX M);	// 2D向量與矩陣相乘
XMVECTOR XM_CALLCONV XMVector2TransformCoord(FXMVECTOR V, FXMMATRIX M);		// 假定要變換的是2D坐標點,矩陣相乘后對每個分量除以w,使得最后w分量為1.0f
XMVECTOR XM_CALLCONV XMVector2TransformNormal(FXMVECTOR V, FXMMATRIX M);	// 假定要變換的是2D向量,則平移變換無效,最后得到的向量w分量為0.0f

// 3D向量與矩陣的函數
XMVECTOR XM_CALLCONV XMVector3Transform(FXMVECTOR V, FXMMATRIX M);	// 3D向量與矩陣相乘
XMVECTOR XM_CALLCONV XMVector3TransformCoord(FXMVECTOR V, FXMMATRIX M);		// 假定要變換的是3D坐標點,矩陣相乘后對每個分量除以w,使得最后w分量為1.0f
XMVECTOR XM_CALLCONV XMVector3TransformNormal(FXMVECTOR V, FXMMATRIX M);	// 假定要變換的是3D向量,則平移變換無效,最后得到的向量w分量為0.0f
XMVECTOR XM_CALLCONV XMVector3Project(FXMVECTOR V, float ViewportX, float ViewportY, float ViewportWidth, float ViewportHeight, float ViewportMinZ, float ViewportMaxZ,
	FXMMATRIX Projection, CXMMATRIX View, CXMMATRIX World);		// 經過四大變換后,獲得最終在屏幕上的像素位置和深度構成的向量,即(x, y, depth, 0.0f)
XMVECTOR XM_CALLCONV XMVector3Unproject(FXMVECTOR V, float ViewportX, float ViewportY, float ViewportWidth, float ViewportHeight, float ViewportMinZ, float ViewportMaxZ,
	FXMMATRIX Projection, CXMMATRIX View, CXMMATRIX World);		// 從屏幕像素位置和深度構成的向量(x, y, depth, 0.0f)開始,進行逆變換,得到在世界的位置

// 4D向量與矩陣的函數
XMVECTOR XM_CALLCONV XMVector4Transform(FXMVECTOR V, FXMMATRIX M);	// 4D向量與矩陣相乘

雜項

這里做一些小補充。首先是弧度與角度之間的轉換函數:

inline XM_CONSTEXPR float XMConvertToRadians(float fDegrees) { return fDegrees * (XM_PI / 180.0f); }
inline XM_CONSTEXPR float XMConvertToDegrees(float fRadians) { return fRadians * (180.0f / XM_PI); }

此外,DirectXMath.h還定義了一些常用的XM_CONST常量表達式,其中XM_CONST的宏定義如下:

#define XM_CONST constexpr

XM_CONST定義的比較經常用到的常量有:

XM_CONST float XM_PI        = 3.141592654f;
XM_CONST float XM_2PI       = 6.283185307f;
XM_CONST float XM_1DIVPI    = 0.318309886f;
XM_CONST float XM_1DIV2PI   = 0.159154943f;
XM_CONST float XM_PIDIV2    = 1.570796327f;
XM_CONST float XM_PIDIV4    = 0.785398163f;

由於一般情況下XMVECTOR的產生要么是來自讀取函數,要么是來自XMVectorSet函數,而某些固定的向量如果經常使用Setter來獲取,會產生大量重復的內存讀取操作。因此在DirectXMath.h中還定義了一些有用的常向量來避免重復的讀取,這些向量的類型都為XMVECTORF32

XMGLOBALCONST XMVECTORF32 g_XMIdentityR0            = { { { 1.0f, 0.0f, 0.0f, 0.0f } } };
XMGLOBALCONST XMVECTORF32 g_XMIdentityR1            = { { { 0.0f, 1.0f, 0.0f, 0.0f } } };
XMGLOBALCONST XMVECTORF32 g_XMIdentityR2            = { { { 0.0f, 0.0f, 1.0f, 0.0f } } };
XMGLOBALCONST XMVECTORF32 g_XMIdentityR3            = { { { 0.0f, 0.0f, 0.0f, 1.0f } } };
XMGLOBALCONST XMVECTORF32 g_XMNegIdentityR0         = { { { -1.0f, 0.0f, 0.0f, 0.0f } } };
XMGLOBALCONST XMVECTORF32 g_XMNegIdentityR1         = { { { 0.0f, -1.0f, 0.0f, 0.0f } } };
XMGLOBALCONST XMVECTORF32 g_XMNegIdentityR2         = { { { 0.0f, 0.0f, -1.0f, 0.0f } } };
XMGLOBALCONST XMVECTORF32 g_XMNegIdentityR3         = { { { 0.0f, 0.0f, 0.0f, -1.0f } } };
XMGLOBALCONST XMVECTORF32 g_XMOne                   = { { { 1.0f, 1.0f, 1.0f, 1.0f } } };
XMGLOBALCONST XMVECTORF32 g_XMZero                  = { { { 0.0f, 0.0f, 0.0f, 0.0f } } };
XMGLOBALCONST XMVECTORF32 g_XMNegativeOne           = { { { -1.0f, -1.0f, -1.0f, -1.0f } } };
XMGLOBALCONST XMVECTORF32 g_XMOneHalf               = { { { 0.5f, 0.5f, 0.5f, 0.5f } } };
XMGLOBALCONST XMVECTORF32 g_XMNegativeOneHalf       = { { { -0.5f, -0.5f, -0.5f, -0.5f } } };
XMGLOBALCONST XMVECTORF32 g_XMNegativeTwoPi         = { { { -XM_2PI, -XM_2PI, -XM_2PI, -XM_2PI } } };
XMGLOBALCONST XMVECTORF32 g_XMNegativePi            = { { { -XM_PI, -XM_PI, -XM_PI, -XM_PI } } };
XMGLOBALCONST XMVECTORF32 g_XMHalfPi                = { { { XM_PIDIV2, XM_PIDIV2, XM_PIDIV2, XM_PIDIV2 } } };
XMGLOBALCONST XMVECTORF32 g_XMPi                    = { { { XM_PI, XM_PI, XM_PI, XM_PI } } };
XMGLOBALCONST XMVECTORF32 g_XMReciprocalPi          = { { { XM_1DIVPI, XM_1DIVPI, XM_1DIVPI, XM_1DIVPI } } };
XMGLOBALCONST XMVECTORF32 g_XMTwoPi                 = { { { XM_2PI, XM_2PI, XM_2PI, XM_2PI } } };
XMGLOBALCONST XMVECTORF32 g_XMReciprocalTwoPi       = { { { XM_1DIV2PI, XM_1DIV2PI, XM_1DIV2PI, XM_1DIV2PI } } };

通過調用共用體成員v就可以獲取XMVECTOR,如g_XMOne.v

DirectX11 With Windows SDK完整目錄

Github項目源碼

歡迎加入QQ群: 727623616 可以一起探討DX11,以及有什么問題也可以在這里匯報。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM