gluProject на NDS?
-
22-09-2019 - |
Вопрос
Я уже давно боролся с этим.Я пытаюсь определить экранные координаты вершин модели на экране моего 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?Можете ли вы увидеть что-то совершенно очевидное, чего мне не хватает в моем коде?(Я уточню, когда это возможно, я знаю, что это грубо, я работаю над прототипом, чистота не является приоритетом)
Огромное спасибо!
ПС:Насколько я понимаю, currentMatrix, который я тяну из регистров ДС, должен дайте мне объединенную матрицу проекции, перевода и вращения, поскольку это должна быть именно та матрица, которая будет использоваться для перевода собственным оборудованием DS, по крайней мере, согласно спецификациям GBATEK.На практике, похоже, к нему не применяются координаты проекции, что, я полагаю, как-то связано с моими проблемами.Но я не уверен, поскольку расчет проекции самостоятельно не дает разных результатов.
Решение
Это почти правильно.
Правильные шаги:
Умножьте представление модели на матрицу проекции (как вы уже сделали).
Расширьте свою трехмерную вершину до однородной координаты, добавив 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)
- Спроецируйте свою точку зрения в двухмерное пространство.Для этого разделите 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 обозначает нижнюю часть экрана.
Вот и все.
Другие советы
Я добавлю еще несколько мыслей к подробному ответу Нильса.
- не используйте двойники.Я не знаком с NDS, но сомневаюсь, что у него есть какое-либо оборудование для двойной математики.
- Я также сомневаюсь, что представление модели и проекция еще не умножены, если вы читаете аппаратные регистры.Я еще не видел аппаратной платформы, которая не использовала бы полный MVP в регистрах напрямую.
- хранение матрицы в регистрах может быть в том же порядке, что и в OpenGL, а может и не быть.если это не так, то умножение матрица-вектор нужно производить в другом порядке.