5

在处理(java 方言)中有方法 screenX、screenY(和 screenZ,但我们现在跳过它)。

假设我在 xyz = 50、100、500 处有一个对象。然后使用 screenX 和 screenY,您现在可以在画布上出现它们的位置。

float x = screenX(50, 100, 500); float y = screenY(50, 100, 500);

这是参考: http: //processing.org/reference/screenX_.html

我感兴趣的是一种逆方法。例如,我希望在 x=175 和 y=100 的画布上出现一个球体。球体的 az 应该是 700。那么实际的 x 和 y 位置会在 z=700 处使其在画布上的 175,100 处出现?

所以方法是float unscreenX(float x, float y, float z),它会返回 x 值。

我的数学/编程技能不是那么先进(让我们称之为糟糕)(我更像是一名设计师)所以我正在寻找一些帮助。我已经准备好在处理板上询问,但这里通常有更多人对矩阵等有更深入的了解。

可以在此处找到来自处理的正常 screenX 方法: https ://github.com/processing/processing/blob/master/core/src/processing/opengl/PGraphicsOpenGL.java

public float screenX(float x, float y, float z) {
    return screenXImpl(x, y, z);
  }

protected float screenXImpl(float x, float y, float z) {
    float ax =
      modelview.m00*x + modelview.m01*y + modelview.m02*z + modelview.m03;
    float ay =
      modelview.m10*x + modelview.m11*y + modelview.m12*z + modelview.m13;
    float az =
      modelview.m20*x + modelview.m21*y + modelview.m22*z + modelview.m23;
    float aw =
      modelview.m30*x + modelview.m31*y + modelview.m32*z + modelview.m33;
    return screenXImpl(ax, ay, az, aw);
  }


  protected float screenXImpl(float x, float y, float z, float w) {
    float ox =
      projection.m00*x + projection.m01*y + projection.m02*z + projection.m03*w;
    float ow =
      projection.m30*x + projection.m31*y + projection.m32*z + projection.m33*w;

    if (nonZero(ow)) {
      ox /= ow;
    }
    float sx = width * (1 + ox) / 2.0f;
    return sx;
  }

当然,还有 y 和 z (我不理解 z 但我们忽略它)。我认为这可能会对如何反转它提供一些见解。

modelview 和 projection 是一个 3d 矩阵,代码在这里: https ://github.com/processing/processing/blob/master/core/src/processing/core/PMatrix3D.java 但我想这是非常基本和常见的。

我还在处理板上发了一个帖子,因为你永远不知道。它解释了我想要的有点不同。 http://forum.processing.org/topic/unscreenx-and-unscreeny

对于描述这篇文章的标签,我没有具体说明原因,我可以想象一个从未使用过 java 但确实使用过 c++ 并且具有矩阵经验的程序员仍然能够提供一个很好的答案。

希望有人可以提供帮助。

4

4 回答 4

3

我强烈建议您学习一些用于 3d 图形的线性代数或矩阵数学。它既有趣又简单,但比 SO 答案要长一点。不过我会尝试 :) 免责声明:我不知道您正在使用的 API!

看起来您正在为某个位置(通常称为顶点)返回 3 个坐标。但是您还提到了一个投影矩阵,该函数有 4 个坐标。通常一个着色器或 API 会为一个顶点使用 4 个坐标。x,y,z,w。为了让它们出现在屏幕上,它会执行以下操作:

xscreen = x/w
yscreen = y/w
zbuffer = z/w

这很有用,因为您可以选择 w。如果你只是在做 2d 绘图,你可以把 w=1。但是如果你正在做 3d 并且想要一些透视效果,你想除以与相机的距离。这就是投影矩阵的用途。它主要取你点的z,其中z表示到相机的距离,并将其放入w。它也可能会稍微缩放一些东西,比如视野。

回顾您发布的代码,这正是最后一个 ScreenXImpl 函数所做的。它应用了一个投影矩阵,它主要只是将 z 移动到 w 中,然后除以 w。最后,它会进行额外的缩放和从 (-1,1) 到 (0,widhtinpixels) 的偏移,但我们可以忽略它。

现在,我为什么要喋喋不休地谈论这些东西?您要做的就是获取给定 xscreen、yscreen、zbuffer 的 x、y、z 坐标,对吗?好吧,诀窍就是倒退。为此,您需要牢牢把握前进的方向:)

倒退有两个问题:1)你真的知道或关心 zbuffer 的值吗?2)你知道投影矩阵做了什么吗?1)假设我们不在乎。有很多可能的值,所以我们可能只选择一个。对于 2) 你将不得不看看它做了什么。一些投影矩阵可能只取 (x,y,z,w) 和输出 (x,y,z,1)。那将是2d。或者 (x,y+z,z,1) 这将是等距的。但从角度来看,它通常是 (x,y,1,z)。加上一些缩放等等。

我刚刚注意到您的第二个 screenXImpl 已经将 x,y,z,w 传递到下一阶段。这有时很有用,但对于所有实际情况,w 将为 1。

在这一点上,我意识到我在解释事情方面很糟糕。:) 你真的应该拿起那本线性代数书,我从这本书中学到了:http ://www.amazon.com/Elementary-Linear-Algebra-Howard-Anton但它的讲座很好,所以我没有知道它自己有多大用处。

无论如何!让我们变得更实际一些。回到你的代码:screenXImpl的最后一个函数。我们现在知道输入 w=1 并且 ow=~z 和 ox=~x; 这里的波浪线表示乘以一些比例加上一些偏移量。我们必须开始的屏幕 x 是 ~ox/ow。(+1/2,*width.. 这就是波浪线的用途)。现在我们又回到了 1)...如果你想要一个特别的盎司 - 现在选择一个。否则,我们可以选择任何一个。对于渲染,在相机前选择任何东西并且易于使用可能是有意义的。像1。

