У меня есть свое 2D/3D преобразование, работающее отлично, как сделать перспективу

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

// 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, можно спроектировать любую ромбическую или тетрагональную систему (который является, почему я выбрал это).

Пока неплохо, когда это оттянуто, все складывается хорошо. Если "моя система" и мышление ясны, позволяет движению к перспективе. Я хотел добавить некоторую перспективу к этой сетке, таким образом, я добавил некоторый extra's как это:

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, мог создать обратную функцию для некоторых особых случаев... она могла решить эту проблему? Кто-то мог, возможно, попробовать?)

1
задан jdv145 21 May 2010 в 11:36
поделиться

2 ответа

Определенная вами функция не имеет инверсии. В качестве примера, как уже указывал пользователь 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)

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

1
ответ дан 3 September 2019 в 00:24
поделиться

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

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

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

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

1
ответ дан 3 September 2019 в 00:24
поделиться
Другие вопросы по тегам:

Похожие вопросы: