Project 3D指向2D屏幕坐标

Pis*_*ete 0 python opengl projection kivy

我正在尝试将几个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)
Run Code Online (Sandbox Code Playgroud)

为了说明这不会产生良好的结果,请采用以下参数

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
Run Code Online (Sandbox Code Playgroud)

使用这些参数,to2D给出屏幕坐标(1383,-274)

我不认为这个问题与OpenGL和python有关,而是与从3D到屏幕坐标的操作有关. 我正在尝试做的事情:当触摸发生时,将3d点投影到2d屏幕坐标.

我的想法: 拍摄相机的模型视图和投影矩阵,我感兴趣的一点,以及触摸位置,然后制作一个从点到触摸位置的方法.通过将gluProject的源代码转换为Python来获取方法

我是怎么做到的:

  1. 将所有数学对象带入Sage以简化计算.

  2. 我的触摸位置是(150,114.1)

  3. 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 ]])

  4. 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 ]])

  5. width = 800.

  6. height = 600.

  7. v4 = vector(QQ, [0.52324, -0.65021, -0.55086, 1.])

  8. p = modelview*v4

  9. p = projection*p

  10. x = p[0] y = p[1] z = p[2] w = p[3]

  11. x /= w y /= w z /= w

  12. x = x*0.5 + 0.5 y = y*0.5 + 0.5 z = z*0.5 + 0.5

  13. 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
Run Code Online (Sandbox Code Playgroud)

修订:由于这甚至没有接近,我回到第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
Run Code Online (Sandbox Code Playgroud)

回想一下,目标是(150,114.1).x坐标非常好,但y坐标不是.所以我看了一下y*z,这是124.69.我可以忍受这个答案,虽然我不确定y*z看看我应该做的事情

Sig*_*erm 5

第一个问题在这里:

p = modelview*[pos[0], pos[1], pos[2], 0])
Run Code Online (Sandbox Code Playgroud)

当您使用矩阵作为4分量向量的多个向量时,Last component(w)必须为1.0

另一个在这里:

c = p[2]
a /= c
b /= c
Run Code Online (Sandbox Code Playgroud)

不要将x和y除以z,而应将x,y和z除以w.w是p [4].

在此之上:

如果有疑问,找到源代码gluProjectgluUnproject,把它拆开,并转换为蟒蛇.

据我所知,当手动将矢量投影到屏幕时,你应该做以下事情:

  1. 将"position"转换为4分量向量,将.w组件设置为1.

    v4.x = v3.x
    v4.y = v3.y
    v4.z = v3.z
    v4.w = 1.0
    
    Run Code Online (Sandbox Code Playgroud)
  2. 乘以矩阵乘以4分量.

  3. 然后用w划分所有分量.

    v4.x /= v4.w
    v4.y /= v4.w
    v4.z /= v4.w
    
    Run Code Online (Sandbox Code Playgroud)

然后,您将获得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)点)

PS另外,我不知道蟒蛇是如何处理整数浮动转换,但我会代替 a = (a+1)*width/2a = (a+1.0)*width/2.0显式指定你在这里使用浮点数.