2

画面の左下隅に 2D 仮想ジョイスティックがあります。原点 (0, 0, 0) に 3D で描かれた球があります... 私のカメラは球を周回できます。ジョイスティックを使ってカメラを動かそうとしていますが、どうすればいいのかわかりません。ジョイスティックから軸角度回転を作成し、クォータニオンを使用して向きを表すカメラ角度を更新する必要があります。これが私が現在持っているものです:

カメラの回転はクォータニオンとして保存されます:

// The current rotation
public Quaternion rotation = new Quaternion();

// The temp quaternion for the new rotation
private Quaternion newRotation = new Quaternion();

回転とカメラは、次のメソッドを介して更新されます。

// Update the camera using the current rotation
public void update(boolean updateFrustum)
{
    float aspect = camera.viewportWidth / camera.viewportHeight;
    camera.projection.setToProjection(Math.abs(camera.near), Math.abs(camera.far), camera.fieldOfView, aspect);
    camera.view.setToLookAt(camera.position, tmp.set(camera.position).add(camera.direction), camera.up);

    // Rotate the current view matrix using our rotation quaternion
    camera.view.rotate(rotation);

    camera.combined.set(camera.projection);
    Matrix4.mul(camera.combined.val, camera.view.val);

    if (updateFrustum)
    {
        camera.invProjectionView.set(camera.combined);
        Matrix4.inv(camera.invProjectionView.val);
        camera.frustum.update(camera.invProjectionView);
    }
}

public void updateRotation(float axisX, float axisY, float axisZ, float speed)
{
    // Update rotation quaternion
    newRotation.setFromAxis(Vector3.tmp.set(axisX, axisY, axisZ), ROTATION_SPEED * speed * MathHelper.PIOVER180);
    rotation.mul(newRotation);

    // Update the camera
    update(true);
}

updateRotation()現在、次のように呼び出しています。

// Move the camera
if (joystick.isTouched)
{
    // Here is where I'm having trouble...
    // Get the current axis of the joystick to rotate around
    tmpAxis = joystick.getAxis();
    axisX = tmpAxis.X;
    axisY = tmpAxis.Y;

    // Update the camera with the new axis-angle of rotation

    // joystick.getSpeed() is just calculating the distance from 
    // the start point to current position of the joystick so that the 
    // rotation will be slower when closer to where it started and faster 
    // as it moves toward its max bounds

    controller.updateRotation(axisX, axisY, axisZ, joystick.getSpeed());
}

クラスからの私の現在のgetAxis()方法:Joystick

public Vector2d getAxis()
{
    Vector2d axis = new Vector2d(0.0f, 0.0f);
    float xOffset = 0;
    float yOffset = 0;
    float angle = getAngle();

    // Determine x offset
    xOffset = 1f;

    // Determine y offset
    yOffset = 1f;

    // Determine positive or negative x offset
    if (angle > 270 || angle < 90)
    {
        // Upper left quadrant
        axis.X = xOffset;
    }
    else
    {
        axis.X = -xOffset;
    }

    // Determine positive or negative y offset
    if (angle > 180 && angle < 360)
    {
        // Upper left quadrant
        axis.Y = yOffset;
    }
    else
    {
        axis.Y = -yOffset;
    }

    return axis;
}
4

3 に答える 3

2

おそらく、固定された軸 (up&right) のグローバル パリを中心に回転するのではなく、現在の回転状態に基づいて回転を更新する必要があります。たとえば、開始位置では、カメラは (0,0,1) ベクトルの方を向いています。

  • オブジェクトは (0,0,0) にあります
  • カムは (0,0,-2) にあります
  • cam dir は (0, 0, 1) を指します

ジョイスティックを 90 度上に押すと、カメラは次のようになります。

  • (0, 2, 0) の位置に移動
  • cam dir が (0, -1, 0) を指すようになりました

ジョイスティックを右 90 度に押すと、カメラは次のようになります。

  • 位置 (2, 0, 0) のカム
  • カム方向ベクトル (-1, 0, 0)

そうは言っても、カメラを固定位置に置いて、カメラが見ているオブジェクトを回転させることで、同じことを達成できます。

単純なアプローチの 1 つは、軸の回転ごとに 1 つずつ、一対の四元数を生成することです。ただし、回転させたい軸は、オブジェクトが回転するにつれて変化します。カメラが上向きに回転し、オブジェクトの中心を見下ろす場合、クォータニオンは、カメラが上向きに回転するように、右軸 (1, 0, 0) を中心とした x 度の回転として定義する必要があります。オブジェクトが回転してカメラが上に見えるようになると、オブジェクトの「上」ベクトルはグローバル アップではなく、前のクォータニオンによって回転された「グローバル アップ ベクトル」になります。

アプローチ:

1- フレームごとに、現在のオブジェクトの回転を使用してグローバルな上向きおよび右向きのベクトルを回転させることにより、ローカルの上向きおよび右向きのベクトルを計算します。

2-ローカルの上と右のベクトルを取得したら、ジョイスティックの移動量を読み取ります。

3- ローカル ベクトルを中心に回転する 2 つの新しい四元数を作成します。両方のクォータニオンを乗算すると、結果が現在のオブジェクトのクォータニオンに適用 (乗算) されます。

4- その最終クォータニオンをマトリックスに変換し、それを現在のモデルビュー マトリックスとして使用します。

編集:

ShadingZen クラスを使用するいくつかのコード (私は libGDX を使用しません):

