前言

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,可以看到:

  1. typedef __m128 XMVECTOR;

而__m128是一个共用体:

  1. typedef union __declspec(intrin_type) __declspec(align(16)) __m128 {
  2. float m128_f32[4];
  3. unsigned __int64 m128_u64[2];
  4. __int8 m128_i8[16];
  5. __int16 m128_i16[8];
  6. __int32 m128_i32[4];
  7. __int64 m128_i64[2];
  8. unsigned __int8 m128_u8[16];
  9. unsigned __int16 m128_u16[8];
  10. unsigned __int32 m128_u32[4];
  11. } __m128;

可以发现,__m128是一种固有类型,并且在内存上严格要求按16字节对齐,即在内存上的地址最后一个十六进制值必须从0开始。除此之外,它还可以被表示成各种类型。在这里,内存要求对齐是因为寄存器从内存中读取或者写入数据也是直接按对齐的16字节进行的,确保快速读写。

如果需要存储向量,则应该用下面的这些类型来进行存储:

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

这些类型可以比较方便进行赋值修改,但不支持运算。

向量的存取

由于XMFLOAT3等这些用于存储的类型是不能直接用到指令集加速的。要想进行向量的运算,就需要从用于存储的变量,通过读取函数,将数据读入到XMVECTOR下面这些函数都是用于向量的读取:

  1. // 2D向量读取
  2. XMVECTOR XMLoadFloat2(const XMFLOAT2* pSource);
  3. XMVECTOR XMLoadSInt2(const XMINT2* pSource);
  4. XMVECTOR XMLoadUInt2(const XMUINT2* pSource);
  5. // 3D向量读取
  6. XMVECTOR XMLoadFloat3(const XMFLOAT3* pSource);
  7. XMVECTOR XMLoadSInt3(const XMINT3* pSource);
  8. XMVECTOR XMLoadUInt3(const XMUINT3* pSource);
  9. // 4D向量读取
  10. XMVECTOR XMLoadFloat4(const XMFLOAT4* pSource);
  11. XMVECTOR XMLoadSInt4(const XMINT4* pSource);
  12. XMVECTOR XMLoadUInt4(const XMUINT4* pSource);

调用了对向量的一些操作、运算函数后,我们需要使用存储函数将结果保存起来

  1. // 2D向量存储
  2. void XMStoreFloat2(XMFLOAT2* pDestination, FXMVECTOR V);
  3. void XMStoreSInt2(XMINT2* pDestination, FXMVECTOR V);
  4. void XMStoreUInt2(XMUINT2* pDestination, FXMVECTOR V);
  5. // 3D向量存储
  6. void XMStoreFloat3(XMFLOAT3* pDestination, FXMVECTOR V);
  7. void XMStoreSInt3(XMINT3* pDestination, FXMVECTOR V);
  8. void XMStoreUInt3(XMUINT3* pDestination, FXMVECTOR V);
  9. // 4D向量存储
  10. void XMStoreFloat4(XMFLOAT4* pDestination, FXMVECTOR V);
  11. void XMStoreSInt4(XMINT4* pDestination, FXMVECTOR V);
  12. void XMStoreUInt4(XMUINT4* pDestination, FXMVECTOR V);

向量间的运算

在转成了XMVECTOR后,就可以使用xna的数学库函数了。首先是向量重载的一些运算符:

  1. // 单目运算符
  2. XMVECTOR XM_CALLCONV operator+ (FXMVECTOR V);
  3. XMVECTOR XM_CALLCONV operator- (FXMVECTOR V);
  4. // 向量的分量运算并赋值
  5. XMVECTOR& XM_CALLCONV operator+= (XMVECTOR& V1, FXMVECTOR V2);
  6. XMVECTOR& XM_CALLCONV operator-= (XMVECTOR& V1, FXMVECTOR V2);
  7. XMVECTOR& XM_CALLCONV operator*= (XMVECTOR& V1, FXMVECTOR V2);
  8. XMVECTOR& XM_CALLCONV operator/= (XMVECTOR& V1, FXMVECTOR V2);
  9. // 向量与标量的乘除
  10. XMVECTOR& XM_CALLCONV operator*= (XMVECTOR& V, float S);
  11. XMVECTOR& XM_CALLCONV operator/= (XMVECTOR& V, float S);
  12. // 向量的分量运算
  13. XMVECTOR XM_CALLCONV operator+ (FXMVECTOR V1, FXMVECTOR V2);
  14. XMVECTOR XM_CALLCONV operator- (FXMVECTOR V1, FXMVECTOR V2);
  15. XMVECTOR XM_CALLCONV operator* (FXMVECTOR V1, FXMVECTOR V2);
  16. XMVECTOR XM_CALLCONV operator/ (FXMVECTOR V1, FXMVECTOR V2);
  17. XMVECTOR XM_CALLCONV operator* (FXMVECTOR V, float S);
  18. XMVECTOR XM_CALLCONV operator* (float S, FXMVECTOR V);
  19. XMVECTOR XM_CALLCONV operator/ (FXMVECTOR V, float S);

