3

現在の視線方向からオブジェクトの中心の方向に宇宙を段階的に回転させるための Java3D メソッドを開発しようとしています。

言い換えれば、3D ユニバースをたとえば 100 の短いステップで回転させて、クリックしたオブジェクトが徐々に画面の中心に移動するように見せたいのです。

3D 回転の質問に対するさまざまな回答を StackOverflow (および Web) で確認しましたが、それらのほとんどすべては、世界自体ではなく、回転するオブジェクトに固有のものです。

また、線形代数を見直してみましたが、要件を満たす Java 固有の関数を特定するのに役立っていません。

これまでのところ、増分 XYZ 座標のセットを定義し、ループを通過するたびに lookAt() を動的に使用してみました。これはほとんど機能しますが、ある完全な回転パスから次の完全な回転パスまでの視点値を保持または取得する方法がわかりません。各回転パスは、原点を見て開始します。

また、ターゲット変換と開始変換の差を取得し、増分の数で割って (およびスケーリング値を削除して) 回転行列を定義してから、その増分回転行列を現在のビュー方向にパスを通過するたびに追加しようとしましたループ。これは、増分値 1 に対しては問題なく機能します。ただし、回転を 2 つ以上の増分に分割すると、常に「BadTransformException: ViewPlatform 上の非合同変換」エラーが生成されます。(私は Java3D API リファレンスでこの例外のわずかなドキュメントを読みました。それから私が理解できる限りウルドゥー語で書かれているかもしれません。3D コンテキスト用語の平易な英語の定義はないようです「affine」、「shear」、「congruent」、「uniform」など、Google が認識できる場所ならどこでも)。

次に、AxisAngle4d を提供し、角度 (ラジアン単位) を取得し、その角度を目的の増分に分割し、増分角度値で回転するようにコードを調整しようとしました。それは世界を回転させました、大丈夫ですが、私が選んだオブジェクトの近くにはありませんでしたし、私が見ることができるパターンにもありませんでした.

必死になって、抽出された角度で rotX と rotY (Z を端点に設定) を使用してみました。さらに、いくつかの Math.cos() および Math.sin() ラッパーを盲目的にそこに投げました。まだ喜びはありません。

私の本能は、基本が整っており、Java3D には比較的単純な解決策があることを教えてくれます。しかし、明らかに私がぶつかっている理解の壁があります。それを続けるのではなく、先に進んで、Java3D で解決策を提案できる人がいるかどうかを確認したいと思いました。コードの方が望ましいですが、線形代数の説明でコードの解決策が得られるのであれば喜んで従います。

以下は、Java の Timer メソッドを使用してローテーションの増分をスケジュールするために使用しているメソッドのコアです。助けが必要な部分は、ActionListener の直前です。おそらく、「一致しない」エラーを発生させずにユニバースを回転させるために、現在のビュー方向に(ループで)適用できるある種のインクリメンタル回転値を作成するマジックコードが行く場所です。

  private void flyRotate(double endX, double endY, double endZ)
  {
    // Rotate universe by increments until target object is centered in view
    // 
    // REQUIREMENTS
    // 1. Rotate the universe by NUMROTS increments from an arbitrary (non-origin)
    //   3D position and starting viewpoint to an ending viewpoint using the
    //   shortest path and preserving the currently defined "up" vector.
    // 2. Use the Java Timer() method to schedule the visual update for each
    //   incremental rotation.
    //
    // GLOBALS
    // rotLoop contains the integer loop counter for rotations (init'd to 0)
    // viewTransform3D contains rotation/translation for current viewpoint
    // t3d is a reusable Transform3D variable
    // vtg contains the view platform transform group
    // NUMROTS contains the number of incremental rotations to perform
    //
    // INPUTS
    // endX, endY, endZ contain the 3D position of the target object
    //
    // NOTE: Java3D v1.5.1 or later is required for the Vector3D getX(),
    //   getY(), and getZ() methods to work.

    final int delay = 20; // milliseconds between firings
    final int pause = 10; // milliseconds before starting

    // Get translation components of starting viewpoint vector
    Vector3d viewVector = new Vector3d();
    viewTransform3D.get(viewVector);
    final double startX = viewVector.getX();
    final double startY = viewVector.getY();
    final double startZ = viewVector.getZ();

    // Don't try to rotate to the location of the current viewpoint
    if (startX != endX || startY != endY || startZ != endZ)
    {
      // Get a copy of the starting view transform
      t3d = new Transform3D(viewTransform3D);

      // Define the initial eye/camera position and the "up" vector
      // Note: "up = +Y" is just the initial naive implementation
      Point3d  eyePoint = new Point3d(startX,startY,startZ);
      Vector3d upVector = new Vector3d(0.0,1.0,0.0);

      // Get target view transform
      // (Presumably something like this is necessary to get a transform
      // containing the ending rotation values.)
      Transform3D tNew = new Transform3D();
      Point3d viewPointTarg = new Point3d(endX,endY,endZ);
      tNew.lookAt(eyePoint,viewPointTarg,upVector);
      tNew.invert();

      // Get a copy of the target view transform usable by the Listener
      final Transform3D tRot = new Transform3D(tNew);

      //
      // (obtain either incremental rotation angle
      // or congruent rotation transform here)
      //

      ActionListener taskPerformer = new ActionListener()
      {
        public void actionPerformed(ActionEvent evt)
        {
          if (++rotLoop <= NUMROTS)
          {
            // Apply incremental angle or rotation transform to the
            // current view
            t3d = magic(tRot);

            // Communicate the rotation to the view platform transform group
            vtg.setTransform(t3d);
          }
          else
          {
            timerRot.stop();
            rotLoop = 0;
            viewTransform3D = t3d;
          }
        }
      };

      // Set timer for rotation steps
      timerRot = new javax.swing.Timer(delay,taskPerformer);
      timerRot.setInitialDelay(pause);
      timerRot.start();
    }
  }

