2

私はこれでしばらく苦労してきました。devKitPro を使用して、NDS の画面上のモデルの頂点の画面座標を特定しようとしています。ライブラリは OpenGL のいくつかの機能を実装しているようですが、特に gluProject 関数が欠落しているため、正確にそれを簡単に実行できると思います。

DS のレジスタに格納されている射影行列を使用して手動で画面座標を計算しようと、かなり長い間試みてきましたが、これに基づいてゼロから射影行列を構築しようとしても、うまくいきませんでした。 OpenGL のドキュメント。使用しようとしているコードは次のとおりです。

void get2DPoint(v16 x, v16 y, v16 z, float &result_x, float &result_y)
{
 //Wait for the graphics engine to be ready
 /*while (*(int*)(0x04000600) & BIT(27))
  continue;*/

 //Read in the matrix that we're currently transforming with
 double currentMatrix[4][4]; int i; 
 for (i = 0; i < 16; i++)
  currentMatrix[0][i] = 
  (double(((int*)0x04000640)[i]))/(double(1<<12));

 //Now this hurts-- take that matrix, and multiply it by the projection matrix, so we obtain
 //proper screen coordinates.
 double f = 1.0 / tan(70.0/2.0);
 double aspect = 256.0/192.0;
 double zNear = 0.1;
 double zFar = 40.0;
 double projectionMatrix[4][4] = 
 {
  { (f/aspect), 0.0, 0.0, 0.0 },
  { 0.0, f, 0.0, 0.0 },
  { 0.0, 0.0, ((zFar + zNear) / (zNear - zFar)), ((2*zFar*zNear)/(zNear - zFar)) },
  { 0.0, 0.0, -1.0, 0.0 },
 };

 double finalMatrix[4][4];
 //Ugh...
 int mx = 0; int my = 0;
 for (my = 0; my < 4; my++)
  for (mx = 0; mx < 4; mx++)
   finalMatrix[mx][my] = 
   currentMatrix[my][0] * projectionMatrix[0][mx] + 
   currentMatrix[my][1] * projectionMatrix[1][mx] + 
   currentMatrix[my][2] * projectionMatrix[2][mx] + 
   currentMatrix[my][3] * projectionMatrix[3][mx] ;

 double dx = ((double)x) / (double(1<<12));
 double dy = ((double)y) / (double(1<<12));
 double dz = ((double)z) / (double(1<<12));

 result_x = dx*finalMatrix[0][0] +  dy*finalMatrix[0][1] + dz*finalMatrix[0][2] + finalMatrix[0][3];
 result_y = dx*finalMatrix[1][0] +  dy*finalMatrix[1][1] + dz*finalMatrix[1][2] + finalMatrix[1][3];

 result_x = ((result_x*1.0) + 4.0)*32.0;
 result_y = ((result_y*1.0) + 4.0)*32.0;


 printf("Result: %f, %f\n", result_x, result_y);

} 

多くのシフトが関係しており、DS は固定小数点表記を使用して内部的に動作するため、それを double に変換して動作させる必要があります。画面に面しているフラットクワッドを使用している場合、ピクセルは完全に変換されますが、回転は不安定です。また、投影行列 (画面の幅/高さを説明するもの) を使用しているため、使用する必要がある最後の手順はまったく正しくないようです。プロジェクション マトリックスは、画面解像度へのステップアップを達成するべきではありませんか?

私はこれらすべてにかなり慣れていません。行列の数学についてはかなり理解していますが、3D グラフィックスに携わりたいほどのスキルはありません。モデルの頂点の 3D の変換されていない座標と、それに適用される行列を考慮して、OpenGL の gluProject 関数を使用せずに実際に画面座標を取得する方法を知っている人はいますか? 私のコードに欠けていることが露骨に明らかなものを見ることができますか? (可能であれば明確にします。大雑把であることは承知しています。これは私が取り組んでいるプロトタイプであり、清潔さは優先事項ではありません)

本当にありがとう!

PS: 私が理解しているように、DS のレジスタから取得した currentMatrix は、DS 自身の変換に使用される正確な行列である必要があるため、結合された投影、変換、および回転行列を提供する必要があります。ハードウェア、少なくとも GBATEK の仕様によると。実際には、実際には投影座標が適用されていないようです。これは私の問題と関係があると思います。しかし、自分で投影を計算しても異なる結果が得られないため、よくわかりません。

4

2 に答える 2

5

それはほぼ正しいです。

正しい手順は次のとおりです。

  • Modelview に射影行列を掛けます (既に行ったように)。

  • 値 1 の W コンポーネントを追加して、3D 頂点を同次座標に拡張します。たとえば、(x,y,z) ベクトルは、w = 1 で (x,y,z,w) になります。

  • このベクトルに行列の積を掛けます。行列は 4x4 で、ベクトルはサイズ 4 である必要があります。結果もサイズ 4 のベクトルになります (まだ w をドロップしないでください!)。この乗算の結果は、クリップ空間のベクトルです。参考までに: このベクトルを使用して、すでにいくつかの非常に便利なことができます: ポイントが画面上にあるかどうかをテストします。条件は以下の6つです。

    x < -w : ポイントは画面の外側 (ビューポートの左側) にあります
    x > W : ポイントが画面の外 (ビューポートの右側) にある
    y < -w : ポイントは画面の外側 (ビューポートの上) にあります
    y > w : ポイントが画面の外側 (ビューポートの下) にある
    z < -w : ポイントは画面の外にあります (znear を超えています)。
    z > w : 点が画面の外側 (zfar を超えている)
  • ポイントを 2D 空間に投影します。これを行うには、x と y を w で割ります。
  x' = x / w;
  y' = y / w;
  • 深度値 (たとえば、zbuffer に書き込まれるもの) に関心がある場合は、z を射影することもできます。
z' = z / w
  • w がゼロの場合、前の手順は機能しないことに注意してください。このケースは、ポイントがカメラの位置と等しい場合に発生します。この場合にできる最善の方法は、x' と y' をゼロに設定することです。(次のステップでポイントを画面の中央に移動します..)。

  • 最終ステップ: OpenGL ビューポート座標を取得して適用します。

  x_screen = ビューポート左 + (x' + 1) * ビューポート幅 * 0.5;
  y_screen = viewport_top + (y' + 1) * viewport_height * 0.5;
  • 重要: 画面の y 座標が上下逆になっている場合があります。OpenGL の他のほとんどのグラフィック API とは対照的に、y=0 は画面の下部を示します。

それで全部です。

于 2009-12-30T09:53:47.983 に答える
1

ニルスの徹底的な回答にさらに考えを追加します。

  1. ダブルスを使用しないでください。私は NDS に詳しくありませんが、二重演算用のハードウェアがあるとは思えません。
  2. また、ハードウェアレジスタを読み取っている場合、モデルビューと投影がまだ乗算されていないのではないかと思います。レジスタで完全な MVP を直接使用しないハードウェア プラットフォームはまだ見たことがありません。
  3. レジスタへの行列の格納は、OpenGL と同じ順序である場合とそうでない場合があります。そうでない場合は、乗算行列ベクトルを別の順序で実行する必要があります。
于 2009-12-31T11:33:03.517 に答える