注意到这里有FXMVECTOR,可以查看具体的含义:

  1. // Fix-up for (1st-3rd) XMVECTOR parameters that are pass-in-register for x86, ARM, ARM64, and vector call; by reference otherwise
  2. #if ( defined(_M_IX86) || defined(_M_ARM) || defined(_M_ARM64) || _XM_VECTORCALL_ ) && !defined(_XM_NO_INTRINSICS_)
  3. typedef const XMVECTOR FXMVECTOR;
  4. #else
  5. typedef const XMVECTOR& FXMVECTOR;
  6. #endif
  7. // Fix-up for (4th) XMVECTOR parameter to pass in-register for ARM, ARM64, and x64 vector call; by reference otherwise
  8. #if ( defined(_M_ARM) || defined(_M_ARM64) || defined(_M_HYBRID_X86_ARM64) || (_XM_VECTORCALL_ && !defined(_M_IX86) ) ) && !defined(_XM_NO_INTRINSICS_)
  9. typedef const XMVECTOR GXMVECTOR;
  10. #else
  11. typedef const XMVECTOR& GXMVECTOR;
  12. #endif
  13. // Fix-up for (5th & 6th) XMVECTOR parameter to pass in-register for ARM64 and vector call; by reference otherwise
  14. #if ( defined(_M_ARM64) || defined(_M_HYBRID_X86_ARM64) || _XM_VECTORCALL_ ) && !defined(_XM_NO_INTRINSICS_)
  15. typedef const XMVECTOR HXMVECTOR;
  16. #else
  17. typedef const XMVECTOR& HXMVECTOR;
  18. #endif
  19. // Fix-up for (7th+) XMVECTOR parameters to pass by reference
  20. 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宏,观察该宏的定义:

  1. #if _XM_VECTORCALL_
  2. #define XM_CALLCONV __vectorcall
  3. #else
  4. #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