これらのことでよくあることですが、一歩下がって問題を再考することで、ここで達成しようとしていることを実行するためのより良い方法があるかもしれません. そこでも建設的な提案を歓迎します。

これについてご協力いただきありがとうございます。


アップデート

目標をもう少し具体的に定義してみましょう。

多くの Sphere オブジェクトを含む Java3D ユニバースがあります。各オブジェクトをクリックして、定義済みの XYZ 座標を動的に取得できます。

いつでも、特定の XYZ 位置とビュー方向にある「カメラ」を使用して、現在表示されているすべてのオブジェクトを見ています。これらのオブジェクトは、回転行列と平行移動ベクトルを保持する変換に含まれています。

(注: オブジェクトをクリックするのとは関係なく、マウスを使用してユニバースを回転させたり移動させたりすることができます。そのため、カメラの現在の回転行列と移動ベクトルを含むビュー変換が、既知の XYZ を持つターゲット オブジェクトを指していない場合があります。コーディネート。)

カメラの変換とオブジェクトの XYZ 座標を指定して、選択したオブジェクトが画面の中央に配置されるまで、現在のカメラ位置を中心にユニバースを回転させたいと考えています。そして、これを一連の個別のインクリメンタル回転として実行したいと考えています。各回転は、選択したオブジェクトが中央に配置されるまで、表示ウィンドウで目に見える宇宙が「回転」しているように見えるようにレンダリングされます。(オブジェクトへの翻訳でこれをフォローアップしています。少なくともその部分は機能しています!)

例: カメラが原点にあり、「上」が Y 軸に沿って 1.0 で、選択されたオブジェクトが私の左に 10 単位だけ中央にあるとします。180 度の視野があると仮定すると、画面の左側と画面の上部と下部の中間に表示されている球体の半分をクリックできます。

私が言葉を言うと、選択したオブジェクトが画面の中心に来るまで、宇宙のすべての目に見えるオブジェクトが、左から右に等間隔の一連のステップ (たとえば 50 としましょう) で移動するように見えるはずです。

コーディング用語では、カメラの位置 (現在は 0,0,0) を通り、カメラの Y 軸と完全に一致する架空の線を中心にユニバースを回転させる Java3D コードを作成する必要があります。宇宙の座標系。(つまり、回転軸は、Z が常にカメラの位置の Z コンポーネントに等しい平面を通過します。)

複雑な要件は次のとおりです。

  1. カメラは、原点以外の 3D 空間のどこかに移動できます。
  2. オブジェクトは、カメラの現在の位置とビューに関して、3D 空間のどこにでも配置できます。これには、表示されているが画面から完全に外れている (視錐台の外側) ことも含まれます。
  3. 回転は最短経路をたどる必要があります。一度に 180 度を超えて宇宙を回転させないでください。
  4. 回転プロセスの最初のステップとして、目に見える宇宙の「ジャンプ」や「ねじれ」があってはなりません。つまり、現在の「上」ベクトル (ユニバースの絶対「上」ベクトルではない) を保持する必要があります。

ここで問題があります: (仮想) カメラの現在の平行移動と回転情報、およびターゲット オブジェクトのユニバース空間の XYZ 座標を保持する変換が与えられた場合、どの Java3D コードが、オブジェクトが画面中央?

おそらく、このソリューションは 2 つの部分に分かれています。まず、カメラ変換とオブジェクトの XYZ 座標のみが与えられた場合の増分回転情報を計算するための 3D 数学 (Java3D で表現)。次に、ループ カウンターがインクリメントの数と等しくなるまで、[インクリメンタル回転を現在の表示変換に適用し、画面を更新する] ループ。

私を打ち負かしているのは、その 3D 数学の部分です。現在のカメラ トランスフォームとターゲット オブジェクトの位置から、カメラ トランスフォームに適用できるインクリメンタルな回転情報を取得する方法がわかりません。少なくとも、ジャンプやねじれ、または不均等な増分移動ステップ (または「ViewPlatform 上の非合同変換」例外) を引き起こさない方法は見つかりませんでした。

簡単な解決策があるはずです....

4

1 に答える 1

0

私が正しく理解していれば、あなたの目標はカメラを回転させて選択したオブジェクトを中心にすることですが、その回転は任意のベクトルを中心にしてはならず、代わりにカメラの「上」方向を維持する必要があります。

次に機能する可能性のあるソリューション:

  1. まず、カメラが目的のオブジェクトに面するために必要な「上」ベクトルについて、回転角度 (A としましょう) を計算します。
  2. 次に、「アップ」ベクトルに沿って必要な平行移動距離/方向 (D としましょう) を計算し、オブジェクトが必要に応じてカメラと整列するようにします。これは、カメラ/オブジェクト間の Z/Y 座標の違いに過ぎない可能性があります。
  3. A/D を N で割って、dA と dD を求めます。これは、動きを滑らかにするために必要な増分の数です。
  4. タイマー/タイム ループでは、A/D を dA/dD ずつそれぞれ N 回インクリメントし、それらを最終値にします。原点ではなく、「上」ベクトルと現在の位置を中心にカメラを回転させていることに注意してください。

さらに滑らかでリアルな回転が必要な場合は、SLERPの使用を検討してください。

于 2012-05-18T00:15:49.383 に答える