У меня отлично работает 2D / 3D конвертация, как сделать перспективу - PullRequest
1 голос
/ 21 мая 2010

Хотя в контексте этого вопроса речь идет о создании 2d / 3d игры, у меня проблема сводится к некоторой математике. Хотя это 2.5D мир, давайте представим, что он только 2d для этого вопроса.

// xa: x-accent, the x coordinate of the projection
// mapP: a coordinate on a map which need to be projected
// _Dist_ values are constants for the projection, choosing them correctly will result in i.e. an isometric projection 
xa = mapP.x * xDistX + mapP.y * xDistY; 
ya = mapP.x * yDistX + mapP.y * yDistY;

xDistX и yDistX определяют угол оси x, а xDistY и yDistY определяют угол оси y на проекции (а также размер сетки, но давайте предположим, что для простоты это 1 пиксель) .

x-axis-angle = atan(yDistX/xDistX)
y-axis-angle = atan(yDistY/yDistY)

"нормальная" система координат, подобная этой

--------------- x
|
|
|
|
|
y

has values like this:
xDistX = 1;
yDistX = 0;
xDistY = 0;
YDistY = 1;

Таким образом, каждый шаг в направлении x приведет к проецированию на 1 пиксель к правому концу на 0 пикселей вниз. Каждый шаг в направлении y проекции приведет к 0 шагам вправо и 1 пикселю вниз. Выбирая правильные xDistX, yDistX, xDistY, yDistY, вы можете проецировать любую триметрическую или диметрическую систему (именно поэтому я выбрал это).

Пока все хорошо, когда это нарисовано, все получается хорошо. Если «моя система» и мышление ясны, давайте перейдем к перспективе. Я хотел добавить некоторую перспективу в эту сетку, поэтому я добавил некоторые дополнительные, как это:

camera = new MapPoint(60, 60);
dx = mapP.x - camera.x; // delta x
dy = mapP.y - camera.y; // delta y
dist = Math.sqrt(dx * dx + dy * dy); // dist is the distance to the camera, Pythagoras etc.. all objects must be in front of the camera

fac = 1 - dist / 100; // this formula determines the amount of perspective

xa = fac * (mapP.x * xDistX  + mapP.y * xDistY) ;
ya = fac * (mapP.x * yDistX + mapP.y * yDistY );

Теперь самая сложная часть ... что если у вас есть (xa, ya) точка на проекции и вы хотите вычислить исходную точку (x, y). Для первого случая (без перспективы) я нашел обратную функцию, но как это можно сделать для формулы с точки зрения. Возможно, математические навыки не совсем подходят для решения этой проблемы.

(я смутно помню, что давным-давно mathematica могла создать обратную функцию для некоторых особых случаев ... может ли она решить эту проблему? Может, кто-то попытается?)

Ответы [ 2 ]

1 голос
/ 22 мая 2010

Я думаю, что с вашим постом я могу решить проблему. Еще уточнить некоторые вопросы:

Решение проблемы в 2d действительно бесполезно, но это было сделано только для облегчения понимания проблемы (для меня и для читателей здесь). Моя программа на самом деле дает идеальную 3D-проекцию (я проверил это с помощью 3D-изображений, визуализированных с помощью Blender). Я что-то упустил из-за обратной функции. Обратная функция предназначена только для координат между 0..camera.x * 0.5 и 0 .. camera.y * 0.5. Так что в моем примере между 0 и 30. Но даже тогда у меня есть сомнения относительно моей функции.

В моей проекции ось z всегда прямая, поэтому для вычисления высоты объекта я использовал только виражингл. Но поскольку вы не можете летать или прыгать в небо, у всего есть только 2d точка. Это также означает, что когда вы пытаетесь решить x и y, значение z действительно равно 0.

Я знаю, что не каждая функция имеет инверсию, а некоторые функции имеют, но только для определенной области. Моя основная мысль во всем этом была ... если я смогу нарисовать сетку с помощью функции ... каждая точка на этой сетке будет отображаться точно в одну точку карты. Я могу прочитать координаты x и y, поэтому, если бы у меня была правильная функция, я мог бы вычислить обратное. Но нет лучшей замены, чем хорошая математика, и я очень рад, что вы нашли время, чтобы дать очень полезный ответ:).

1 голос
/ 22 мая 2010

Функция, которую вы определили, не имеет обратной. Например, пользователь 207422 уже указал, что все, что находится на расстоянии 100 единиц от камеры, будет отображено в (xa, ya) = (0,0), поэтому обратное не определено однозначно.

Что более важно, это не то, как вы рассчитываете перспективу. Обычно коэффициент масштабирования перспективы определяется как viewdist/zdist, где zdist - это перпендикулярное расстояние от камеры до объекта, а viewdist - это константа, которая представляет собой расстояние от камеры до гипотетического экрана, на который проецируется все , (См. Диаграмму здесь , но не стесняйтесь игнорировать все остальное на этой странице.) Коэффициент масштабирования, который вы используете в своем примере, не имеет такого поведения.

Вот попытка преобразовать ваш код в правильный расчет перспективы (заметьте, я не упрощаю до 2D; перспектива - это проецирование трех измерений в два, попытка упростить задачу до 2D - это бессмысленно):

camera = new MapPoint(60, 60, 10);
camera_z = camera.x*zDistX + camera.y*zDistY + camera.z*zDistz;

// viewdist is the distance from the viewer's eye to the screen in
// "world units". You'll have to fiddle with this, probably.
viewdist = 10.0;

xa = mapP.x*xDistX + mapP.y*xDistY + mapP.z*xDistZ;
ya = mapP.x*yDistX + mapP.y*yDistY + mapP.z*yDistZ;
za = mapP.x*zDistX + mapP.y*zDistY + mapP.z*zDistZ;

zdist = camera_z - za;
scaling_factor = viewdist / zdist;
xa *= scaling_factor;
ya *= scaling_factor;

Вы только вернете xa и ya из этой функции; za только для перспективного расчета. Я предполагаю, что «za-direction» указывает за пределы экрана, поэтому, если ось x предварительного проецирования указывает на зрителя, то zDistX должно быть положительным и наоборот, и аналогично для zDistY. Для триметрической проекции вы, вероятно, имели бы xDistZ==0, yDistZ<0 и zDistZ==0. Это сделает точку предварительного проецирования по оси z прямой после проекции.

Теперь плохие новости: эта функция также не имеет обратной. Любая точка (xa, ya) является изображением бесконечного числа точек (x, y, z). Но! Если вы предполагаете, что z = 0, то вы можете решить для x и y, что, возможно, достаточно хорошо.

Для этого вам понадобится некоторая линейная алгебра. Вычислить camera_x и camera_y аналогично camera_z. Это координаты камеры после преобразования. Точка на экране имеет посттрансформационные координаты (xa,ya,camera_z-viewdist). Проведите линию через эти две точки и вычислите, где in пересекается с плоскостью, натянутой на векторы (xDistX, yDistX, zDistX) и (xDistY, yDistY, zDistY). Другими словами, вам нужно решить уравнения:

x*xDistX + y*xDistY == s*camera_x + (1-s)*xa
x*yDistX + y*yDistY == s*camera_y + (1-s)*ya
x*zDistX + y*zDistY == s*camera_z + (1-s)*(camera_z - viewdist)

Это не красиво, но это будет работать.

...