接下来列出一些可能比较常用的向量相关函数:

  1. // 用于获取向量的函数
  2. XMVECTOR XM_CALLCONV XMVectorZero(); // 返回向量(0.0f, 0.0f, 0.0f, 0.0f)
  3. XMVECTOR XM_CALLCONV XMVectorSet(float x, float y, float z, float w); // 返回向量(x, y, z, w)
  4. XMVECTOR XM_CALLCONV XMVectorReplicate(float Value); // 返回向量(Value, Value, Value, Value)
  5. XMVECTOR XM_CALLCONV XMVectorSplatX(FXMVECTOR V); // 返回向量(V.x, V.x, V.x, V.x)
  6. XMVECTOR XM_CALLCONV XMVectorSplatY(FXMVECTOR V); // 返回向量(V.y, V.y, V.y, V.y)
  7. XMVECTOR XM_CALLCONV XMVectorSplatZ(FXMVECTOR V); // 返回向量(V.z, V.z, V.z, V.z)
  8. XMVECTOR XM_CALLCONV XMVectorSplatW(FXMVECTOR V); // 返回向量(V.w, V.w, V.w, V.w)
  9. XMVECTOR XM_CALLCONV XMVectorTrueInt(); // 返回128位全1的向量
  10. XMVECTOR XM_CALLCONV XMVectorFalseInt(); // 返回128位全0的向量
  11. // 用于获取向量分量的函数
  12. float XM_CALLCONV XMVectorGetX(FXMVECTOR V); // 获取分量V.x
  13. float XM_CALLCONV XMVectorGetY(FXMVECTOR V); // 获取分量V.y
  14. float XM_CALLCONV XMVectorGetZ(FXMVECTOR V); // 获取分量V.z
  15. float XM_CALLCONV XMVectorGetW(FXMVECTOR V); // 获取分量V.w
  16. // 用于设置向量分量的函数
  17. XMVECTOR XM_CALLCONV XMVectorSetX(FXMVECTOR V, float x); // 返回向量(x, V.y, V.z, V.w)
  18. XMVECTOR XM_CALLCONV XMVectorSetY(FXMVECTOR V, float y); // 返回向量(V.x, y, V.z, V.w)
  19. XMVECTOR XM_CALLCONV XMVectorSetZ(FXMVECTOR V, float z); // 返回向量(V.x, V.y, z, V.w)
  20. XMVECTOR XM_CALLCONV XMVectorSetW(FXMVECTOR V, float w); // 返回向量(V.x, V.y, V.z, w)
  21. 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])
  22. // 用于向量比较的函数
  23. // 下面这些函数若为真,返回128位全1,否则返回128位全0
  24. XMVECTOR XM_CALLCONV XMVectorEqual(FXMVECTOR V1, FXMVECTOR V2); // 对比两个向量128位是否都相同
  25. XMVECTOR XM_CALLCONV XMVectorNotEqual(FXMVECTOR V1, FXMVECTOR V2); // 对比两个向量128位是否存在不同
  26. XMVECTOR XM_CALLCONV XMVectorGreater(FXMVECTOR V1, FXMVECTOR V2); // 对比V1四个分量是否都比V2的大
  27. XMVECTOR XM_CALLCONV XMVectorGreaterOrEqual(FXMVECTOR V1, FXMVECTOR V2);// 对比V1四个分量是否都比V2的大或相等
  28. XMVECTOR XM_CALLCONV XMVectorLess(FXMVECTOR V1, FXMVECTOR V2); // 对比V1四个分量是否都比V2的小
  29. XMVECTOR XM_CALLCONV XMVectorLessOrEqual(FXMVECTOR V1, FXMVECTOR V2); // 对比V1四个分量是否都比V2的小或相等
  30. // 用于向量分量操作的函数
  31. XMVECTOR XM_CALLCONV XMVectorMin(FXMVECTOR V1, FXMVECTOR V2); // 返回向量的每一个分量对应V1和V2分量的最小值
  32. XMVECTOR XM_CALLCONV XMVectorMax(FXMVECTOR V1, FXMVECTOR V2); // 返回向量的每一个分量对应V1和V2分量的最大值
  33. XMVECTOR XM_CALLCONV XMVectorRound(FXMVECTOR V); // 对每个分量四舍五入
  34. XMVECTOR XM_CALLCONV XMVectorFloor(FXMVECTOR V); // 对每个分量向下取整
  35. XMVECTOR XM_CALLCONV XMVectorCeiling(FXMVECTOR V); // 对每个分量向上取整
  36. XMVECTOR XM_CALLCONV XMVectorClamp(FXMVECTOR V, FXMVECTOR Min, FXMVECTOR Max); // 对每个分量限定在[Min, Max]范围
  37. XMVECTOR XM_CALLCONV XMVectorSaturate(FXMVECTOR V); // 对每个分量限定在[0.0f, 1.0f]范围
  38. XMVECTOR XM_CALLCONV XMVectorReciprocal(FXMVECTOR V); // 返回(1/V.x, 1/V.y, 1/V.z, 1/V.w)
  39. // 2D向量的函数
  40. XMVECTOR XM_CALLCONV XMVector2Dot(FXMVECTOR V1, FXMVECTOR V2); // 每个分量都是V1.x * V2.x + V1.y * V2.y
  41. XMVECTOR XM_CALLCONV XMVector2Cross(FXMVECTOR V1, FXMVECTOR V2); // 每个分量都是V1.x * V2.y - V2.x * V1.y
  42. XMVECTOR XM_CALLCONV XMVector2LengthSq(FXMVECTOR V); // 每个分量都是V.x * V.x + V.y * V.y
  43. XMVECTOR XM_CALLCONV XMVector2Length(FXMVECTOR V); // 每个分量都是sqrt(V.x * V.x + V.y * V.y)
  44. XMVECTOR XM_CALLCONV XMVector2Normalize(FXMVECTOR V); // 标准化2D向量(单位向量化)
  45. XMVECTOR XM_CALLCONV XMVector2Reflect(FXMVECTOR Incident, FXMVECTOR Normal); // 镜面反射向量
  46. XMVECTOR XM_CALLCONV XMVector2LinePointDistance(FXMVECTOR LinePoint1, FXMVECTOR LinePoint2, FXMVECTOR Point); // 每个分量都是点到直线的距离
  47. // 3D向量的函数
  48. XMVECTOR XM_CALLCONV XMVector3Dot(FXMVECTOR V1, FXMVECTOR V2); // 每个分量都是V1.x * V2.x + V1.y * V2.y + V1.z * V2.z
  49. 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)
  50. XMVECTOR XM_CALLCONV XMVector3LengthSq(FXMVECTOR V); // 每个分量都是V.x * V.x + V.y * V.y + V.z * V.z
  51. XMVECTOR XM_CALLCONV XMVector3Length(FXMVECTOR V); // 每个分量都是sqrt(V.x * V.x + V.y * V.y + V.z * V.z)
  52. XMVECTOR XM_CALLCONV XMVector3Normalize(FXMVECTOR V); // 标准化3D向量(单位向量化)
  53. XMVECTOR XM_CALLCONV XMVector3Reflect(FXMVECTOR Incident, FXMVECTOR Normal); // 镜面反射向量
  54. XMVECTOR XM_CALLCONV XMVector3LinePointDistance(FXMVECTOR LinePoint1, FXMVECTOR LinePoint2, FXMVECTOR Point); // 每个分量都是点到直线的距离
  55. // 4D向量的函数
  56. XMVECTOR XM_CALLCONV XMVector4Dot(FXMVECTOR V1, FXMVECTOR V2); // 每个分量都是V1.x * V2.x + V1.y * V2.y + V1.z * V2.z + V1.w * V2.w
  57. XMVECTOR XM_CALLCONV XMVector4LengthSq(FXMVECTOR V); // 每个分量都是V.x * V.x + V.y * V.y + V.z * V.z + V.w * V.w
  58. XMVECTOR XM_CALLCONV XMVector4Length(FXMVECTOR V); // 每个分量都是sqrt(V.x * V.x + V.y * V.y + V.z * V.z + V.w * V.w)
  59. XMVECTOR XM_CALLCONV XMVector4Normalize(FXMVECTOR V); // 标准化4D向量(单位向量化)
  60. XMVECTOR XM_CALLCONV XMVector4Reflect(FXMVECTOR Incident, FXMVECTOR Normal); // 镜面反射向量

