5

この問題に数週間費やしましたが、適切な解決策が見つからないようで、アドバイスが必要です.

LWJGL/Java を使用して Camera クラスの作成に取り組んでおり、クォータニオンを使用してベアリング (ヨー)、ピッチ、ロールの回転を処理しています。このカメラが 3D 空間で 6 度の動きすべてを処理し、ロールすることを望みます。ベアリング、ピッチ、ロールはすべてクォータニオンです。それらを「変更」四元数に乗算し、そこから変換行列を作成します。それを float バッファーに入れ、モデルビュー マトリックスに回転マトリックスを含むバッファーを掛けます。

ベアリングとピッチの回転は正しく機能しますが、ロールを実装すると問題が発生します。主にZ軸周りの回転(ローリング)がうまくいかないようです。カメラを「回転」させると、ローカル カメラの方向軸ではなく、グローバル Z 軸を中心に回転しているように見えます。通常、クォータニオンを乗算する順序に応じて、3 つのうち 2 つを機能させることができますが、それらを一緒に機能させることはできません。

それらはすべて独立して動作するため、それらを組み合わせて回転行列を構築する方向方法に何か問題があると思います。クラス全体を貼り付けるのに問題があるため、回転に関連するメソッドと宣言を次に示します。

private final static float DEGTORAD = (float)(Math.PI/180);    

//Eye - position of the camera in the 3D world.
private Vector3f eye;

//Camera axis vectors, calculated each time reorient() is called.
//Initialized to global x, y, and z axis initially.
private Vector3f up;
private Vector3f right;
private Vector3f direction;

//Angles of rotation (in degrees)    
private float pitchAngle;
private float bearingAngle;
private float rollAngle;

private Quaternion pitch;
private Quaternion bearing;
private Quaternion roll;

private FloatBuffer viewMatrixBuffer = BufferUtils.createFloatBuffer(16);
private Quaternion currentOrientation;

...

/**
 * Change the bearing (yaw)
 * @param bearing delta in degrees
 */
public void bearing(float bearingDelta){
    bearingAngle += bearingDelta;
    if(bearingAngle > 360){
        bearingAngle -= 360;
    }else if(bearingAngle < 0){
        bearingAngle += 360;
    }
    bearing.setFromAxisAngle(new Vector4f(0f, 1f, 0f, bearingAngle * DEGTORAD));
    bearing.normalise();
}

/**
 * Change the pitch
 * @param pitch delta in degrees
 */
public void pitch(float pitchDelta){
    pitchAngle += pitchDelta;
    if(pitchAngle > 360){
        pitchAngle -= 360;
    }else if(pitchAngle < 0){
        pitchAngle += 360;
    }
    pitch.setFromAxisAngle(new Vector4f(1f, 0f, 0f, pitchAngle * DEGTORAD));
    pitch.normalise();
}

/**
 * @param initialRoll
 */
public void roll(float initialRoll) {
    rollAngle += initialRoll;
    if(rollAngle > 360){
        rollAngle -= 360;
    }else if(rollAngle < 0){
        rollAngle += 360;
    }
    roll.setFromAxisAngle(new Vector4f(0, 0, 1, rollAngle * DEGTORAD));
    roll.normalise();
}

/**
 * Change direction to focus on a certain point in the world
 * @param eye
 */
public void lookThrough(){
    reorient();
    GL11.glMultMatrix(viewMatrixBuffer);
}    

public void reorient(){
    //Multiply in order: bearing, pitch, roll.  Non-commutative!
    Quaternion change = new Quaternion();
    Quaternion.mul(bearing, pitch, change);
    Quaternion.mul(roll, change, change);
    // orient the camera...
    Matrix4f rotationMatrix = getRotationMatrix(change);

    //Get the looking direction
    direction.x = rotationMatrix.m20;
    direction.y = rotationMatrix.m21;
    direction.z = rotationMatrix.m22;

    //Set the position
    rotationMatrix.m30 = eye.x;
    rotationMatrix.m31 = eye.y;
    rotationMatrix.m32 = eye.z;
    rotationMatrix.m33 = 1;

    rotationMatrix.invert();
    rotationMatrix.store(viewMatrixBuffer);

    viewMatrixBuffer.rewind();

    Vector3f.cross(new Vector3f(0,1,0), direction, null).normalise(right);
    Vector3f.cross(right, direction, null).normalise(up);               
}

Vector3f、Quaternion、Matrix4f はすべて LWJGL クラスであり、カスタムメイドではありません。

私の質問は、ベアリング、ピッチ、ロールを表す 3 つのクォータニオンが与えられた場合、ModelView マトリックスを変更してこれらの回転を正確に表すにはどうすればよいですか?

編集:これは非常に近いと思います。RiverC のコメントの Gist リンクを参照してください。非常に多くの角度で回転した後、回転すると通常の状態に戻る前に、ビューが大きくジャンプします。要点はそこにありますが、まだ少しずれています。

4

2 に答える 2

3

これが古いことは知っていますが、とにかく推測させてください。問題はまさにあなたが自分で言ったことです:

カメラを「回転」させると、ローカル カメラの方向軸ではなく、グローバル Z 軸を中心に回転しているように見えます。

これは、ベクトル (0,0,1)、つまりグローバル Z 軸の周りを回転するように指示したためです。

これが単位四元数の機能です。ベクトル (ここではベクトルのセット、回転行列) を、ベクトルの虚部 (x,y,z) で指定された軸の周りに w スカラー (w = cos) の角度関数で回転させます。 (角度/2))。

あなたがやろうとしていること、つまり頭を左から右に傾けるときのようにカメラを回転させていることを理解したら、方向ベクトルの周りにロール四元数を構築する必要があります。

 roll.setFromAxisAngle(new Vector4f(direction.x, direction.y, direction.z, rollAngle * DEGTORAD));

方向ベクトルが正規化されているか、またはsetFromAxisAngleを呼び出すときに LWJGL が非ユニタリ軸ベクトルの処理方法を知っていると想定しています。

于 2012-04-04T01:26:24.710 に答える
3

乗算を間違った順序で実行しています。

2 つのローテーション q1 と q2 の場合、q2 が q1 に続く場合 (ローテーションは一般に非共有的であるため)、q2*q1 を乗算します。

FPS のコントロールなどのジンバル スタイルのシステムでは、優先順位は常にヨー、ピッチ、ロールです。これは、次の計算を示唆しています。

roll * pitch * yaw

Javaのポイントとして、更新ごとに新しいクォータニオンを作成しないことをお勧めしますが、とにかく

change = Quaternion.mul(Quaternion.mul(roll, pitch, change), yaw, change);

コードを見ると、3 番目のクォータニオンは結果で上書きされるだけなので、フレーム/更新ごとにリセットしたり再作成したりする必要はありません。

この回転順序はややこしいですが、Quaternions に関する Wiki ページを見ると、それが一般的なルールです。

于 2013-05-24T03:38:20.027 に答える