XNA数学库

Posted 廉江宏

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了XNA数学库相关的知识,希望对你有一定的参考价值。

XNA Math Vectors

在direct3D 9 和10中,包含3D数学库的D3DX库支持向量和其他核心类型的计算。在direct11中,D3DX库不在包含3D数学库,取而代之的是XNA数学库。XNA利用的是特殊的硬件寄存器。在windows环境中,XNA
数学库使用SSE2(Streaming SIMD Extension 2)指令集。它使用128-bits大小的SIMD(single instruction multiple data)寄存器,可以用一个指令操作4个32-bits浮点型数据或整型数据。
例如:u + v = (ux + vx, uy + vy, uz + vz)
我们用3个加法指令将每个分量加起来。但通过使用SIMD,我们可以只用一个SIMD指令做4D向量加法,而不是用4个标量指令。如果你操作3D向量,我们仍然可以使用SIMD,
我们只需将向量的第四个分量置为0并且忽略它。在2D中类似。
为了使用XNA数学库,我们只需添加头文件#include<xnamath.h> 。当然,你也可以通过包含头文件#include<d3dx10.h>,并且链接静态库d3dx10.lib使用D3DX10数学库。


在XNA数学库中,关键的向量类型是XMVECTOR. 它是一个128-bits类型,可以被单个SIMD指令处理。
它被这样定义:

1 typedef __m128 XMVECTOR;

这里,__m128是一个特殊的SIMD类型。对于类数据成员,最好使用XMFLOAT2,XMFLOAT3,XMFLAOT4。它们被如下定义:

1 typedef struct _XMFLOAT4
2 {
3     FLOAT x;
4     FLOAT y;
5     FLOAT z;
6     FLOAT w;
7 }XMFLOAT4;

然而,如果我们直接使用这些类型进行计算,将不能利用SIMD硬件。我们需要将这些类型的实例转化为XMFLOAT类型。这些是XNA载入函数(loading functions)的工作。相反地,XNA还提供存储函数(storage functions)将
XMVECTOR转化为XMFLOAT*类型。

Loading 和 Storage 函数

Loading函数:

1 XMVECTOR XMLoadFloat2(CONST XMFLOAT2 *pSource); // 将XMFLOAT2转化为XMVECTOR类型
2 XMVECTOR XMLoadFloat3(CONST XMFLOAT3 *pSource); // 将XMFLOAT3转化为XMVECTOR类型
3 XMVECTOR XMLoadFloat4(CONST XMFLOAT4 *pSource); // 将XMFLOAT4转化为XMVECTOR类型

还有很多方法将其他类型转化为XMVECTOR类型,下面是一些例子:

1 XMVECTOR XMLoadInt3(CONST UINT* pSource);    // Loads 3-element UINT array into XMVECTOR
2 XMVECTOR XMLoadColor(CONST XMCOLOR *pSource);    // Loads XMCOLOR into XMVECTOR
3 XMVECTOR XMLoadByte4(CONST XMBYTE4 *pSource);    // Loads XMBYTE4 into XMVECTOR

Storage函数:

1 VOID XMStoreFloat2(XMFLOAT2 *pDestination,FXMVECTOR V); // 将XMVECTOR转化为XMFLOAT2类型

还有很多方法将XMVECTOR转化为其他类型,下面是一些例子:

1 VOID XMStoreInt3(UINT* pDestination, FXMVECTOR V);
2 VOID XMStoreColor(XMCOLOR* pDestination, FXMVECTOR V);
3 VOID XMStoreByte4(XMBYTE4 *pDestination, FXMVECTOR V);

有时,我们只想获取或者设置XMVECTOR的一个分量,可以使用下面的函数完成:

1 FLOAT XMVectorGetX(FXMVECTOR V);
2 XMVECTOR XMVectorSetX(FXMVECTOR V,Float x);

传递参数的规则

为了利用SIMD,将参数传递给XMVECTOR类型的函数有一些规则需要遵守,这些规则根据平台的不同而不同。尤其是32-bit Windows、64-bit Windows和XBOX 360. 为了独立于平台,
我们使用类型CXMVECTOR和FXMVECTOR传递XMVECTOR的参数。对于Windows,它们被定义如下:

1 // 32-bit Windows
2 typedef const XMVECTOR FXMVECTOR;
3 typedef const XMVECTOR& CXMVECTOR;
4 // 64-bit Windows
5 typedef const XMVECTOR& FXMVECTOR;
6 typedef const XMVECTOR& CXMVECTOR;

它们的不同点是,我们可以直接传递XMVECTOR的副本还是我们必须传递一个引用。现在,传递XMVECTOR参数的规则如下:
前三个XMVECTOR参数应该是FXMVECTOR类型,后面的为CXMVECTOR类型。

1 XMINLINE XMMATRIX XMMatrixTransformation(
2     FXMVECTOR ScalingOrigin,
3     FXMVECTOR ScalingOrientationQuaternion,
4     FXMVECTOR Scaling,
5     CXMVECTOR RotationOrigin,
6     CXMVECTOR RotationQuaternion,
7     CXMVECTOR Translation);

注意,你可以拥有非XMVECTOR类型的参数,前面的规则仍然有效:

1 XMINLINE XMMATRIX XMMatrixTransformation2D(
2 FXMVECTOR ScalingOrigin,
3 FLOAT ScalingOrientation, // FLOAT类型
4 FXMVECTOR Scaling,
5 FXMVECTOR RotationOrigin,
6 FLOAT Rotation, // FLOAT类型
7 CXMVECTOR Translation);