矩阵

在xna数学库中,用于运算的矩阵类型为XMMATRIX,实际上里面是由4个XMVECTOR的数组构成的结构体。

如果需要存储矩阵,则可以使用下面这些类型:

  1. XMFLOAT3X3
  2. XMFLOAT4X3
  3. XMFLOAT4X4

矩阵的存取

要想进行矩阵的运算,就需要从用于存储的变量,通过读取函数,将数据读入到XMMATRIX。下面这些函数都是用于矩阵的读取:

  1. XMMATRIX XM_CALLCONV XMLoadFloat3x3(const XMFLOAT3X3* pSource);
  2. XMMATRIX XM_CALLCONV XMLoadFloat4x3(const XMFLOAT4X3* pSource);
  3. XMMATRIX XM_CALLCONV XMLoadFloat4x4(const XMFLOAT4X4* pSource);

如果需要存储运算得到的矩阵,则可以使用下面的函数:

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

矩阵间的运算

在转成了XMMATRIX后,就可以使用xna的数学库函数了。首先是矩阵重载的一些运算符,这些都是矩阵类内定义的函数:

  1. // 赋值
  2. XMMATRIX& operator= (const XMMATRIX& M);
  3. // 单目符号运算符
  4. XMMATRIX operator+ () const;
  5. XMMATRIX operator- () const;
  6. // 运算并赋值
  7. XMMATRIX& XM_CALLCONV operator+= (FXMMATRIX M);
  8. XMMATRIX& XM_CALLCONV operator-= (FXMMATRIX M);
  9. XMMATRIX& XM_CALLCONV operator*= (FXMMATRIX M);
  10. XMMATRIX& operator*= (float S);
  11. XMMATRIX& operator/= (float S);
  12. // 矩阵运算,注意矩阵与矩阵的乘法不是各分量相乘的
  13. XMMATRIX XM_CALLCONV operator+ (FXMMATRIX M) const;
  14. XMMATRIX XM_CALLCONV operator- (FXMMATRIX M) const;
  15. XMMATRIX XM_CALLCONV operator* (FXMMATRIX M) const;
  16. XMMATRIX operator* (float S) const;
  17. XMMATRIX operator/ (float S) const;
  18. friend XMMATRIX XM_CALLCONV operator* (float S, FXMMATRIX M);

