2

我在使用下面的代码限制相机俯仰角(-90º 和 90º 之间)时遇到了一个大问题。这在某种程度上是对这个问题的跟进。

问题似乎在于相机旋转超过 -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º 则向下)。现在这只有时有效,其他时候我会面向另一个方向,而不是我最初看的方式。

4

3 回答 3

2

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

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

低头看你的胸膛。你的头顶实际上是向下的。如果您想从同一点看同一事物,但头顶朝上,则必须旋转它(可悲的是,这在人类上是不可行的)

问题2:同上。CrossProduct(Reference, WORLD_SKY_VECTOR) 给出了 cross(up,down) 这并不意味着什么,只是在纸上试一试。

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

  • 计算您的视图方向向量。您可靠地知道这一点,因为您有自己的方向和音调。
  • 计算你的正确向量。相同,但方向多 90°,俯仰角为 0(相机始终是水平的,即您不会弯下头)
  • 将您的向上向量计算为交叉(右,前)。

所以 :

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

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

x = cos(pitch) * sin(yaw)
y = sin(pitch)
z = cos(pitch) * cos(yaw)
于 2011-03-22T13:50:28.893 回答
1

你不能取两个平行向量的叉积。我认为这就是它失败的地方,即当 accumPitchAngle 为 ±90° 时。

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

编辑:从头开始,您想将俯仰和偏航转换为前向矢量和向上矢量gluLookAt(),对吗?那我建议:

1)使用yaw(并且yaw仅)创建一个right与地面平行的向量。
2) 交叉rightWORLD_SKY_VECTOR得到一个forward偏航正确但俯仰不正确的向量。
3)通过度数forward旋转以获得在俯仰和偏航方面都正确的前向矢量。(我认为您已经做到了这一点。) 4)交叉并获得适用于所有情况的相机矢量。rightpitch
forwardrightup

于 2011-03-22T13:20:46.890 回答
1

过了一会儿,我想出了一个非常简单明了的解决方案。就像在答案和评论中多次说过的那样,问题出在前向向量(my 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);
    }
}

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

于 2011-03-23T13:39:46.420 回答