gpt4 book ai didi

python - 如何找到 OpenGL 顶点的 PyGame 窗口坐标?

转载 作者:行者123 更新时间:2023-12-01 02:31:18 32 4
gpt4 key购买 nike

我试图找出使用 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)上的坐标?

最佳答案

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

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

enter image description here

透视投影矩阵:

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

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

enter image description here

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

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

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

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]]

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

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()

关于python - 如何找到 OpenGL 顶点的 PyGame 窗口坐标?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46801701/

32 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com