然后是一些常用的矩阵函数:

  1. bool XM_CALLCONV XMMatrixIsNaN(FXMMATRIX M); // 矩阵的每个分量都不是一个数(NaN)
  2. bool XM_CALLCONV XMMatrixIsInfinite(FXMMATRIX M); // 矩阵的每个分量都是无穷大
  3. bool XM_CALLCONV XMMatrixIsIdentity(FXMMATRIX M); // 矩阵是否为单位向量
  4. XMMATRIX XM_CALLCONV XMMatrixMultiply(FXMMATRIX M1, CXMMATRIX M2); // 矩阵乘法
  5. XMMATRIX XM_CALLCONV XMMatrixMultiplyTranspose(FXMMATRIX M1, CXMMATRIX M2); // 矩阵乘法后转置
  6. XMMATRIX XM_CALLCONV XMMatrixTranspose(FXMMATRIX M); // 矩阵转置
  7. XMMATRIX XM_CALLCONV XMMatrixInverse(_Out_opt_ XMVECTOR* pDeterminant, _In_ FXMMATRIX M); // 矩阵求逆,可选输出行列式
  8. XMVECTOR XM_CALLCONV XMMatrixDeterminant(FXMMATRIX M); // 矩阵求行列式,每个分量都是
  9. // 将矩阵的缩放、旋转、平移分量拆出来,其中旋转分量是四元数
  10. bool XM_CALLCONV XMMatrixDecompose(_Out_ XMVECTOR *outScale, _Out_ XMVECTOR *outRotQuat, _Out_ XMVECTOR *outTrans, _In_ FXMMATRIX M);
  11. XMMATRIX XM_CALLCONV XMMatrixIdentity(); // 获取单位向量
  12. XMMATRIX XM_CALLCONV XMMatrixSet(float m00, float m01, float m02, float m03, // 设置每个分量并获取一个矩阵
  13. float m10, float m11, float m12, float m13,
  14. float m20, float m21, float m22, float m23,
  15. float m30, float m31, float m32, float m33);
  16. XMMATRIX XM_CALLCONV XMMatrixTranslation(float OffsetX, float OffsetY, float OffsetZ); // 平移矩阵
  17. XMMATRIX XM_CALLCONV XMMatrixTranslationFromVector(FXMVECTOR Offset); // 使用向量来获取平移矩阵
  18. XMMATRIX XM_CALLCONV XMMatrixScaling(float ScaleX, float ScaleY, float ScaleZ); // 缩放矩阵
  19. XMMATRIX XM_CALLCONV XMMatrixScalingFromVector(FXMVECTOR Scale); // 使用向量来获取缩放矩阵
  20. XMMATRIX XM_CALLCONV XMMatrixRotationX(float Angle); // 绕X轴旋转(弧度,从X轴正方向朝原点看顺时针)矩阵
  21. XMMATRIX XM_CALLCONV XMMatrixRotationY(float Angle); // 绕Y轴旋转(弧度,从Y轴正方向朝原点看顺时针)矩阵
  22. XMMATRIX XM_CALLCONV XMMatrixRotationZ(float Angle); // 绕Z轴旋转(弧度,从Z轴正方向朝原点看顺时针)矩阵
  23. XMMATRIX XM_CALLCONV XMMatrixRotationRollPitchYaw(float Pitch, float Yaw, float Roll); // 按照先绕Z轴,然后X轴,最后Y轴的顺序得到旋转矩阵(弧度,逆时针)
  24. XMMATRIX XM_CALLCONV XMMatrixRotationRollPitchYawFromVector(FXMVECTOR Angles); // 使用向量来获取旋转矩阵(弧度,逆时针)
  25. XMMATRIX XM_CALLCONV XMMatrixRotationNormal(FXMVECTOR NormalAxis, float Angle); // 绕经过标准化的向量轴旋转(弧度,逆时针)矩阵
  26. XMMATRIX XM_CALLCONV XMMatrixRotationAxis(FXMVECTOR Axis, float Angle); // 绕向量轴旋转(弧度,逆时针)矩阵,若轴已经标准化,应该用上面的函数
  27. XMMATRIX XM_CALLCONV XMMatrixRotationQuaternion(FXMVECTOR Quaternion); // 用旋转四元数构造旋转矩阵
  28. XMMATRIX XM_CALLCONV XMMatrixReflect(FXMVECTOR ReflectionPlane); // 平面反射矩阵
  29. XMMATRIX XM_CALLCONV XMMatrixShadow(FXMVECTOR ShadowPlane, FXMVECTOR LightPosition); // 阴影矩阵
  30. XMMATRIX XM_CALLCONV XMMatrixLookAtLH(FXMVECTOR EyePosition, FXMVECTOR FocusPosition, FXMVECTOR UpDirection); // 观察矩阵
  31. XMMATRIX XM_CALLCONV XMMatrixPerspectiveFovLH(float FovAngleY, float AspectRatio, float NearZ, float FarZ); // 透视投影矩阵