protected float screenXImpl(float x, float y, float z, float w==1) {
float ox = 1*x + 0*y + 0*z + 0*w; // == x
float ow = 0*x + 0*y + 1*z + 0*w; // == z == 1

ox /= ow; // == ox

float sx = width * (1 + ox) / 2.0f;
return sx;
}

怎么回事?sx = 宽度 * (1+ox)/2 ? 为什么我刚才不这么说?好吧,我输入的所有零可能都不为零。但它最终会一样简单。那些可能不是。我试图展示您必须做出的重要假设才能返回。现在它应该像从 sx 回到 ox 一样简单。

那是最困难的部分!但是您仍然必须从最后一个函数转到第二个函数。我想第二个到第一个很容易。:) 该函数正在执行线性矩阵变换。这对我们有好处。它接受四个值 (x,y,z) 和 (w=1) 的隐式输入,并输出四个其他值 (ax,ay,az,aw)。我们可以弄清楚如何手动返回那里!我必须在学校里这样做……四个未知数,四个方程。您知道 ax,ay,az,aw... 求解 x,y,z 并免费获得 w=1!很有可能和很好的锻炼,但也很乏味。好消息是,这些方程的编写方式称为矩阵。(x,y,z,1) * MODELMATRIX = (ax,ay,az,aw)。真的很方便,因为我们可以找到 MODELMATRIX^-1。这叫逆天!就像 1/2 是乘以实数时 2 的倒数,或者 -1 是加法时 1 的倒数。你真的应该读一读这很有趣而且不难,顺便说一句:)。无论如何,使用任何标准库来获得模型矩阵的逆。可能类似于 modelView.Inverse()。然后用它做同样的功能,然后你倒退。简单的!

现在,为什么我们之前没有对 PROJECTION 矩阵做同样的事情呢?很高兴你问!那一个需要 4 个输入(x,y,z,w)并且只输出三个输出(screenx,screeny,zbufferz)。因此,如果不做一些假设,我们就无法解决它!一个直观的方法是,如果你有一个 3d 点,你在 2d 屏幕上投影,就会有很多可能的解决方案。所以我们必须选择一些东西。而且我们不能使用方便的矩阵逆函数。

让我知道这是否有帮助。我有一种感觉,它不是,但我写得很开心!google for unproject in processing 也给出了这个:http: //forum.processing.org/topic/read-3d-positions-gluunproject

于 2013-06-10T11:02:25.200 回答
0

在进行这项工作之前,您需要知道项目矩阵,而 Processing 没有为您提供。但是,我们可以通过检查三个向量 (1,0,0)、(0,1,0) 和 (0,0,1) 的 screenX/Y/Z 值来自己解决。从这些我们可以计算出屏幕的平面公式(从技术上讲,它只是一个穿过 3D 空间的裁剪平面 2D 表面)。然后,给定“屏幕”表面上的 (x,y) 坐标和预定z值,我们可以找到穿过屏幕平面的法线与 处的平面之间的交点z=...

但是,这不是您想要做的,因为您可以简单地为您想要做的任何事情重置坐标系。用于pushMatrix“保存”您当前的 3D 变换,resetMatrix将所有内容设置回“直线”,然后根据您的世界轴和视图轴对齐的事实绘制您的球体。然后当你完成后,调用popMatrix恢复你之前的世界变换并完成。省去实现数学的麻烦=)

于 2013-06-06T23:53:12.780 回答
0

你可以用简单的三角函数来解决这个问题。你需要的是,h眼睛到画布中心的距离,cx以及cy代表画布中心的 和 。为简单起见,假设cxcy为 0。请注意,它不是您实际眼睛的距离,而是用于构建 3d 场景透视图的虚拟眼睛的距离。

接下来,给定sxsy,计算到中心的距离,b = sqrt(sx * sx + sy * sy)

现在,您有一个直角三角形,其 baseb和 height h。这个三角形由“眼睛”、画布中心和屏幕上对象的所需位置组成:(sx, sy)

这个三角形形成了另一个由“眼睛”形成的直角三角形的顶部,画布上的中心被给定的深度向后推z,物体本身:(x, y)

三角形的底边和高度的比例完全相同,因此在bb给定高度hh = zhh = h + z取决于z值是来自眼睛还是来自画布的情况下,计算较大三角形的底边应该是微不足道的。要使用的等式是b / h = bb / hh您知道b的,h并且hh

从那里您可以轻松计算(x, y),因为两个底座与水平面的角度相同。IE。sy / sx = y / x.

唯一混乱的部分是从 3d 设置中提取眼睛到画布的距离和画布中心。

于 2013-06-15T20:55:26.967 回答
0

将 3d 点转换到 2d 屏幕上的概述

当您拥有对象 (x,y,z) 的 3d 表示时,您希望将其“投影”到 2d 的显示器上。为此,有一个转换函数可以接收您的 3d 坐标,然后输出 2d 坐标。在底层(至少在 openGL 中),发生的转换函数是一个特殊的矩阵。要进行转换,您需要获取点(由向量表示),然后进行简单的矩阵乘法。

对于一些漂亮的精美图表和推导(如果您好奇,则没有必要),请查看: http: //www.songho.ca/opengl/gl_projectionmatrix.html

对我来说,screenXImpl看起来它正在做矩阵乘法。

做相反的事情

反向变换只是原始变换的逆矩阵。

于 2013-06-16T07:42:49.987 回答