DirectX:Vector

Tag DirectX下的博客主要用於記錄DirectX的學習過程,主要參考《DirectX 12 3D 遊戲實戰開發》。函數

Vector in DirectX

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字節對齊,其中向量類型爲XMFLOAT2XMFLOAT3XMFLOAT4。而在開啓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;

Setter函數

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; }
相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息