向量与矩阵的运算

接下来是常用的向量与矩阵的运算:

  1. // 2D向量与矩阵的函数
  2. XMVECTOR XM_CALLCONV XMVector2Transform(FXMVECTOR V, FXMMATRIX M); // 2D向量与矩阵相乘
  3. XMVECTOR XM_CALLCONV XMVector2TransformCoord(FXMVECTOR V, FXMMATRIX M); // 假定要变换的是2D坐标点,矩阵相乘后对每个分量除以w,使得最后w分量为1.0f
  4. XMVECTOR XM_CALLCONV XMVector2TransformNormal(FXMVECTOR V, FXMMATRIX M); // 假定要变换的是2D向量,则平移变换无效,最后得到的向量w分量为0.0f
  5. // 3D向量与矩阵的函数
  6. XMVECTOR XM_CALLCONV XMVector3Transform(FXMVECTOR V, FXMMATRIX M); // 3D向量与矩阵相乘
  7. XMVECTOR XM_CALLCONV XMVector3TransformCoord(FXMVECTOR V, FXMMATRIX M); // 假定要变换的是3D坐标点,矩阵相乘后对每个分量除以w,使得最后w分量为1.0f
  8. XMVECTOR XM_CALLCONV XMVector3TransformNormal(FXMVECTOR V, FXMMATRIX M); // 假定要变换的是3D向量,则平移变换无效,最后得到的向量w分量为0.0f
  9. XMVECTOR XM_CALLCONV XMVector3Project(FXMVECTOR V, float ViewportX, float ViewportY, float ViewportWidth, float ViewportHeight, float ViewportMinZ, float ViewportMaxZ,
  10. FXMMATRIX Projection, CXMMATRIX View, CXMMATRIX World); // 经过四大变换后,获得最终在屏幕上的像素位置和深度构成的向量,即(x, y, depth, 0.0f)
  11. XMVECTOR XM_CALLCONV XMVector3Unproject(FXMVECTOR V, float ViewportX, float ViewportY, float ViewportWidth, float ViewportHeight, float ViewportMinZ, float ViewportMaxZ,
  12. FXMMATRIX Projection, CXMMATRIX View, CXMMATRIX World); // 从屏幕像素位置和深度构成的向量(x, y, depth, 0.0f)开始,进行逆变换,得到在世界的位置
  13. // 4D向量与矩阵的函数
  14. XMVECTOR XM_CALLCONV XMVector4Transform(FXMVECTOR V, FXMMATRIX M); // 4D向量与矩阵相乘

杂项

这里做一些小补充。首先是弧度与角度之间的转换函数:

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

此外,DirectXMath.h还定义了一些常用的XM_CONST常量表达式,其中XM_CONST的宏定义如下:

  1. #define XM_CONST constexpr

XM_CONST定义的比较经常用到的常量有:

  1. XM_CONST float XM_PI = 3.141592654f;
  2. XM_CONST float XM_2PI = 6.283185307f;
  3. XM_CONST float XM_1DIVPI = 0.318309886f;
  4. XM_CONST float XM_1DIV2PI = 0.159154943f;
  5. XM_CONST float XM_PIDIV2 = 1.570796327f;
  6. XM_CONST float XM_PIDIV4 = 0.785398163f;

