Math I';我的2D/3D转换工作得很好,如何进行透视

Math I';我的2D/3D转换工作得很好,如何进行透视,math,3d,2d,coordinates,perspective,Math,3d,2d,Coordinates,Perspective,虽然这个问题的背景是关于制作2d/3d游戏,但我的问题可以归结为一些数学问题。 虽然这是一个2.5维的世界,但对于这个问题,让我们假设它只是二维的 // 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 c

虽然这个问题的背景是关于制作2d/3d游戏,但我的问题可以归结为一些数学问题。 虽然这是一个2.5维的世界,但对于这个问题,让我们假设它只是二维的

// 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
|
|
|
|
|
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可以为一些特殊情况创建反函数……它能解决这个问题吗?有人可以试试吗?)

你定义的函数没有反函数。举个例子,正如user207422已经指出的,距离相机100个单位的任何东西都会被映射到(xa,ya)=(0,0),因此,反向不是唯一定义的

更重要的是,这不是计算透视图的方式。通常,透视比例因子被定义为
viewdist/zdist
,其中
zdist
是从相机到对象的垂直距离,
viewdist
是一个常数,是从相机到所有投影到的假设屏幕的距离。(请参见图表,但可以忽略该页上的所有其他内容。)您在示例中使用的比例因子没有相同的行为

这里尝试将代码转换为正确的透视计算(注意,我并没有简化为2D;透视是将三维投影为二维,试图将问题简化为2D是毫无意义的):


您只需要从该函数返回
xa
ya
za
仅用于透视计算。我假设“za方向”指向屏幕外,因此如果预投影x轴指向观看者,则
zDistX
应为正,反之亦然,同样,对于
zDistY
。对于三角形投影,您可能需要
xDistZ==0
yDistZ我认为通过您的帖子,我可以解决这个问题。不过,为了澄清一些问题:

在2d中解决问题确实是无用的,但这样做只是为了让问题更容易理解(对我和这里的读者来说)。我的程序实际上给了它一个完美的3d投影(我用blender渲染的3d图像检查了它)。我确实遗漏了一些关于反函数的内容。反函数仅适用于0..camera.x*0.5和0。。照相机。y*0.5。在我的例子中,0和30之间。但即使这样,我也对我的功能有怀疑

在我的投影中,z轴总是笔直向上的,因此,为了计算物体的高度,我只使用了vieuwingangle。但是,由于你不能真正地飞或跳到天空中,一切都只有一个2d点。这也意味着当你试图解x和y时,z实际上是0

我知道不是每个函数都有逆函数,有些函数也有逆函数,但只适用于特定的域。我的基本想法是。。。如果我能用一个函数画一个网格。。。该栅格上的每个点都映射到一个映射点。我可以读取x和y坐标,所以如果我有正确的函数,我就能计算出逆。
但是没有比一些好的扎实的数学更好的替代品了,我很高兴你花时间给出了一个非常有用的回答:)。

什么是
fac
?什么是“视角量”?100跟什么有什么关系?是
fac
的意思是由于透视而实现对象的明显缩小。这使得距离为100的对象的大小为零,所以这没有意义?fac代表因子,就像透视因子一样,可以调整为重透视或小透视。使用这个函数绘制网格,看起来确实不错。对象应始终放置在相机前面的“网格”上。这意味着对象的距离不能大于sqrt(60^2+60^2)85。我选择100的原因是因为它比85大,给了我我需要的外观。我的功能不正确,但视觉效果看起来不错,虽然“透视”有点过头了。。。为了将来的参考,这通常会在你的问题的编辑中,或者在评论中。
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 );
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;
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)