某个z深度的xy位置

时间:2013-06-06 21:58:32

标签: java math opengl 3d processing

在处理(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的画布上使用球体到apear。球体的z值应为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 ++并且具有矩阵经验仍然能够提供一个很好的答案。 / p> 希望有人可以提供帮助。

4 个答案:

答案 0 :(得分: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,*宽度......那是什么波浪线)。现在我们回到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;
}

WTF? sx =宽*(1 +牛)/ 2?为什么我不这么说?好吧,我放在那里的所有零都可能不是零。但它最终会变得如此简单。可能不是那些人。我试图展示你必须做出的重要假设才能回归。现在它应该像从sx回到牛一样容易。

那是困难的部分!但你仍然需要从最后一个功能到第二个功能。我想第一个第二个很容易。 :)该函数正在进行线性矩阵变换。这对我们有好处。它需要输入四个值(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点,你在二维屏幕上投影,那么将会有很多可能的解决方案。所以我们必须选择一些东西。而且我们不能使用方便的矩阵逆函数。

请告诉我这是否有所帮助。我觉得它没有,但我写得很开心!此外,google for unproject in processing也提供了这个:http://forum.processing.org/topic/read-3d-positions-gluunproject

答案 1 :(得分:0)

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

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

的麻烦

答案 2 :(得分:0)

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

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

现在,您有一个直角三角形,其基数为b,高度为h。此三角形由“眼睛”,画布中心以及屏幕上对象的所需位置组成:(sx, sy)

此三角形形成由“眼睛”形成的另一个直角三角形的顶部,画布上的中心被推回给定深度z和对象本身:(x, y)

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

从那里你可以很容易地计算(x, y),因为两个基地与水平方向的角度相同。 I. e。 sy / sx = y / x

唯一令人讨厌的部分是从3D设置中提取眼睛到画布的距离和画布的中心。

答案 3 :(得分:0)

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

如果对象(x,y,z)有3d表示,则需要将其“投影”到显示器上,即2d。要做到这一点,有一个转换函数,它接收你的3D坐标,并吐出2d坐标。在引擎盖下(至少在openGL中),发生的转换函数是一个特殊的矩阵。要进行转换,请采用您的观点(由向量表示),并进行简单的矩阵乘法。

对于一些不错的花哨图表和推导(如果你很好奇,没有必要),请查看:http://www.songho.ca/opengl/gl_projectionmatrix.html

对我来说,screenXImpl看起来正在进行矩阵乘法。

执行相反的操作

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