由于一般情况下XMVECTOR的产生要么是来自读取函数,要么是来自XMVectorSet函数,而某些固定的向量如果经常使用Setter来获取,会产生大量重复的内存读取操作。因此在DirectXMath.h中还定义了一些有用的常向量来避免重复的读取,这些向量的类型都为XMVECTORF32

  1. XMGLOBALCONST XMVECTORF32 g_XMIdentityR0 = { { { 1.0f, 0.0f, 0.0f, 0.0f } } };
  2. XMGLOBALCONST XMVECTORF32 g_XMIdentityR1 = { { { 0.0f, 1.0f, 0.0f, 0.0f } } };
  3. XMGLOBALCONST XMVECTORF32 g_XMIdentityR2 = { { { 0.0f, 0.0f, 1.0f, 0.0f } } };
  4. XMGLOBALCONST XMVECTORF32 g_XMIdentityR3 = { { { 0.0f, 0.0f, 0.0f, 1.0f } } };
  5. XMGLOBALCONST XMVECTORF32 g_XMNegIdentityR0 = { { { -1.0f, 0.0f, 0.0f, 0.0f } } };
  6. XMGLOBALCONST XMVECTORF32 g_XMNegIdentityR1 = { { { 0.0f, -1.0f, 0.0f, 0.0f } } };
  7. XMGLOBALCONST XMVECTORF32 g_XMNegIdentityR2 = { { { 0.0f, 0.0f, -1.0f, 0.0f } } };
  8. XMGLOBALCONST XMVECTORF32 g_XMNegIdentityR3 = { { { 0.0f, 0.0f, 0.0f, -1.0f } } };
  9. XMGLOBALCONST XMVECTORF32 g_XMOne = { { { 1.0f, 1.0f, 1.0f, 1.0f } } };
  10. XMGLOBALCONST XMVECTORF32 g_XMZero = { { { 0.0f, 0.0f, 0.0f, 0.0f } } };
  11. XMGLOBALCONST XMVECTORF32 g_XMNegativeOne = { { { -1.0f, -1.0f, -1.0f, -1.0f } } };
  12. XMGLOBALCONST XMVECTORF32 g_XMOneHalf = { { { 0.5f, 0.5f, 0.5f, 0.5f } } };
  13. XMGLOBALCONST XMVECTORF32 g_XMNegativeOneHalf = { { { -0.5f, -0.5f, -0.5f, -0.5f } } };
  14. XMGLOBALCONST XMVECTORF32 g_XMNegativeTwoPi = { { { -XM_2PI, -XM_2PI, -XM_2PI, -XM_2PI } } };
  15. XMGLOBALCONST XMVECTORF32 g_XMNegativePi = { { { -XM_PI, -XM_PI, -XM_PI, -XM_PI } } };
  16. XMGLOBALCONST XMVECTORF32 g_XMHalfPi = { { { XM_PIDIV2, XM_PIDIV2, XM_PIDIV2, XM_PIDIV2 } } };
  17. XMGLOBALCONST XMVECTORF32 g_XMPi = { { { XM_PI, XM_PI, XM_PI, XM_PI } } };
  18. XMGLOBALCONST XMVECTORF32 g_XMReciprocalPi = { { { XM_1DIVPI, XM_1DIVPI, XM_1DIVPI, XM_1DIVPI } } };
  19. XMGLOBALCONST XMVECTORF32 g_XMTwoPi = { { { XM_2PI, XM_2PI, XM_2PI, XM_2PI } } };
  20. 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,以及有什么问题也可以在这里汇报。

