在处理(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> 希望有人可以提供帮助。
答案 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
,眼睛距画布中心的距离,以及代表画布中心的cx
和cy
。为简单起见,假设cx
和cy
为0.请注意,它不是您的实际眼睛的距离,而是用于在3d场景上构建透视的虚拟眼睛的距离。
接下来,给定sx
和sy
,计算到中心的距离,b = sqrt(sx * sx + sy * sy)
现在,您有一个直角三角形,其基数为b
,高度为h
。此三角形由“眼睛”,画布中心以及屏幕上对象的所需位置组成:(sx, sy)
。
此三角形形成由“眼睛”形成的另一个直角三角形的顶部,画布上的中心被推回给定深度z
和对象本身:(x, y)
。
三角形的基数和高度的比率完全相同,因此在给定高度bb
或hh = z
的情况下计算较大三角形hh = h + z
的基数应该是微不足道的。取决于z
值是来自眼睛还是来自画布。要使用的等式为b / h = bb / hh
,您知道b
,h
和hh
从那里你可以很容易地计算(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
看起来正在进行矩阵乘法。
执行相反的操作
反向变换只是原始变换的逆矩阵。