我的2D / 3D转换工作完美,如何做透视

时间:2010-05-21 11:36:51

标签: math 3d 2d coordinates perspective

虽然这个问题的背景是关于制作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 个答案:

答案 0 :(得分:1)

您定义的函数没有反函数。就像一个例子,因为user207422已经指出距离摄像机100个单位的任何东西都将被映射到(xa,ya)=(0,0),所以逆不是唯一定义的。

更重要的是,这不是你如何计算视角。通常,透视比例因子被定义为viewdist/zdist,其中zdist是从相机到物体的垂直距离,viewdist是一个常数,即从相机到假想屏幕的距离投射一切的东西。 (请参阅图here,但可以忽略该页面上的所有其他内容。)您在示例中使用的缩放因子不具有相同的行为。

这是尝试将代码转换为正确的透视计算的一个尝试(注意我不是简化为2D;透视是关于将三维投影到两个,试图将问题简化为2D有点无意义):< / p>

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;

您只会从此功能返回xaya; za仅用于透视计算。我假设屏幕上有“za-direction”指向,所以如果预投影x轴指向观察者,则zDistX应为正,反之亦然,{{1 }}。对于trimetric投影,您可能会zDistYxDistZ==0yDistZ<0。这将使预投影的z轴点在投影后直线向上。

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

要做到这一点,你必须做一些线性代数。计算与zDistZ==0类似的camera_xcamera_y。那是相机的后变换坐标。屏幕上的点具有转换后坐标camera_z。在这两个点上绘制一条直线,并计算由矢量(xa,ya,camera_z-viewdist)(xDistX, yDistX, zDistX)跨越的平面相交的位置。换句话说,你需要解决方程:

(xDistY, yDistY, zDistY)

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

答案 1 :(得分:1)

我认为用你的帖子我可以解决问题。仍然,澄清一些问题:

解决2d中的问题确实没用,但这只是为了让问题更容易理解(对我和这里的读者来说)。我的程序实际上给出了一个完美的3D投影(我使用搅拌机渲染的3D图像进行了检查)。我确实留下了关于反函数的一些东西。反函数仅适用于0..camera.x * 0.5和0 .. camera.y * 0.5之间的坐标。所以在我的例子中介于0和30之间。但即便如此,我也怀疑我的功能。

在我的投影中,z轴始终是直线向上,因此要计算物体的高度,我只使用了旋转角。但是,由于你实际上无法飞行或跳入天空,所以只有2d点。这也意味着当你尝试解决x和y时,z确实为0.

我知道并非每个函数都有反函数,而且有些函数可以执行,但仅适用于特定域。我在这一切的基本思想是...如果我可以使用函数绘制网格...该网格上的每个点都映射到一个地图点。我可以读取x和y坐标,所以如果我只有正确的函数,我将能够计算逆。 但是没有更好的替代品,而是一些好的可靠的数学,我非常高兴你花时间给出一个非常有帮助的答案:)。