1

尽管这个问题的背景是关于制作 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可以为某些特殊情况创建反函数......它可以解决这个问题吗?有人可以试试吗?)

4

2 回答 2

1

您定义的函数没有逆函数。举个例子,正如 user207422 已经指出的那样,距离相机 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和返回;仅用于透视计算。我假设“za 方向”指向屏幕外,所以如果预投影 x 轴指向观察者,那么应该是正的,反之亦然,对于. 对于三角投影,您可能会有、和。这将使投影前的 z 轴点在投影后笔直向上。yazazDistXzDistYxDistZ==0yDistZ<0zDistZ==0

现在有个坏消息:这个函数也没有逆函数。任何点 (xa,ya) 都是无限个点 (x,y,z) 的图像。但!如果您假设 z=0,那么您可以求解 x 和 y,这可能已经足够好了。

为此,您必须做一些线性代数。计算camera_xcamera_y类似camera_z。那是相机的变换后坐标。屏幕上的点具有变换后坐标(xa,ya,camera_z-viewdist)(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)

它不漂亮,但它会起作用。

于 2010-05-22T08:00:43.213 回答
1

我认为通过您的帖子,我可以解决问题。不过,要澄清一些问题:

在 2d 中解决问题确实没有用,但这只是为了让问题更容易理解(对我和这里的读者来说)。我的程序实际上给出了一个完美的 3d 投影(我用搅拌器渲染的 3d 图像检查了它)。不过,我确实遗漏了一些关于反函数的内容。反函数仅适用于 0..camera.x * 0.5 和 0..camera.y*0.5 之间的坐标。因此,在我的示例中,介于 0 到 30 之间。但即便如此,我仍然对我的功能表示怀疑。

在我的投影中,z 轴始终是直向上的,因此为了计算对象的高度,我只使用了 viewuwingangle。但是由于您实际上不能飞或跳入天空,所以一切都只有一个 2d 点。这也意味着当您尝试求解 x 和 y 时,z 确实为 0。

我知道不是每个函数都有逆,有些函数有,但只针对特定域。我对此的基本想法是......如果我可以使用函数绘制网格......该网格上的每个点都映射到一个地图点。我可以读取 x 和 y 坐标,所以如果我只有正确的函数,我就可以计算逆。但是没有比一些好的坚实数学更好的替代品了,我很高兴你花时间给出一个非常有帮助的回应:)。

于 2010-05-22T11:04:09.730 回答