Как найти координаты окна PyGame вершины OpenGL?

Я пытаюсь выяснить координаты вершин двух прямоугольников в окне pygame, которое использует OpenGL для создания 3D-объектов.

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) объекта?


person goofyreader    schedule 18.10.2017    source источник


Ответы (1)


Матрица проекции описывает отображение трехмерных точек сцены в двухмерные точки окна просмотра. Он преобразуется из пространства глаза в пространство клипа, а координаты в пространстве клипа преобразуются в нормализованные координаты устройства (NDC) путем деления на компонент w координат клипа. NDC находится в диапазоне от (-1,-1,-1) до (1,1,1).

В Perspective Projection матрица проекции описывает отображение трехмерных точек мира, как они видны с камеры-обскуры, в двухмерные точки окна просмотра.
Координаты пространства глаза в усеченной пирамиде камеры (усеченная пирамида) сопоставляются с кубом (нормализованные координаты устройства).

введите здесь описание изображения

Матрица перспективной проекции:

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

Поскольку матрица проекции определяется полем зрения и соотношением сторон, можно восстановить положение окна просмотра с полем зрения и соотношением сторон. При условии, что это симметричная перспективная проекция, где поле зрения не смещено (как в вашем случае).

введите здесь описание изображения

Сначала вам нужно преобразовать позицию mose в нормализованные координаты устройства:

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)

И вам нужна функция, которая преобразует трехмерный декартов вектор с помощью проекционной матрицы. Это делается путем умножения вектора на матрицу проекции, что дает однородные координаты пространства клипа. Нормализованные координаты устройства рассчитываются путем деления компонентов x, y и z на компонент 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()
person Rabbid76    schedule 18.10.2017
comment
Отлично. Я немного смущен тем, как вы приблизились к положению прямоугольников в фрейме pygame. Я хочу получить (x, y) позиции этих точек в моем фрейме pygame. Таким образом, в основном преобразование (x, y, z) прямоугольников в (x, y) pygame, как показано на моем экране. - person goofyreader; 19.10.2017
comment
Вместо того, чтобы сравнивать их как нормализованные координаты устройства, которые являются трехмерными, могу ли я сравнить их с точки зрения положения мыши? В основном я пытаюсь создать набор данных, который содержит координаты окна NDC и PyGame. - person goofyreader; 23.10.2017
comment
@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] - person Rabbid76; 23.10.2017