使用四元数方向的第一人称相机中的夹紧间距
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 应用适当的限制后,它被累加到四元数中。
我正在构建一个简单的第一人称相机,它假定向上矢量始终与世界 + 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 应用适当的限制后,它被累加到四元数中。