【问题标题】:How to find PyGame Window Coordinates of an OpenGL Vertice?如何找到 OpenGL 顶点的 PyGame 窗口坐标?
【发布时间】:2017-10-18 01:59:32
【问题描述】:

我试图在使用 OpenGL 创建 3D 对象的 pygame 窗口中找出两个矩形的顶点坐标。

import pygame
from pygame.locals import *
import random
from OpenGL.GL import *
from OpenGL.GLU import *

rect1 = [(-5.125,0,-40),(-3.125,0,-40),(-3.125,5,-40),(-5.125,5,-40),]
rect2 = [(3.125,0,-40),(5.125,0,-40),(5.125,5,-40),(3.125,5,-40)]
edges = ((0,1),(1,2),(2,3),(3,0))

#This draws the rectangles edges
def Target():
    glBegin(GL_LINES)
    for edge in edges:
        for vertex in edge:
            glVertex3fv(rect1[vertex])
    glEnd()
    glBegin(GL_LINES)
    for edge in edges:
        for vertex in edge:
            glVertex3fv(rect2[vertex])
    glEnd()

def main():
    try:
        pygame.init()
        display = (320,240)
        pygame.display.set_mode(display, DOUBLEBUF|OPENGL)
        gluPerspective(45, (display[0]/display[1]), .1, 1000)

        while True:
            #iterates through events to check for quits
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    pygame.quit()
                    quit()        
            Target()
            pygame.display.flip()
            pygame.time.wait(100)
            glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)         
    except Exception as e:
        print (e)
main()

如何在对象的 pygame 窗口(320,240)上获取坐标?

