前言

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,以及有什么问题也可以在这里汇报。

DirectX11 With Windows SDK--06 DirectXMath数学库的更多相关文章

  1. DX11 Without DirectX SDK--06 DirectXMath数学库

    回到 DirectX11--使用Windows SDK来进行开发 xnamath.h原本是位于DirectX SDK的一个数学库,但是现在Windows SDK包含的数学库已经抛弃掉原来的xnamat ...

  2. 粒子系统与雨的效果 (DirectX11 with Windows SDK)

    前言 最近在学粒子系统,看这之前的<<3D图形编程基础 基于DirectX 11 >>是基于Direct SDK的,而DXSDK微软已经很久没有更新过了并且我学的DX11是用W ...

  3. DirectX11 学习笔记6 - 使用D3DXMATH数学库的一个样例

    这个样例是在之前的样例基础上 .把之前d3dx10math数学库换成了最新的d3dxmath.长处就不说了.先上效果图 所有代码.以及效果文件 文件结构 所有代码: 依照上图的文件顺序 #pragma ...

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

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

  5. DirectX11 With Windows SDK--00 目录

    前言 (更新于 2019/4/10) 从第一次接触DirectX 11到现在已经有将近两年的时间了.还记得前年暑假被要求学习DirectX 11,在用龙书的源码配置项目运行环境的时候都花了好几天的时间 ...

  6. DirectX11 With Windows SDK--21 鼠标拾取

    前言 拾取是一项非常重要的技术,不论是电脑上用鼠标操作,还是手机的触屏操作,只要涉及到UI控件的选取则必然要用到该项技术.除此之外,一些类似魔兽争霸3.星际争霸2这样的3D即时战略游戏也需要通过拾取技 ...

  7. DirectX11 With Windows SDK--18 使用DirectXCollision库进行碰撞检测

    前言 在DirectX SDK中,碰撞检测的相关函数位于xnacollision.h中.但是现在,前面所实现的相关函数都已经转移到Windows SDK的DirectXCollision.h中,并且处 ...

  8. DirectX11 With Windows SDK--02 顶点/像素着色器的创建、顶点缓冲区

    前言 由于在Direct3D 11中取消了固定管线,要想绘制图形必须要了解可编程渲染管线的流程,一个能绘制出图形的渲染管线最少需要有这两个可编程着色器:顶点着色器和像素着色器. 本章会直接跳过渲染管线 ...

  9. DirectX11 With Windows SDK--04 使用DirectX Tool Kit帮助开发

    前言(2018/11/4) DXTK库现在已经不随Github项目提供,因为只用到了其中的键鼠类,已经过提取加入到后续的项目中 但是如果你需要配置DirectXTK到自己的项目当中,可以参考这篇博客进 ...

随机推荐

  1. WMware workstation中几种网络连接的说明 【转】

    博客来源:WMware workstation中几种网络连接的说明 VMware workstation中几种网络连接的说明 WMware workstation中网络连接包括,桥接模式.NAT模式. ...

  2. webstorm 的 .后缀名-tab快捷键

    if (key) {}//key.if tab if (!key) {}//key.else tab if (key != null) {}//key.notnull tab if (typeof k ...

  3. LeetCode算法题-Non-decreasing Array(Java实现)

    这是悦乐书的第283次更新,第300篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第151题(顺位题号是665).给定一个包含n个整数的数组,您的任务是通过修改最多1个元 ...

  4. tomcat报异常Invalid character found in method name. HTTP method names must be tokens

    最近监控了一下测试环境的日志,突然出现如下一个异常 由Error parsing HTTP request header可以看出是由于解析请求头出错导致的,但是它属于DEBUG级别的异常,虽然不影响系 ...

  5. 好程序员web前端开发测验之css部分

    好程序员web前端开发测验之css部分Front End Web Development Quiz CSS 部分问题与解答 Q: CSS 属性是否区分大小写? <p><font si ...

  6. [P1169] 棋盘制作 &悬线法学习笔记

    学习笔记 悬线法 最大子矩阵问题: 在一个给定的矩形中有一些障碍点,找出内部不包含障碍点的,边与整个矩形平行或重合的最大子矩形. 极大子矩型:无法再向外拓展的有效子矩形 最大子矩型:最大的一个有效子矩 ...

  7. Numpy基本操作

    NumPy:数组计算 NumPy是高性能科学计算和数据分析的基础包.它是Pandas等其他各种工具的基础 NumPy的主要功能: ndarray,一个多维数据结构,高校且节省空间 无需循环即可对整组数 ...

  8. Java语法----Java中equals和==的区别

    [正文] 平时在学Android和Java语言的时候,总是碰到“equals”和“==”这两个字符,老感觉差不多:其实还是有一些区别的,今天干脆把它们彻底弄清楚. 一.java当中的数据类型和“==” ...

  9. react的jsx语法

    在webpack.config.js中配置解析的loader { test:/\.jsx?$/, use:{ loader:"babel-loader", options:{ pr ...

  10. 软工+C(2): 分数和checklist

    // 上一篇:题目设计.点评和评分 // 下一篇:超链接 教学里,建立清晰明确的评分规则并且一开始就公布,对于教师.助教.学生都是重要的. 公布时机 在课程开始的时候,就需要确定并公布评分机制,随着课 ...