使用四元数方向在第一人称相机中夹紧音高

时间:2016-01-24 01:07:55

标签: c++ opengl graphics 3d eigen

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

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

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

考虑到相机的方向存储为四元数,有什么方法可以做到这一点?

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

这是我目前的音高设置代码(使用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;
    }
}

2 个答案:

答案 0 :(得分:1)

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

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

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

一旦你可以使用这个方便的通用数学函数,你就有了两个选项:你可以在这个函数调用中设置边界,这样就可以预先确定,或者你可以修改你现有的音高函数的签名以包含两个以上的值这样调用者或用户可以适当地设置它们。然后在你的音调函数中你可以按如下方式使用它:

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语句检查有效性。我在你的函数中省略了你的最后一个陈述,因为我不确定你的课程是否需要它,但我相信在回答你的问题时,在这个答案中写出来并不重要。

答案 1 :(得分:0)

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

任何时候我通过pitch()函数在方向上累积音高或使用lookAt(vec3 target)函数重置音高(原始问题中没有发布),此音高成员变量会相应更新。

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

pitch(float angleDelta)函数中,如果音高增量超过最小/最大界限,则会更正音高增量。对delta应用适当的限制后,它会累积到四元数中。