python屏幕坐标系的原点_关于python:将3D点投影到2D屏幕坐标

我正在尝试将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坐标。 (其他实现可能需要更多参数并设置指针而不是返回!)