class Camera{
static Vector3 mRight = new Vector3(1.f, 0.f, 0.f);
static Vector3 mUp = new Vector3(0.f, 1.f, 0.f);
static Vector3 mFront = new Vector3(0.f, 0.f, 1.f); // Maybe your front is (0, 0, -1) or (0, 1, 0)

Vector3 mLocalRight; = new Vector3(1.f, 0.f, 0.f);
Vector3 mLocalUp = new Vector3(0.f, 1.f, 0.f);
Vector3 mLocalFront = new Vector3(0.f, 0.f, 1.f);

Quaternion mRotation = new Quaternion(0.f, 0.f, 0.f, 1.f); // Identity rotation
Vector3 mCameraInitialPos = ...

...

/**
 * Compute local axis vectors given the current camera rotation 
 * tickDeltaTime is useful to prevent "jumps" in movement 

*/
private void updateRotation(){
    // Get local (rotated) vectors (current local axis)
    mLocalRight = mRotation.mul(mRight); // Rotate mRight using mRotation quaternion
    mLocalUp = mRotation.mul(mUp); 
    mLocalFront = mRotation.mul(mFront);

    Quaternion rotationAroundRightAxis = new Quaternion(mLocalRight, mJoystickAmmountY*tickDeltaTime);
    Quaternion rotationAroundUpAxis = new Quaternion(mLocalUp, mJoystickAmmountX*tickDeltaTime);

    // Chain rotations
    mRotation = mRotation.mul(rotationAroundRightAxis.mul(rotationAroundUpAxis));

    // Now mRotation contains this step or tick's rotation ammount nad past rotations
    mCameraPos = mRotation.mul(mCameraInitialPos);
}

public void update(boolean updateFrustum)
{
    updateRotation();

    float aspect = camera.viewportWidth / camera.viewportHeight;
    camera.projection.setToProjection(Math.abs(camera.near), Math.abs(camera.far), camera.fieldOfView, aspect);
    camera.view.setToLookAt(mCameraPos, mLocalFront, mLocalUp); // This probably computes your view matrix

    // Not sure if libGDX needs this or calculates internally
    camera.combined.set(camera.projection);
    Matrix4.mul(camera.combined.val, camera.view.val);

    if (updateFrustum)
    {
        camera.invProjectionView.set(camera.combined);
        Matrix4.inv(camera.invProjectionView.val);
        camera.frustum.update(camera.invProjectionView);
    }
}
}
于 2012-12-20T15:53:33.503 に答える
2

カメラを定義するには、位置、前方、上 (右 = 交差 (前方、上)) の 3 つのベクトルを使用します。次に、あなたの場合、常に (0,0,0) に向かっているので、入力でこれらの 3 つのベクトルを次のように更新するだけです。

上/下に移動:

right = cross(forward, up);
posititon = normalized(position + up*(input*zoomFactor)) * length(position); //zoomFactor might be useful if you are looking from close to reduce the move but is optional
forward = normalized((0,0,0)-position);
up = cross(right, forward);//should already be normalized Note: might be cross(forward, right)

左/右に移動:

right = cross(forward, up);
position = normalized(position + right*(input*zoomFactor)) * length(position);
forward = normalized((0,0,0)-position);
right = cross(forward, up); //you do need to update it
up = cross(right, forward); //Note: might be cross(forward, right)

左/右に傾ける:

up = normalize(up + right*input); //no zoom factor needed here

ズームイン/アウト:

position = position + forward*input;//in your case you might just want to set it to some percentage: position = position + forward*(length(position) * (input));

このアプローチは、小さな入力値の場合にのみうまく機能するはずですが、通常はそうです。入力に対する角度の変化は alpha = atan(input) になるため、入力が無限大の場合、角度は 90 度変化します。ただし、状態を簡単に保存/ロードでき、カメラの位置とコンパニオン ベクトルを手動で設定するだけです。これで、「lookAt」と呼ぶために必要なものがすべて揃いました。

于 2012-12-20T10:30:18.567 に答える
1

考えるヒントをお伝えします。完全な答えではないかもしれませんが、役立つはずです!

1- 数学的に完全な棒を持つことは本当に必要ですか? つまり、中心からの変位を検出し、そこから角度を計算できます。例。スティックは [x,y] を中心に、ユーザーの指は [x+a, y+b] を中心にすると、回転角度は func(a)、func(b) になります。ここで、func() はユーザーが定義する関数です (ほとんど常に経験的に)。例えば:

   angleX = (a/30) % 360; //you should consider maximum displacements may be

2-四元数の使用方法に注意してください。

rotation.mul(newRotation); 
rotation.leftMul(newRotation);

は同等ではありません。通常、モデルがすでに回転しているかどうかに関係なく、2 番目のオプションを使用して、指定した軸で回転したオブジェクトを回転させます。ユーザーがスティックを上に動かした場合、カメラを上に回転させます (カメラに回転が蓄積されているかどうかは気にしません)。

3-私の「安価な実装」の答えを完成させるために、LibgdxのsetEulerAngles(フロートヨー、フロートピッチ、フロートロール)を使用して、四元数を非常に簡単に計算できます。そう:

   newRotation.setEulerAngles(angleX, angleY, 0). 

3 つの軸すべてで回転が得られるように、このクォータリオンを左に乗算することを忘れないでください。

いくつかの詳細が欠落している可能性があります。setEulerAngles でパラメーターを切り替える必要があるかもしれません。カムが適切な場所を見ていない可能性がありますが、安価なソリューションが最良のソリューションである場合があることを示したかっただけです (これは 3 直線近似であるため)。少なくとも、簡単なプロトタイプには十分なはずです!

于 2012-12-21T14:44:52.027 に答える