使用四元数方向的第一人称相机中的夹紧间距

Clamping pitch in first person camera using quaternion orientation

我正在构建一个简单的第一人称相机,它假定向上矢量始终与世界 + Z 轴匹配,即没有滚动。

我正在使用四元数来存储相机的方向。 (我试图远离欧拉角。)

现在,假设我想将相机的可能俯仰限制在 -85 到 85 度的俯仰范围内。这意味着当我向相机应用一些额外的音高时,如果音高超过上述阈值,我将需要某种逻辑来限制音高。

鉴于相机的方向存储为四元数,执行此操作的有效方法是什么?

我目前的想法是从四元数中提取正向(即look At)和右向向量。将前向向量和全局 X 或 Y 向量投影到由右向量的法线形成的平面中,然后找到这些投影向量之间的 angular 差异。每次我改变音调时,这似乎需要做很多数学运算。假设这甚至是一个有效的解决方案,我想知道是否有更好更简单的方法...

这是我当前的音调设置代码(使用 Eigen 数学库):

void Camera::pitch(float angleInRadians)
{
    if (angleInRadians != 0.0)
    {

        // TODO: Apply pitch restriction. <----------------- how?

        mRotation *= Quaternionf(AngleAxisf(-angleInRadians, Vector3f::UnitX()));
        mRotation.normalize();

        mIsViewMatrixDirty = true;
    }
}

我认为在这种情况下您不必担心四元数;你的情况是关心代表你的旋转的弧度角度,无论是俯仰,偏航还是滚动。您希望防止此角度超出范围或具有特定范围。要做到这一点很简单;您只需要一个约束或钳位函数。

template<class T>
inline void clamp( T min, T max, T &value ) {    
    if ( value < min ) {
        value = min;
        return;
    }

    if ( value > max ) {
        value = max;
    }    
} // clamp

一旦你有了这个方便的通用数学函数可供使用,你就有两个选择:要么你可以在这个函数调用中设置边界以便它是预先确定的,要么你可以修改你现有的音调函数的签名以包括两个更多值,以便调用者或用户可以适当地设置这些值。然后在你的 pitch 函数中,你只需按如下方式使用它:

void Camera::pitch( float angleInRadians ) {
    clamp( -85.0f, 85.0f, angleInRadians );

    mRotation *= Quaternionf(AngleAxisf(-angleInRadians, Vector3f::UnitX()));
    mRotation.normalize();
}

或者

void Camera::pitch( float angleInRadians, float min, float max ) {
    clamp( min, max, angleInRadians );

    mRotation *= Quaternionf(AngleAxisf(-angleInRadians, Vector3f::UnitX()));
    mRotation.normalize();
}

我认为这应该可以解决您的问题,而且它还删除了检查有效性的 if 语句。我在您的函数中省略了您的最后一条语句,因为我不确定您的 class 是否需要它,但我认为在回答您的问题时将其写在这个答案中还不够重要问题。

通过将音调作为成员变量跟踪来解决这个问题。实际方向仍然存储在四元数中。

每当我通过 pitch() 函数将音高累积到方向中或使用 lookAt(vec3 target) 函数(未在原始问题中发布)重置音高时,都会更新此音高成员变量因此。

lookAt函数中,我使用asin(direction.y())计算了音高。如果音高超出界限,则会抛出一个断言(而不是纠正它)。

pitch(float angleDelta) 函数中,如果音高增量超过 min/max 范围,则会对其进行校正。对 delta 应用适当的限制后,它被累加到四元数中。