0x00 需要用到的头文件


#include <DirectXMath>
#include <DirectXPackedVector.h> using namespace DirectX;
using namespace DirectX::PackedVector;

0x01 针对不同平台的设置


针对 x86 平台

需要启用 SSE2 指令集(Project Properties(工程属性) -> Configuration Properties(配置属性) -> C/C++ -> Code Generation(代码生成) -> Enable Enhanced Instruction Set(启用增强指令集)

针对所有平台

应当启用快速浮点模型 /fp:fast (Project Properties(工程属性) -> Configuration Properties(配置属性) -> C/C++ -> Code Generation(代码生成) -> Floating Point Model(浮点模型)

针对 x64 平台

不必开启 SSE2 指令集,因为所有的 x64 CPU 对此均有支持。

0x02 充分利用 SIMD 技术


什么是 SIMD ?

SIMD 全称 Single Instruction Multiple Data,单指令多数据流,能够复制多个操作数,并把它们打包在大型寄存器的一组指令集,可一次性获得所有操作数进行运算。

在 DirectXMath 中,核心向量类型是 XMVECTOR,它将被映射到 SIMD 硬件寄存器。在计算向量的过程中,必须通过此类型才可以充分地利用 SIMD 技术。

XMVECTOR 类型的数据需要按 16 字节对齐,这对局部变量和全局变量都是自动实现的。至于类中的数据成员,建议分别使用 XMFLOAT2、XMFLOAT3 和 XMFLOAT4 类型来加以代替。

总结:

  1. 局部变量或全局变量用 XMVECTOR 类型。
  2. 对于类中的数据成员,使用 XMFLOAT2、XMFLOAT3 和 XMFLOAT4 类型。
  3. 在运算之前,通过加载函数将 XMFLOATn 类型转换为 XMVECTOR 类型。
  4. 用 XMVECTOR 实例来进行运算。
  5. 通过存储函数将 XMVECTOR 类型转换为 XMFLOATn 类型。

0x03 加载和存储方法


使用下面的方法将数据从 XMFLOATn 类型加载到 XMVECTOR 类型:

XMVECTOR XM_CALLCONV XMLoadFloatn(const XMFLOATn *pSource);

使用下面的方法将数据从 XMVECTOR 类型存储到 XMFLOATn 类型:

void XM_CALLCONV XMStoreFloatn(XMFLOATn *pDestination, FXMVECTOR V);

如果只希望从 XMVECTOR 中得到一个向量的分量或将一个向量的分量转换为 XMVECTOR 类型,可以使用下面的方法:

float XM_CALLCONV XMVectorGetX(FXMVECTOR V);
float XM_CALLCONV XMVectorGetY(FXMVECTOR V);
float XM_CALLCONV XMVectorGetZ(FXMVECTOR V);
float XM_CALLCONV XMVectorGetW(FXMVECTOR V); XMVECTOR XM_CALLCONV XMVectorSetX(FXMVECTOR V, float x);
XMVECTOR XM_CALLCONV XMVectorSetY(FXMVECTOR V, float y);
XMVECTOR XM_CALLCONV XMVectorSetZ(FXMVECTOR V, float z);
XMVECTOR XM_CALLCONV XMVectorSetW(FXMVECTOR V, float w);

0x04 参数的传递


为了提高效率,可以将 XMVECTOR 类型的值作为函数的参数,直接传送至 SSE/SSE2 寄存器里,而不存于栈(stack)内。

这里有几个使用 XMVECTOR 参数的规则:

  1. 将约定注解 XM_CALLCONV 加在函数名前;
  2. 前 3 个 XMVECTOR 参数应当用类型 FXMVECTOR;
  3. 第 4 个 XMVECTOR 参数应当用类型 GXMVECTOR;
  4. 第 5、6 个 XMVECTOR 参数应当用类型 HXMVECTOR;
  5. 其余的 XMVECTOR 参数应当用类型 CXMVECTOR。

构造函数注意事项

在编写构造函数时,前 3 个 XMVECTOR 参数用 FXMVECTOR 类型,其余 XMVECTOR 参数则用 CXMVECTOR 类型。

0x05 常向量


XMVECTOR 类型的常量实例应当用 XMVECTORF32 类型来表示。在初始化的时候就要使用 XMVECTORF32 类型。

static const XMVECTORF32 g_vHalfVector = {0.5f, 0.5f, 0.5f, 0.5f};

XMVECTORF32 是一种按 16 字节对齐的结构体,数学库中还提供了将它转换至 XMVECTOR 类型的运算符。其定义如下:

__declspec(align(16)) struct XMVECTORF32
{
union
{
float f[4];
XMVECTOR v;
}; inline operator XMVECTOR() const { return v; }
inline operator const float*() const { return f; }
#if !defined(_XM_NO_INTRINSICS_) && defined(_XM_SSE_INTRINSICS_)
inline operator __m128i() const { return _mm_castps_si128(v); }
inline operator __m128d() const { return _mm_castps_pd(v); }
#endif
};

另外,也可以通过 XMVECTORU32 类型来创建由整型数据构成的 XMVECTOR 常向量:

static const XMVECTORU32 vGrabY = {0x00000000, 0xFFFFFFFF, 0x00000000, 0x00000000};

0x06 向量函数


DirectXMath 库除了提供常见的向量加减法和标量运算的运算符重载之外,还提供了一些函数来执行各种向量运算。

DirectXMath 库也提供一些估算方法,精度低但速度快。如:

// 返回估算值 ||V||
XMVECTOR XM_CALLCONV XMVector2LengthEst(FXMVECTOR V);

0x07 浮点数误差


在比较浮点数时,一定要注意浮点数存在的误差。我们认为相等的两个浮点数可能会存在细微的差别。为了弥补浮点数精确性上的不足,我们通过比较两个浮点数是否近似相等来加以解决。在比较时,需要定义一个非常小的常量 Epsilon。如果两个数相差小于 Epsilon,就说这两个数是近似相等的。

对此, DirectXMath 库提供了 XMVector3NearEqual 函数,用于以 Epsilon 作为容差,测试比较的向量是否相等:

// return
// abs(V1.x - V2.x) <= Epsilon.x &&
// abs(V1.y - V2.y) <= Epsilon.y &&
// abs(V1.z - V2.z) <= Epsilon.z
bool XM_CALLCONV XMVector3NearEqual(FXMVECTOR V1, FXMVECTOR V2, FXMVECTOR Epsilon);
05-11 22:33