对于常量XMVECTOR实例,我们应该使用XMVECTORF32类型存储浮点型向量。这儿有一些例子:

1 static const XMVECTORF32 g_vZero = { 0.0f, 0.0f, 0.0f, 0.0f };
2 
3 XMVECTORF32 vRightTop = {
4 vViewFrust.RightSlope,
5 vViewFrust.TopSlope,
6 1.0f,1.0f
7 };

使用XMVECTORU32存储整型向量:

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

XNA数学库定义了一些与PI有关的常量:

1 #defineXM_PI 3.141592654f
2 #defineXM_2PI 6.283185307f
3 #defineXM_1DIVPI 0.318309886f // 1/PI
4 #defineXM_1DIV2PI 0.159154943f // 1/2PI
5 #defineXM_PIDIV2 1.570796327f // PI/2
6 #defineXM_PIDIV4 0.785398163f // PI/4

它还定义了如下内联函数用来在角度和弧度之间进行转换:

1 XMFINLINE FLOAT XMConvertToRadians(FLOAT fDegrees)
2 {
3     return fDegrees*(XM_PI/180.0f);
4 }
5 
6 XMFINLINE FLOAT XMConvertToDegrees(FLOAT fRadians)
7 {
8     return fRadians*(180.0f/XM_PI);
9 }

它也定义了最大最小宏:

1 #define XMMin(a,b) (((a) < (b)) ? (a) : (b))
2 #define XMMax(a, b) (((a) > (b)) ? (a) : (b))

XNA数学库提供了下列函数来设置XMVECTOR的内容:

1 XMVECTOR XMVectorZero(); // 返回0向量(0,0,0,0)
2 XMVECTOR XMVectorSplatOne(); // 返回向量(1, 1, 1, 1)
3 XMVECTOR XMVectorSet(FLOAT x, FLOAT y, FLOAT z,FLOAT w); // 返回向量(x, y, z, w)
4 XMVECTOR XMVectorReplicate(FLOAT s); // 返回向量(s, s, s, s)
5 XMVECTOR XMVectorSplatX(FXMVECTOR V);// 返回向量(vx, vx, vx,vx)
6 XMVECTOR XMVectorSplatY(FXMVECTOR V);// 返回向量(vy, vy, vy,vy)
7 XMVECTOR XMVectorSplatZ(FXMVECTOR V);// 返回向量(vz, vz, vz,vz)

 

一些向量计算的函数:(下面为3D的例子,2D,4D同理,只需将函数名中的3替换为2、4)

 1 XMVECTOR XMVector3Length(FXMVECTOR V);    // Returns || v ||
 2 XMVECTOR XMVector3LengthSq(FXMVECTOR V); // Returns || v ||的二次方
 3 XMVECTOR XMVector3Dot(FXMVECTOR V1, FXMVECTOR V2);    // Returns v1 • v2
 4 XMVECTOR XMVector3Cross(FXMVECTOR V1, FXMVECTOR V2);    //Returns v1 × v2
 5 XMVECTOR XMVector3Normalize(FXMVECTOR V);    // Returns v/|| v ||
 6 XMVECTOR XMVector3Orthogonal(FXMVECTOR V);    // Returns a vector orthogonal to v
 7 XMVector3AngleBetweenVectors(FXMVECTOR V1, FXMVECTOR V2);    // Returns the angle between v1 and v2
 8 VOID XMVector3ComponentsFromNormal(XMVECTOR* pParallel,XMVECTOR* pPerpendicular, FXMVECTOR V, FXMVECTORNormal);    // Returns projn(v)// Returns prepn(v)
 9 BOOL XMVector3Equal(FXMVECTOR V1, FXMVECTOR V2);    // Returns v1 = v2
10 BOOL XMVector3NotEqual(FXMVECTOR V1, FXMVECTOR V2);    // Returns v1 ≠ v2

 

还有一些估算方法,如果你不在乎精确度,而在乎速度,可以使用如下函数:

MFINLINE XMVECTOR XMVector3LengthEst(XMVECTOR V); // Returns estimated || v ||
MFINLINE XMVECTOR XMVector3NormalizeEst(XMVECTOR V); // Returns estimated v/|| v ||

众所周知,浮点数的操作结果是不精确的。
可以定义一个阙值来解决:

1 const float Epsilon = 0.001f;
2 bool Equals(float lhs, float rhs)
3 {
4 return fabs(lhs - rhs) < Epsilon ? true : false;
5 }

 

在XNA数学库中有类似的函数:XMVector3NearEqual()

1 // Returns
2 // abs(U.x – V.x) <= Epsilon.x &&
3 // abs(U.y – V.y) <= Epsilon.y &&
4 // abs(U.z – V.z) <= Epsilon.z
5 XMFINLINE BOOL XMVector3NearEqual(
6 FXMVECTOR U,
7 FXMVECTOR V,
8 FXMVECTOR Epsilon);

 

以上是关于XNA数学库的主要内容,如果未能解决你的问题,请参考以下文章

从 Maya 导入 XNA 曲线?

C#XNA鼠标的位置投射到3D平面

XNA纹理问题

什么是在XNA框架枚举中为键调用的'@'符号?

XNA“游戏循环”的实际代码在哪里?

无法理解在 monogame/xna 中“发布”代码的工作原理