我已经为此苦苦挣扎有一段时间了。我正在尝试使用 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 函数?你能看到我的代码中明显缺少的东西吗?(如果可能的话,我会澄清,我知道这很粗糙,这是我正在开发的原型,清洁度不是重中之重)

非常感谢!

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

有帮助吗?

解决方案

这几乎是正确的。

正确的步骤是:

  • 将模型视图与投影矩阵相乘(就像您已经做的那样)。

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

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

    x < -w : Point is outside the screen (left of the viewport)
    x >  W : Point is outside the screen (right of the viewport)
    y < -w : Point is outside the screen (above the viewport)
    y >  w : Point is outside the screen (below the viewport)
    z < -w : Point is outside the screen (beyond znear)
    z >  w : Point is outside the screen (beyond zfar)
  • 将您的点投影到二维空间中。为此,将 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 表示屏幕底部。

就这样。

其他提示

我将在尼尔斯的彻底回答中添加更多想法。

  1. 不要使用双打。我不熟悉 NDS,但我怀疑它是否有任何用于双重数学的硬件。
  2. 如果您正在阅读硬件寄存器,我还怀疑模型视图和投影尚未成倍增加。我还没有看到一个硬件平台不直接使用寄存器中的完整 MVP。
  3. 寄存器中的矩阵存储顺序可能与 OpenGL 相同,也可能不同。如果不是,则需要以其他顺序进行矩阵向量相乘。
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top