我正在尝试将3d点投影到屏幕坐标,以确定是否在大致相同的区域发生触摸。应当指出,我是在Kivy(Python和OpenGL)中执行此操作的。我已经看到过类似的问题,但是我仍然没有解决方案。我已经尝试了以下方法,但是数字与屏幕坐标并不接近。
def to2D(self, pos, width, height, modelview, projection):
p = modelview*[pos[0], pos[1], pos[2], 0])
p = projection*p
a = p[0]
b = p[1]
c = p[2]
a /= c
b /= c
a = (a+1)*width/2.
b = (b+1)*height/2.
return (a, b)
为了说明这不会产生好的结果,请采用以下参数
modelview = [[-0.831470, 0.553001, 0.053372, 0.000000],
[0.000000, 0.096068, -0.995375, 0.000000],
[-0.555570, -0.827624, -0.079878, 0.000000],
[-0.000000, -0.772988, -2.898705, 1.000000]]
projection = [[ 15.763722, 0.000000, 0.000000, 0.000000],
[ 0.000000, 15.257052, 0.000000, 0.000000],
[ 0.000000, 0.000000, -1.002002, -2.002002],
[ 0.000000, 0.000000, -1.000000, 0.000000]]
pos = [0.523355213060808, -0.528964010275341, -0.668054187020413] #I'm working on a unit sphere, so these are more meaningful in spherical coordinates
width = 800
height = 600
使用这些参数,to2D给出屏幕坐标(1383,-274)
我不认为问题与OpenGL和python有关,而与从3d转换到屏幕坐标有关的操作无关。
我想做的是:发生触摸时,将3d点投影到2d屏幕坐标上。
我的点子:
获取相机的模型视图和投影矩阵,我感兴趣的点以及触摸位置,然后制定一种从该点到触摸位置的方法。通过将gluProject的此源代码转换为Python来获取方法
我是如何做到的:
为了简化计算,将所有数学对象带入Sage。
我的触摸位置是(150,114.1)
modelview = matrix([[ -0.862734, 0.503319, 0.048577, 0.000000 ],
[ 0.000000, 0.096068, -0.995375, 0.000000 ],
[ -0.505657, -0.858744, -0.082881, 0.000000 ],
[ 0.000000, -0.772988, -2.898705, 1.000000 ]])
projection = matrix([[ 15.763722, 0.000000, 0.000000, 0.000000 ],
[ 0.000000, 15.257052, 0.000000, 0.000000 ],
[ 0.000000, 0.000000, -1.002002, -2.002002 ],
[ 0.000000, 0.000000, -1.000000, 0.000000 ]])
width = 800.
height = 600.
v4 = vector(QQ, [0.52324, -0.65021, -0.55086, 1.])
p = modelview*v4
p = projection*p
x = p[0]
y = p[1]
z = p[2]
w = p[3]
x /= w
y /= w
z /= w
x = x*0.5 + 0.5
y = y*0.5 + 0.5
z = z*0.5 + 0.5
x = x*width
y = y*height #There's no term added because the widget is located at (0, 0)
结果:
x = 15362.18
y = -6251.43
z = 10.14
修订:由于还没有结束,我回到了步骤8和9,切换了乘法顺序以查看会发生什么。所以现在8.是p = v4*modelview,而9.是p = p*projection。在这种情况下,向量是行向量。另一种查看方式是p = modelviewTranspose*v4和p = projectionTranspose*p,其中向量是列向量。
结果部分2:
x = 150.29
y = 196.15
z = 0.6357
回想一下目标是(150,114.1)。 x坐标很好,但y坐标不是。因此,我查看了y*z,它是124.69。我可以接受这个答案,尽管我不确定是否应该真正查看y*z
您能否举一些输入和输出示例,说明您得到的不是您所期望的?
也许您弄错了模型视图或投影。不能保证将值放入屏幕上的一种配件中,或者即使它们确实适合屏幕上也可以清楚辨别。
@TimothyGroote我从相机获取了模型视图和投影矩阵,所以我认为这不是问题。
@khelwood查看修改。
p = modelview*[pos[0], pos[1], pos[2], 0])由于括号而抛出SyntaxError,而没有括号则抛出TypeError。
@Ryan,我的错。将id放入Sage矩阵中进行计算,因此该行最初是p = modelview*Vector(QQ, [pos[0], pos[1], pos[2], 0])。我一定忘了删除")"
您的矩阵对我来说非常可疑。实际上,您的modelview矩阵看起来像代码中的最后4个值是转换部分。但是,您的投影矩阵看起来好像已转换为该矩阵,即(0 0 -2 0)应该是转换部分。看起来您对两者都使用了两种不同的约定。
@derhass我不知道矩阵背后的"方式";我只是得到它们,然后尝试对它们进行数学运算。如果我了解kivy3(Im在3d中使用的功能)的工作方式,则modelview是根据与gluLookAt类似的东西计算出来的。 projection是从Kivys透视函数计算得出的,您对解决常规问题有何建议? Ive发现,查看每个矩阵的转置可获得更好的结果,但我没有理由。
@PistolPete:您的电话号码根本没有意义。如果我以应该解释的方式解释模型视图和投影矩阵,那么我会得到一个视锥细胞之外的问题。如果我使用投影矩阵转换为实际应有的数字,则得到的数字与您相同。但是对我来说最有趣的问题是:您是如何在第一位置想到输入位置矢量(v4)?
v4来自Im试图做的事情:发生触摸时,将一组特定的3d点投影到屏幕坐标上,以查看该点是否靠近触摸。我的(x,y,z)似乎很武断,但这是单位球面上的一点,这正是我正在研究的。那么,如果矩阵被换位,那么它们的解释是正确的吗?这是有道理的,因为我通过移调得到的答案几乎是正确的,但是我尝试的原始方式却很糟糕。
@PistolPete:" v4来自Im试图做的事情:发生触摸时,将一组特定的3d点投影到屏幕坐标上,以查看该点是否接近触摸。"那没有道理。要获得v4,您必须做相反的操作,即从触摸位置到达对象空间点(通常称为"非投影")。那是您真正想要做的吗?
Ive有一组提前知道的"特殊"要点。那就是我怎么知道v4是什么。我投射每个物体,然后查看它们相对于触摸位置的位置。
让我们继续聊天中的讨论。
第一个问题在这里:
p = modelview*[pos[0], pos[1], pos[2], 0])
当您使用矩阵作为4分量向量的多个向量时,最后一个分量(w)必须为1.0
另一个在这里:
c = p[2]
a /= c
b /= c
不要将x和y除以z,而应将x,y和z除以w。 w是p [4]。
在此之上:
如有疑问,请找到gluProject和gluUnproject的源代码,将其拆开并转换为python。
据我所知,当手动将矢量投影到屏幕上时,您应该执行以下操作:
将.w分量设置为1,将"位置"转换为4分量向量。
v4.x = v3.x
v4.y = v3.y
v4.z = v3.z
v4.w = 1.0
将4分量乘以矩阵。
然后将所有分量除以w。
v4.x /= v4.w
v4.y /= v4.w
v4.z /= v4.w
然后,您将获得x和y在+ -1.0范围内的屏幕坐标。 (z会在0.0..1.0或0.0 ..- 1.0之内,在OpenGL中我忘记了)。
w之所以起作用,是因为您无法通过矩阵乘法进行除法,因此当您需要将x / y / z除以某物时,可以将其放入w分量,并在所有矩阵乘法之后执行除法。 w也使翻译矩阵成为可能。 w == 0的任何向量都不能使用平移矩阵平移,只能绕原点旋转并通过仿射变换变形("原点"表示坐标空间的零点-(0.0,0.0,0.0)点)
附言另外,我不知道python如何处理整数到浮点的转换,但是我将a = (a+1)*width/2替换为a = (a+1.0)*width/2.0以明确指定您在此处使用浮点数。
因此,我从这里查看了gluProject源,并尝试转换为Python,但是得到了v4.x = 10530和v4.y = -12220。 无论如何,我都将p = modelview*v4和p = projection*p更改为p = v4*modelview和p = p*projection。 这样就得到了x = 111, y = 276, z 0.6397,如果我将触摸位置与x和y*z进行比较,则它的位置接近,但略有偏离。 触摸位置为(124,162)。
@PistolPete:您可以做一个简短的完整示例,并将其链接到您的问题吗? 这可能和乘法顺序一样琐碎(意味着,取决于您的矩阵库,modelview * p和p * modelview可能会产生不同的结果),但是没有最低工作OpenGL应用程序的人编写了python(我没有一个,并且没有 感觉就像写一个,没有冒犯的意思),如果没有工作的榜样,可能不会打扰进一步研究您的问题。 只是一个画点并在子程序中使用准系统opengl的东西。
如果可以访问它,则pyopengl的gluProject(x, y, z)返回当前上下文的屏幕空间x,y,z坐标。 (其他实现可能需要更多参数并设置指针而不是返回!)