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 在内部使用定点表示法工作,我需要将其转换为双精度才能使用。我得到的结果似乎有些正确——如果我使用面向屏幕的平面四边形,像素可以完美转换,但旋转不稳定。此外,由于我要使用投影矩阵(它考虑了屏幕宽度/高度?),我需要使用的最后一步似乎根本不正确。投影矩阵不应该为我完成提高屏幕分辨率的步骤吗?

我对这一切都很陌生,我对矩阵数学有相当的了解,但我在 3D 图形方面没有我想的那么熟练。有没有人知道一种方法,给定模型顶点的 3D、非变换坐标,以及将应用于它的矩阵,在不使用 OpenGL 的 gluProject 函数的情况下实际得出屏幕坐标?你能看到我的代码中明显缺少的东西吗?(我会尽可能澄清,我知道这很粗糙,这是我正在制作的原型,清洁度不是最重要的)

非常感谢!

PS:据我了解,我从 DS 的寄存器中提取的 currentMatrix应该给我组合的投影、平移和旋转矩阵,因为它应该是 DS 自己用于平移的确切矩阵硬件,至少根据 GBATEK 的规格。实际上,它似乎实际上并没有应用投影坐标,我想这与我的问题有关。但我不确定,因为自己计算投影不会产生不同的结果。

4

2 回答 2

5

这几乎是正确的。

正确的步骤是:

  • 将 Modelview 与 Projection 矩阵相乘(正如您已经做过的那样)。

  • 通过添加值为 1 的 W 分量将 3D 顶点扩展到齐次坐标。例如,您的 (x,y,z)-向量变为 (x,y,z,w) 且 w = 1。

  • 将此向量与矩阵乘积相乘。您的矩阵应该是 4x4 和大小为 4 的向量。结果也将是一个大小为 4 的向量(不要丢弃 w !)。此乘法的结果是您在剪辑空间中的向量。仅供参考:您已经可以在这里使用此向量做一些非常有用的事情:测试该点是否在屏幕上。这六个条件是:

    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 = viewport_left + (x' + 1) * viewport_width * 0.5;
  y_screen = viewport_top + (y' + 1) * viewport_height * 0.5;
  • 重要提示:屏幕的 y 坐标可能会颠倒。与 OpenGL 中的大多数其他图形 API 不同,y=0 表示屏幕底部。

就这样。

于 2009-12-30T09:53:47.983 回答
1

我将为 Nils 的彻底回答添加更多想法。

  1. 不要使用双打。我不熟悉 NDS,但我怀疑它是否有任何用于双重数学的硬件。
  2. 如果您正在读取硬件寄存器,我也怀疑模型视图和投影尚未成倍增加。我还没有看到不直接在寄存器中使用完整 MVP 的硬件平台。
  3. 将矩阵存储到寄存器中的顺序可能与 OpenGL 的顺序相同,也可能不同。如果不是,则乘法矩阵向量需要按其他顺序完成。
于 2009-12-31T11:33:03.517 回答