DX11 Without DirectX SDK--06 DirectXMath数学库的更多相关文章

  1. DirectX11 With Windows SDK--06 DirectXMath数学库

    前言 xnamath.h原本是位于DirectX SDK的一个数学库,但是现在Windows SDK包含的数学库已经抛弃掉原来的xnamath.h,并演变成了现在的DirectXMath.h.其实本质 ...

  2. DX11 Without DirectX SDK--使用Windows SDK来进行开发

    在看龙书(Introduction to 3D Game Programming with Directx 11)的时候,里面所使用的开发工具包为Microsoft DirectX SDK(June ...

  3. 学习笔记一:关于directx sdk的安装于一些概念

    关于directx sdk开发环境的安装: 在百度搜索了directx sdk,进入了微软的官网,下载了DXSDK_Jun10.exe 百度网盘:http://pan.baidu.com/s/1o6r ...

  4. 微软数学库XNAMATH(DirectXMath)

    这篇文章只是对着MSDN文档的一些吐槽和总结记录,个人笔记之类的 运行库与头文件 老实说,这个数学库微软还是更像蛮频繁的,我这里有的最早版本是伴随DX9的,在这个头文件里面 最近在使用DXUT,顺便也 ...

  5. DX11 Without DirectX SDK--02 渲染一个三角形

    回到 DirectX11--使用Windows SDK来进行开发 目前暂时没有写HLSL具体教程的打算,而是着重于如何做到不用DirectX SDK来进行渲染.除此之外,这里也没有使用Effects框 ...

  6. DX11 Without DirectX SDK--01 DirectX11初始化

    回到 DirectX11--使用Windows SDK来进行开发 由于个人觉得龙书里面第4章提供的Direct3D 初始化项目封装得比较好,而且DirectX SDK Samples里面的初始化程序过 ...

  7. DirectX SDK版本与Visual Studio版本

    对于刚刚接触 DirectShow 的人来说,安装配置是一个令人头疼的问题,经常出现的情况是最基本的 baseclass 就无法编译.一开始我也为此费了很大的功夫,比如说修改代码.修改编译选项使其编译 ...

  8. VS2012添加对DirectX SDK中需要文件的引用

    error LNK2019: 无法解析的外部符号 _DirectDrawCreateEx@16,该符号在函数 "int __cdecl DD_Init(int,int,int)" ...

  9. 在VS2013、VS2015下如何配置DirectX SDK的开发环境

    在Visual Studio 2013下配置DirectX SDK可以进行基于DirectX的3D大型应用程序的开发.如果在开发DirectX程序时不配置其开发环境会引起编译器报错, 下面就与大家分享 ...

随机推荐

  1. kafka原理简介并且与RabbitMQ的选择

    kafka原理简介并且与RabbitMQ的选择 kafka原理简介,rabbitMQ介绍,大致说一下区别 Kafka是由LinkedIn开发的一个分布式的消息系统,使用Scala编写,它以可水平扩展和 ...

  2. JQuery实战总结一 可编辑的表格

    JQuery视频看完了,总结学习,记得在牛腩视频中的修改新闻类别的时候也使用了这样的可编辑的表格,使用到 了ajax控制界面不再刷新,轻松解决了类别的名称的修改的问题,直接提交到数据库,这样的方式比起 ...

  3. Logistic Regression求解classification问题

    classification问题和regression问题类似,区别在于y值是一个离散值,例如binary classification,y值只取0或1. 方法来自Andrew Ng的Machine ...

  4. 更改EBS服务器域名/IP

    more: 341322.1 : How to change the hostname of an Applications Tier using AutoConfig 338003.1 : How  ...

  5. SharePoint 门户网站的图片轮播-页面定制

    这个想法是自己突然的一个想法,想想我们经常用SharePoint做门户网站,不知道你们多数项目都是怎么完成的,我们客户要求的效果都还是很严格的,所有展现起来,还是很漂亮的,但是很多时候的效果,还是难以 ...

  6. MySQL数据库存储过程动态表建立(PREPARE)

    PREPARE statement_name FROM sql_text /*定义*/ EXECUTE statement_name [USING variable [,variable...]] / ...

  7. sublime使用技巧之集成VI

    熟悉开发工具,减少多余的操作流程有助于提高开发效率,而Sublime Text 2是sublime产品的经典版本,因此本文基于Sublime Text 2讲解sublime的使用技巧. VI的主要作用 ...

  8. Day3_函数

    为啥要用到函数: 复杂度增大 组织结构不清晰 可读性差 工具就是具备某一种功能的物件,就是程序中函数的概念. 事先准备工具的过程称为函数的定义 遇到特定的场景拿来用就称为函数的调用 函数的分类: 内置 ...

  9. lua函数随记

    在大多数Lua语法分析中可以获得这些标准Lua函数. 无可争辩, 我们可以查阅Lua网站, 但是一些少了的函数被Blizzard进行了调整. 下面列出了所有Lua函数. WoW API中的Lua注意在 ...

  10. 人手一份核武器 - Hacking Team 泄露(开源)资料导览手册

    https://zhuanlan.zhihu.com/p/20102713 author:蒸米 0x00 序 事先声明本人并不是全栈安全工程师,仅仅是移动安全小菜一枚,所以对泄漏资料的分析难免会有疏忽 ...