【问题讨论】:

    标签: python opengl pygame coordinate-transformation perspectivecamera


    【解决方案1】:

    投影矩阵描述了从场景的 3D 点到视口的 2D 点的映射。它从眼睛空间转换到剪辑空间,剪辑空间中的坐标通过除以剪辑坐标的w 分量转换为标准化设备坐标(NDC)。 NDC 的范围是 (-1,-1,-1) 到 (1,1,1)。

    在透视投影中,投影矩阵描述了从针孔相机看到的世界中的 3D 点到视口的 2D 点的映射。
    相机平截头体(截断的金字塔)中的眼睛空间坐标映射到立方体(标准化设备坐标)。

    透视投影矩阵:

    r = right, l = left, b = bottom, t = top, n = near, f = far
    
    2*n/(r-l)      0              0                0
    0              2*n/(t-b)      0                0
    (r+l)/(r-l)    (t+b)/(t-b)    -(f+n)/(f-n)    -1    
    0              0              -2*f*n/(f-n)     0
    

    在哪里:

    aspect = w / h
    tanFov = tan( fov_y / 2 );
    
    2 * n / (r-l) = 1 / (tanFov * aspect)
    2 * n / (t-b) = 1 / tanFov
    

    由于投影矩阵是由视场和纵横比定义的,因此可以通过视场和纵横比恢复视口位置。前提是它是对称的透视投影,其中视野没有移位(如您的情况)。

    首先,您必须将鼠标位置转换为标准化设备坐标:

    w = with of the viewport
    h = height of the viewport
    x = X position of the mouse
    y = Y position ot the mouse
    
    ndc_x = 2.0 * x/w - 1.0;
    ndc_y = 1.0 - 2.0 * y/h; // invert Y axis
    

    然后你必须将标准化的设备坐标转换为视图坐标:

    z = z coodinate of the geometry in view space
    
    viewPos.x = -z * ndc_x * aspect * tanFov;
    viewPos.y = -z * ndc_y * tanFov;  
    


    如果你想检查鼠标是否悬停在你的矩形上,那么代码可能如下所示:

    mpos = pygame.mouse.get_pos()
    z = 40
    
    ndc = [ 2.0 * mpos[0]/width - 1.0, 1.0 - 2.0 * mpos[1]/height ]
    tanFov = math.tan( fov_y * 0.5 * math.pi / 180 )
    aspect = width / height 
    viewPos = [z * ndc[0] * aspect * tanFov, z * ndc[1] * tanFov ]
    
    onRect1 = 1 if (viewPos[0]>=rect1[0][0] and viewPos[0]<=rect1[1][0] and viewPos[1]>=rect1[0][1] and viewPos[1]<=rect1[2][1] ) else 0
    onRect2 = 1 if (viewPos[0]>=rect2[0][0] and viewPos[0]<=rect2[1][0] and viewPos[1]>=rect2[0][1] and viewPos[1]<=rect2[2][1] ) else 0
    


    进一步看:


    在下面,我将算法添加到您的示例中。如果鼠标悬停在一个矩形上,则该矩形将显示为红色。

    import pygame
    from pygame.locals import *
    import random
    from OpenGL.GL import *
    from OpenGL.GLU import *
    import math
    
    rect1 = [(-5.125,0,-40),(-3.125,0,-40),(-3.125,5,-40),(-5.125,5,-40),]
    rect2 = [(3.125,0,-40),(5.125,0,-40),(5.125,5,-40),(3.125,5,-40)]
    edges = ((0,1),(1,2),(2,3),(3,0))
    
    fov_y = 45
    width = 320
    height = 200
    
    #This draws the rectangles edges
    def Target():
        mpos = pygame.mouse.get_pos()
        z = 40
    
        ndc = [ 2.0 * mpos[0]/width - 1.0, 1.0 - 2.0 * mpos[1]/height ]
        tanFov = math.tan( fov_y * 0.5 * math.pi / 180 )
        aspect = width / height 
        viewPos = [z * ndc[0] * aspect * tanFov, z * ndc[1] * tanFov ]
    
        onRect1 = 1 if (viewPos[0]>=rect1[0][0] and viewPos[0]<=rect1[1][0] and viewPos[1]>=rect1[0][1] and viewPos[1]<=rect1[2][1] ) else 0
        onRect2 = 1 if (viewPos[0]>=rect2[0][0] and viewPos[0]<=rect2[1][0] and viewPos[1]>=rect2[0][1] and viewPos[1]<=rect2[2][1] ) else 0
    
        glColor3f( 1, 1-onRect1, 1-onRect1 )
        glBegin(GL_LINES)
        for edge in edges:
            for vertex in edge:
                glVertex3fv(rect1[vertex])
        glEnd()
    
        glColor3f( 1, 1-onRect2, 1-onRect2 )
        glBegin(GL_LINES)
        for edge in edges:
            for vertex in edge:
                glVertex3fv(rect2[vertex])
        glEnd()
    
    def main():
        try:
            pygame.init()
            display = (width,height)
            pygame.display.set_mode(display, DOUBLEBUF|OPENGL)
            glMatrixMode(GL_PROJECTION)
            gluPerspective(fov_y, (display[0]/display[1]), .1, 1000)
            glMatrixMode(GL_MODELVIEW)
    
            while True:
                #iterates through events to check for quits
                for event in pygame.event.get():
                    if event.type == pygame.QUIT:
                        pygame.quit()
                        quit()        
                Target()
                pygame.display.flip()
                pygame.time.wait(100)
                glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)         
        except Exception as e:
            print (e)
    main()
    


    答案的扩展

    当然你也可以反过来做。您可以将矩形的角点转换为规范化的设备坐标,并将它们与鼠标位置(以规范化的设备坐标)进行比较。

    为此,您必须阅读glGetFloatv(GL_PROJECTION_MATRIX)的投影矩阵:

    prjMat = (GLfloat * 16)()
    glGetFloatv(GL_PROJECTION_MATRIX, prjMat)
    

    您需要一个函数,通过投影矩阵转换 3 维笛卡尔向量。这是通过将向量乘以投影矩阵来完成的,投影矩阵给出了齐次剪辑空间坐标。通过将xyz 分量除以w 分量来计算归一化的设备坐标。

    def TransformVec3(vecA,mat44):
        vecB = [0, 0, 0, 0]
        for i0 in range(0, 4):
            vecB[i0] = vecA[0] * mat44[0*4+i0] + vecA[1] * mat44[1*4+i0] + vecA[2] * mat44[2*4+i0] + mat44[3*4+i0]
        return [vecB[0]/vecB[3], vecB[1]/vecB[3], vecB[2]/vecB[3]]
    

    以下函数测试鼠标位置是否在由左下角和右上角定义的矩形中(角点必须在视图空间坐标中):

    def TestRec(prjMat, mpos, ll, tr):
        ll_ndc = TransformVec3(ll, prjMat)
        tr_ndc = TransformVec3(tr, prjMat)
        ndc = [ 2.0 * mpos[0]/width - 1.0, 1.0 - 2.0 * mpos[1]/height ]
        inRect = 1 if (ndc[0]>=ll_ndc[0] and ndc[0]<=tr_ndc[0] and ndc[1]>=ll_ndc[1] and ndc[1]<=tr_ndc[1] ) else 0
        return inRect
    


    我再次将算法添加到您的示例中。如果鼠标悬停在一个矩形上,则该矩形将显示为红色。

    import pygame
    from pygame.locals import *
    import random
    from OpenGL.GL import *
    from OpenGL.GLU import *
    import math
    
    rect1 = [(-5.125,0,-40),(-3.125,0,-40),(-3.125,5,-40),(-5.125,5,-40),]
    rect2 = [(3.125,0,-40),(5.125,0,-40),(5.125,5,-40),(3.125,5,-40)]
    edges = ((0,1),(1,2),(2,3),(3,0))
    
    fov_y = 45
    width = 320
    height = 200
    
    def TransformVec3(vecA,mat44):
        vecB = [0, 0, 0, 0]
        for i0 in range(0, 4):
            vecB[i0] = vecA[0] * mat44[0*4+i0] + vecA[1] * mat44[1*4+i0] + vecA[2] * mat44[2*4+i0] + mat44[3*4+i0]
        return [vecB[0]/vecB[3], vecB[1]/vecB[3], vecB[2]/vecB[3]]
    
    def TestRec(prjMat, mpos, ll, tr):
        ll_ndc = TransformVec3(ll, prjMat)
        tr_ndc = TransformVec3(tr, prjMat)
        ndc = [ 2.0 * mpos[0]/width - 1.0, 1.0 - 2.0 * mpos[1]/height ]
        inRect = 1 if (ndc[0]>=ll_ndc[0] and ndc[0]<=tr_ndc[0] and ndc[1]>=ll_ndc[1] and ndc[1]<=tr_ndc[1] ) else 0
        return inRect
    
    
    #This draws the rectangles edges
    def Target():
    
        prjMat = (GLfloat * 16)()
        glGetFloatv(GL_PROJECTION_MATRIX, prjMat)
    
        mpos = pygame.mouse.get_pos()
        onRect1 = TestRec(prjMat, mpos, rect1[0], rect1[2])
        onRect2 = TestRec(prjMat, mpos, rect2[0], rect2[2])
    
        glColor3f( 1, 1-onRect1, 1-onRect1 )
        glBegin(GL_LINES)
        for edge in edges:
            for vertex in edge:
                glVertex3fv(rect1[vertex])
        glEnd()
    
        glColor3f( 1, 1-onRect2, 1-onRect2 )
        glBegin(GL_LINES)
        for edge in edges:
            for vertex in edge:
                glVertex3fv(rect2[vertex])
        glEnd()
    
    def main():
        try:
            pygame.init()
            display = (width,height)
            pygame.display.set_mode(display, DOUBLEBUF|OPENGL)
            glMatrixMode(GL_PROJECTION)
            gluPerspective(fov_y, (display[0]/display[1]), .1, 1000)
            glMatrixMode(GL_MODELVIEW)
    
            while True:
                #iterates through events to check for quits
                for event in pygame.event.get():
                    if event.type == pygame.QUIT:
                        pygame.quit()
                        quit()        
                Target()
                pygame.display.flip()
                pygame.time.wait(100)
                glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)         
        except Exception as e:
            print (e)
    main()
    

    【讨论】:

    • 这很棒。我对您如何近似 pygame 框架中矩形的位置感到有些困惑。我正在寻找在我的 pygame 框架中获得这些点的 (x,y) 位置。所以基本上将矩形的 (x,y,z) 转换为在我的屏幕上查看的 pygame 的 (x,y)。
    • 我可以根据鼠标位置比较它们,而不是将它们作为 3 维的标准化设备坐标进行比较?基本上我正在尝试创建一个包含 NDC 和 PyGame 窗口坐标的数据集。
    • @goofyreader 但这就是第二灵魂的作用。注意,pos_ndc = TransformVec3(pos, prjMat);pos_pixel_xy = [width*(pos_ndc[0]+1.0)/2.0, height*(1.0-pos_ndc[1])/2.0]
    猜你喜欢
    • 1970-01-01
    • 2015-12-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-18
    相关资源
    最近更新 更多