不能使用矢量将OpenGL中的摄像机俯仰角限制在[-90º,90º]之间!

时间:2011-03-22 13:10:58

标签: opengl vector camera rotation angle

我在使用下面的代码限制摄像机俯仰角(在-90º和90º之间)时遇到了很大的问题。这有点是this question的后续行动。

问题似乎是相机旋转超过-90º或超过+90º,当发生这种情况时,我会向下看(或向上看),但视线只围绕Y轴旋转180º。

示例:我正朝北看地平线,然后我开始往下看,直到我不能再往下走了(受下面的代码限制)。然后我开始仰视,我将面向南方。

void Camera::Rotate(Vector3D angle) {
    angle = angle * CAMERA_ROTATION_SPEED;

    accumPitchAngle += angle.x;

    if(accumPitchAngle > 90.0f) {
        angle.x = 90.0f - (accumPitchAngle - angle.x);
        accumPitchAngle = 90.0f;
    }

    if(accumPitchAngle < -90.0f) {
        angle.x = -90.0f - (accumPitchAngle - angle.x);
        accumPitchAngle = -90.0f;
    }

    // Rotate along the WORLD_SKY_VECTOR axis (yaw/heading rotation)
    // WORLD_SKY_VECTOR = (0.0f, 1.0f, 0.0f)
    if(angle.y != 0.0f) {
        Reference = RotateArbitraryAxis(Reference, WORLD_SKY_VECTOR, angle.y);
        RightVector = Vector3D::CrossProduct(Reference, WORLD_SKY_VECTOR);
        UpVector = Vector3D::CrossProduct(RightVector, Reference);
    }

    // Rotate along the x axis (pitch rotation)
    if(angle.x != 0.0f) {
        Reference = RotateArbitraryAxis(Reference, RightVector, angle.x);
        UpVector = Vector3D::CrossProduct(RightVector, Reference);
    }

    // Makes sure all vectors are perpendicular all the time
    Reference.Normalize();
    RightVector = Vector3D::CrossProduct(Reference, UpVector);
    RightVector.Normalize();
    UpVector = Vector3D::CrossProduct(RightVector, Reference);
    UpVector.Normalize();
}

Vector3D Camera::RotateArbitraryAxis(const Vector3D v, Vector3D u, double angle) {
    Vector3D result;

    u.Normalize();

    double scalar = Vector3D::DotProduct(v, u);
    double c = cos(Math::DegreesToRadians(angle));
    double s = sin(Math::DegreesToRadians(angle));
    double a = 1.0f - c;

    result.x = u.x * scalar * a + (v.x * c) + ((-u.z * v.y) + (u.y * v.z)) * s;
    result.y = u.y * scalar * a + (v.y * c) + (( u.z * v.x) - (u.x * v.z)) * s;
    result.z = u.z * scalar * a + (v.z * c) + ((-u.y * v.x) + (u.x * v.y)) * s;

    return result;
}

问题可能在if(angle.y != 0.0f)语句中,如果我对该代码块进行评论,则该问题根本不存在。它与WORLD_SKY_VECTOR有关,但该代码就像允许我旋转标题并保持相机水平。如果我使用UpVector代替,问题就解决了。但这只适用于飞行模拟器,我需要保持地平线,这就是WORLD_SKY_VECTOR背后的原因。但是,当我直接指向相机时,似乎是“侧面切换”的原因。

根据以下评论的要求......这是第一人(和第三人,但我还没有开始实施那部分)相机,当我直视,-90º(或直线上升,+ 90º)当角度从-89º到-91º(或+89º到+91º)时,我希望相机防止这种情况发生,并且不要超过-90º,+90º限制。当它达到那个极限时,我需要相机能够返回(如果我在-90º则向上,如果我在+90º则向下)。现在这只会有效,有时我会面对另一种方式,而不是我最初看的方式。

3 个答案:

答案 0 :(得分:2)

问题1:代码belew“//确保所有矢量始终垂直”确保您的UP摄像机矢量实际上是正确的(相对于WORLD_SKY_VECTOR)。所以:

  • 当你完全失望时,“向上”并不意味着很多。在你的情况下,你的camera_up应该朝北,而不是天空
  • 当你继续转弯时,经过这一点,相机会旋转以保持其向上移动。

低头看你的胸部。你的头顶实际上已经下降了。如果你想从同一个角度看同样的事情,但是你的头顶向上,你必须旋转它(不可能,不可思议)

问题2:与上述相同。 CrossProduct(参考,WORLD_SKY_VECTOR)给出了交叉(向上,向下),这并不意味着什么,只是尝试在纸上。

如果您希望能够“颠倒”:

  • 计算视图方向向量。你可靠地知道,因为你有你的方向和你的投球。
  • 计算你的右矢量。它是相同的,但方向为90°,0°(相机始终是水平的,即你不会弯曲头部)
  • 将您的向上矢量计算为交叉(右,前)。

所以:

yaw += whatever;
pitch += whatever;
FrontVector = SphericalToCartesian(yaw, pitch);
RightVector = SphericalToCartesian(yaw + PI/2.0f, 0.0);
UpVector = cross(RightVector, FrontVector);

使用SphericalToCartesian,如if(pitch = 0,yaw = 0)意味着向南看:

x = cos(pitch) * sin(yaw)
y = sin(pitch)
z = cos(pitch) * cos(yaw)

答案 1 :(得分:1)

您无法获取两个平行向量的叉积。我认为这是失败的地方,即当accumPitchAngle为±90°时。

您可能希望将其限制为-89.999°〜+ 89.999°。

编辑:要从头开始,您要转换音高和音高。偏向gluLookAt()的前向矢量和向上矢量,对吧?然后我建议:

1)使用yaw(仅限yaw)创建一个与地面平行的right向量。
2)交叉rightWORLD_SKY_VECTOR以获得偏航正确但不在俯仰中的forward向量。
3)将forwardright旋转至pitch度,以获得在俯仰和偏航方面均正确的前向矢量。 (我想你已经走到了这一步。)
4)交叉forwardright以获取适用于所有情况的相机up向量。

答案 2 :(得分:1)

在我提出一个非常简单明了的解决方案之后。就像在答案和评论中多次说过的那样,问题出在前向向量(我的Reference)向上或向下看,这意味着这个向量与WORLD_SKY_VECTOR平行,这就是它的原因变得一团糟。

我在这个解决方案背后的想法是,当我直视(或向上)并且我想向左或向右旋转时,这实际上是围绕前向矢量的滚动旋转。当俯仰角为-90º或90°时,为什么不执行侧倾运动?

把它放在一起,我通过简单地用以下代码替换偏航/航向旋转来解决问题:

if(angle.y != 0.0f) {
    if(abs(accumPitchAngle) == 90.0f) {
        RightVector = RotateArbitraryAxis(RightVector, Reference, -angle.y);
        UpVector = Vector3D::CrossProduct(RightVector, Reference);
    } else {
        Reference = RotateArbitraryAxis(Reference, WORLD_SKY_VECTOR, angle.y);
        RightVector = Vector3D::CrossProduct(Reference, WORLD_SKY_VECTOR);
        UpVector = Vector3D::CrossProduct(RightVector, Reference);
    }
}

这似乎是一个相当简单和直接的工作解决方案,我可能会将我的答案标记为已被接受,除非有人指出任何针对此实现的强烈问题,我可能不会考虑。我会等几个小时才能做到这一点,以便之前的答案得出某种结论。