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 です (カメラは常に水平です。つまり、頭を曲げません)。
  • アップ ベクトルを cross(right,front) として計算します。

そう :

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

if (pitch=0,yaw=0) のように SphericalToCartesian beeing を使用すると、南を見ることを意味します。

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

2 つの平行なベクトルの外積を取ることはできません。それが失敗していると思います。つまり、accumPitchAngle が ±90° の場合です。

-89.999° ~ +89.999° に制限したい場合があります。

編集: 最初から始めるには、ピッチとヨーを のフォワード ベクトルとアップ ベクトルに変換しますgluLookAt()よね? それから私は提案します:

1)地面に平行なベクトルを作成するためにのみyaw使用します。2)とを 交差させて、ヨーでは正しいがピッチでは正しくないベクトルを取得します。 3)ピッチとヨーの両方で正しい前方ベクトルを取得するために、角度を介して回転します。(ここまで来たと思います。) 4)とを 交差させて、すべての場合で機能するカメラベクトルを取得します。yawright
rightWORLD_SKY_VECTORforward
forwardrightpitch
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 に答える