gluProject в NDS?

Я уже давно борюсь с этим. Пытаюсь определить экранные координаты вершин в модели на экране своего NDS с помощью devKitPro. Библиотека, кажется, реализует некоторые функции 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-графике, как хотел бы. Кто-нибудь здесь знает способ, учитывая трехмерные непреобразованные координаты вершин модели, а также учитывая матрицы, которые будут к ней применяться, фактически получить экранные координаты без использования функции OpenGL gluProject? Вы видите что-то явно очевидное, чего мне не хватает в моем коде? (Я уточню, когда это будет возможно, я знаю, что это грубо, это прототип, над которым я работаю, чистота не является приоритетом)

Огромное спасибо!

PS: Насколько я понимаю, currentMatrix, который я извлекаю из регистров DS, должен давать мне комбинированную матрицу проекции, перевода и вращения, поскольку это должна быть точная матрица, которая будет использоваться. для перевода собственным оборудованием DS, по крайней мере, согласно спецификации на GBATEK. На практике, похоже, на самом деле к нему не применяются координаты проекции, что, я полагаю, как-то связано с моими проблемами. Но я не уверен, так как сам расчет проекции не дает других результатов.


person Nicholas Flynt    schedule 30.12.2009    source источник


Ответы (2)


Это почти правильно.

Правильные шаги:

  • Умножьте Modelview на матрицу Projection (как вы уже делали).

  • Расширьте свою трехмерную вершину до однородной координаты, добавив W-компонент со значением 1. Например, ваш (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)
  • Спроецируйте свою точку в 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 вашего экрана может быть перевернута. В отличие от большинства других графических API в OpenGL y=0 обозначает нижнюю часть экрана.

Это все.

person Nils Pipenbrinck    schedule 30.12.2009
comment
Ух ты! Я думал, что могу забыть некоторые шаги. Ура, спасибо, вечером проверю. ^_^ - person Nicholas Flynt; 30.12.2009
comment
+1, хотя математика области просмотра мне кажется неправильной. x' и y' находятся в диапазоне [-1 1], поэтому их необходимо вернуть к [0-1] перед умножением - person Bahbar; 31.12.2009

Я добавлю еще несколько мыслей к тщательному ответу Нильса.

  1. не используйте двойников. Я не знаком с NDS, но сомневаюсь, что у него есть аппаратное обеспечение для двойной математики.
  2. Я также сомневаюсь, что вид модели и проекция еще не умножены, если вы читаете аппаратные регистры. Мне еще предстоит увидеть аппаратную платформу, которая не использует полный MVP в регистрах напрямую.
  3. хранение матрицы в регистрах может быть или не быть в том же порядке, что и OpenGL. если это не так, умножение матрицы-вектора нужно выполнять в другом порядке.
person Bahbar    schedule 31.12.2009
comment
Первый комментарий кажется правдивым, я получал странные результаты при умножении вручную. Действительно, деление на w казалось реальным решением, но выглядит это очень странно. Вы абсолютно правы, DS не имеет аппаратной поддержки вычислений с плавающей запятой. Внутри он использует математику с фиксированной точкой, поэтому я выполнял все операции сдвига и умножения, чтобы вернуть его к двойной точности — это экспериментальный проект, и я полностью планирую использовать математическую библиотеку с фиксированной точкой в ​​финальной версии. версию по соображениям производительности. - person Nicholas Flynt; 06.01.2010