c++ - DirectXMath vector 运算精度

标签 c++ xna directx direct3d directx-11

XMVector3AngleBetweenVectors 函数的结果很奇怪。考虑这段代码:

float angle = XMConvertToDegrees(XMVectorGetX(
        XMVector3AngleBetweenVectors(GMathFV(XMFLOAT3(0.0f, 100.0f, 0.0f)), 
        GMathFV(XMFLOAT3(0.0f, 200.0f, 0.0f)))));

它正在寻找两个 3D vector 之间的角度,由 XMFLOAT3 结构描述。 GMathFV 是将 XMFLOAT3 转换为 XMVECTOR 的用户定义函数,如下所示:

inline XMVECTOR GMathFV(XMFLOAT3& val)
{
    return XMLoadFloat3(&val);    
}

其他都是 directxmath.h 库。这里一切正常,结果角度为 0.00000,正如预期的那样。

但对于其他y轴值为负的 vector ,例如:

float angle = XMConvertToDegrees(XMVectorGetX(
        XMVector3AngleBetweenVectors(GMathFV(XMFLOAT3(0.0f, -100.0f, 0.0f)), 
        GMathFV(XMFLOAT3(0.0f, -99.0f, 0.0f)))));

结果是 0.0197823402,我很难称之为零角度。

请有人帮我找出问题所在。是负数精度、太接近 vector 坐标还是其他原因?

UPD:太棒了,但它为 a(0.0f, 100.0f, 0.0f) x b(0.0f, 99.0f, 0.0f) 提供 0.0197823402,但为 a( 0.0f, 101.0f, 0.0f) x b(0.0f, 100.0f, 0.0f)

最佳答案

DirectXMath 专为 32 位 float 学而设计。你看到了 floating point error升级。这是 XMVector3AngleBetweenVectors 的定义。

inline XMVECTOR XM_CALLCONV XMVector3AngleBetweenVectors(FXMVECTOR V1, FXMVECTOR V2)
{
    XMVECTOR L1 = XMVector3ReciprocalLength(V1);
    XMVECTOR L2 = XMVector3ReciprocalLength(V2);

    XMVECTOR Dot = XMVector3Dot(V1, V2);

    L1 = XMVectorMultiply(L1, L2);

    XMVECTOR CosAngle = XMVectorMultiply(Dot, L1);
    CosAngle = XMVectorClamp(CosAngle, g_XMNegativeOne.v, g_XMOne.v);

    return XMVectorACos(CosAngle);
}

在您的第一个示例中,CosAngle 等于 1.000000000

在你的第二个例子中 CosAngle 等于 0.999999940

XMVectorACos(0.999999940) = 0.000345266977

这个大误差来自 ACos 的多项式逼近。一般来说,您应该尽可能避免使用三角函数逆函数。他们又慢又吵。这是定义,因此您可以了解它的大小。

inline XMVECTOR XM_CALLCONV XMVectorACos (FXMVECTOR V)
{
    __m128 nonnegative = _mm_cmpge_ps(V, g_XMZero);
    __m128 mvalue = _mm_sub_ps(g_XMZero, V);
    __m128 x = _mm_max_ps(V, mvalue);  // |V|

    // Compute (1-|V|), clamp to zero to avoid sqrt of negative number.
    __m128 oneMValue = _mm_sub_ps(g_XMOne, x);
    __m128 clampOneMValue = _mm_max_ps(g_XMZero, oneMValue);
    __m128 root = _mm_sqrt_ps(clampOneMValue);  // sqrt(1-|V|)

    // Compute polynomial approximation
    const XMVECTOR AC1 = g_XMArcCoefficients1;
    XMVECTOR vConstants = XM_PERMUTE_PS( AC1, _MM_SHUFFLE(3, 3, 3, 3) );
    __m128 t0 = _mm_mul_ps(vConstants, x);

    vConstants = XM_PERMUTE_PS( AC1, _MM_SHUFFLE(2, 2, 2, 2) );
    t0 = _mm_add_ps(t0, vConstants);
    t0 = _mm_mul_ps(t0, x);

    vConstants = XM_PERMUTE_PS( AC1, _MM_SHUFFLE(1, 1, 1, 1) );
    t0 = _mm_add_ps(t0, vConstants);
    t0 = _mm_mul_ps(t0, x);

    vConstants = XM_PERMUTE_PS( AC1, _MM_SHUFFLE(0, 0, 0, 0) );
    t0 = _mm_add_ps(t0, vConstants);
    t0 = _mm_mul_ps(t0, x);

    const XMVECTOR AC0 = g_XMArcCoefficients0;
    vConstants = XM_PERMUTE_PS( AC0, _MM_SHUFFLE(3, 3, 3, 3) );
    t0 = _mm_add_ps(t0, vConstants);
    t0 = _mm_mul_ps(t0, x);

    vConstants = XM_PERMUTE_PS( AC0, _MM_SHUFFLE(2, 2, 2, 2) );
    t0 = _mm_add_ps(t0, vConstants);
    t0 = _mm_mul_ps(t0, x);

    vConstants = XM_PERMUTE_PS( AC0, _MM_SHUFFLE(1, 1, 1, 1) );
    t0 = _mm_add_ps(t0, vConstants);
    t0 = _mm_mul_ps(t0, x);

    vConstants = XM_PERMUTE_PS( AC0, _MM_SHUFFLE(0, 0, 0, 0) );
    t0 = _mm_add_ps(t0, vConstants);
    t0 = _mm_mul_ps(t0, root);

    __m128 t1 = _mm_sub_ps(g_XMPi, t0);
    t0 = _mm_and_ps(nonnegative, t0);
    t1 = _mm_andnot_ps(nonnegative, t1);
    t0 = _mm_or_ps(t0, t1);
    return t0;
}

关于c++ - DirectXMath vector 运算精度,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14812925/

相关文章:

c++ - 理解 char* - C++

c++ - 迭代二维 STL vector c++

c++ - 强制模板静态成员实例化

c# - 绘制等轴瓦图

opengl - XNA, 直接 X , OpenGL

c++ - 多态性是否适用于值?或者在按(基)值返回时使用派生类的 move 构造函数

Xbox 上提供 C# Marshal 类吗?

c# - 2D Passes.apply() XNA 4.0

c# - 无论如何知道屏幕何时更新/刷新(可能是 OpenGL 或 DirectX?)

c++ - 如何将着色器嵌入到 UWP-DLL 库中?