3

我正在研究两个四元数之间的 Slerp 插值方法的两个不同来源。它们非常相似,除了一个显着的区别:一个将点积限制在 0 和 1 之间,另一个将点积限制在 -1 和 1 之间。是其中之一:

glm::fquat Slerp(const glm::fquat &v0, const glm::fquat &v1, float alpha)
{
    float dot = glm::dot(v0, v1);

    const float DOT_THRESHOLD = 0.9995f;
    if (dot > DOT_THRESHOLD)
        return Lerp(v0, v1, alpha);

    glm::clamp(dot, -1.0f, 1.0f); //<-- The line in question
    float theta_0 = acosf(dot);
    float theta = theta_0*alpha;

    glm::fquat v2 = v1 - v0*dot;
    v2 = glm::normalize(v2);

    return v0*cos(theta) + v2*sin(theta);
}

这是另一个:

template <typename T>
inline QuaternionT<T> QuaternionT<T>::Slerp(T t, const QuaternionT<T>& v1) const
{
    const T epsilon = 0.0005f;
    T dot = Dot(v1);

    if (dot > 1 - epsilon) {
        QuaternionT<T> result = v1 + (*this - v1).Scaled(t);
        result.Normalize();
        return result;
    }

    if (dot < 0) //<-The lower clamp
        dot = 0;

    if (dot > 1)
        dot = 1;

    T theta0 = std::acos(dot);
    T theta = theta0 * t;

    QuaternionT<T> v2 = (v1 - Scaled(dot));
    v2.Normalize();

    QuaternionT<T> q = Scaled(std::cos(theta)) + v2.Scaled(std::sin(theta));
    q.Normalize();
    return q;
}

我认为值得注意的Lerp是第二个算法似乎不适用于所有情况?

我只是想要一些关于这些差异的反馈,以及它们是否真的很重要。

4

1 回答 1

4

如果您slerp在两个四元数q1和之间进行q2操作,即使您在将它们传递给函数之前对它们都进行了归一化,浮点差异也会导致它们的内积最终略大于一或小于负一。这会导致acos崩溃。当然,在您提供的两个代码片段中,如果dot(q1,q2)>1,代码会进行线性插值并立即返回。所以钳位到 +1 是不必要的。Lerp在第二个示例中,我没有看到任何特别的问题。

对于这两种情况,通常都不需要钳位到 0 或 -1,而且可能是个坏主意。

主要的是,如果两个四元数的内积为负,则在它们之间进行插值意味着要走很长的路。为了澄清,如果两条线之间的角度是 30 度,您也可以通过旋转 330 度将一条线插入到另一条线上。方向空间也是如此。

单位四元数是方向的 2x 冗余表示;因此,如果两个四元数之间的内积小于零,则通常在插值之前否定其中一个的所有元素。

如果您确实想进行很长的插值,则钳制到 -1 是正确的。将点积钳制为零会非常糟糕。如果您不想进行长时间的插值,则在将四元数传递给这些函数中的任何一个之前,您应该始终确保非负内积,因为它们不会为您执行此操作。所以钳制为零是不必要的。

于 2013-06-21T20:26:17.223 回答