3

2 つの四元数間の補間の Slerp メソッドの 2 つの異なるソースを見ています。それらは 1 つの注目すべき違いを除いて非常に似ています: 1 つは内積を 0 と 1 の間でクランプし、もう 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また、2番目のアルゴリズムがすべての場合に適しているとは限らないことにも注意する価値があると思いますか?

これらの違いと、それらが本当に重要であるかどうかについてのフィードバックが欲しいだけです。

4

1 に答える 1

4

slerp2 つのクォータニオンq1との間で実行している場合q2、それらを関数に渡す直前に両方を正規化しても、浮動小数点の不一致により、内積が 1 よりわずかに大きくなるか、負の 1 より小さくなる可能性があります。これはacosクラッシュします。もちろん、提供する両方のコード スニペットで、 の場合dot(q1,q2)>1、コードは線形補間を行い、すぐに戻ります。したがって、+1 へのクランプは不要です。Lerp2 番目の例では特に問題は見られません。

どちらの場合も、0 または -1 へのクランプは通常不要であり、悪い考えかもしれません。

重要なことは、2 つの四元数の内積が負の場合、それらの間を補間することは長い道のりを意味するということです。明確にするために、2 つの線の間の角度が 30 度の場合、330 度回転することにより、1 つの線を別の線に補間することもできます。オリエンテーション スペースについても同様です。

単位クォータニオンは、方向の 2 倍の冗長表現です。したがって、2 つの四元数間の内積がゼロ未満の場合、通常は補間する前に、そのうちの 1 つのすべての要素を否定します。

長い間補間したい場合は、-1 にクランプするのが正しいです。内積をゼロに固定すると、事態がかなり悪化します。長い間補間したくない場合は、クォータニオンをこれらの関数のいずれかに渡す前に、内積が負でないことを常に確認する必要があります。したがって、ゼロにクランプする必要はありません。

于 2013-06-21T20:26:17.223 に答える