我正试图选择一个3d点。我读了各种网站,但我的代码不起作用。
在鼠标右键单击:
glGetFloatv(GL_MODELVIEW_MATRIX,mv_mat)
glGetFloatv(GL_PROJECTION_MATRIX,p_mat)
ip_mat = np.linalg.inv(mat4(p_mat))
# clip = array[
# (2*x)/window_width-1
# 1-(y*2)/window.height
# -1
# 1
camera_coord = np.dot(ip_mat,clip)
camera_coord = np.array([camera_coord[0,0],camera_coord[0,1],-1,0])
imv_mat = np.linalg.inv(mat4(mv_mat))
ray_world = np.dot(imv_mat,camera_coord)
ray_world = np.array([ray_world[0],ray_world[1],ray_world[2]])
ray_world = ray_world/np.linalg.norm(ray_world)
Intersect_sphere函数:
v = np.array([model.rx,model.ry,model.rz]) - np.array([-0.5, 0.5, 0])
b = 2 * np.dot(v, ray_world)
c = np.dot(v, v) - 1 * 1
delta = b * b - 4 * c
if (delta > 0):
print('select')
return True
return False
编辑:我发现了一个错字。即使更改代码后仍然无法正常工作。
如果要在窗口中选择一个点,则必须将窗体窗口坐标转换为世界坐标或对象坐标。
要将窗口坐标映射到对象坐标,可以使用gluUnProject
。
gluUnProject
的参数是GLdouble
类型。
为投影矩阵和GLdouble
类型的视图矩阵创建一个数组,为视口矩形创建一个GLint
类型的数组:
self.mv_mat = (GLdouble * 16)()
self.p_mat = (GLdouble * 16)()
self.v_rect = (GLint * 4)()
获取当前投影矩阵,模型视图矩阵和视口矩形:
glGetDoublev(GL_MODELVIEW_MATRIX, self.mv_mat)
glGetDoublev(GL_PROJECTION_MATRIX, self.p_mat)
glGetIntegerv(GL_VIEWPORT, self.v_rect)
在视口上绘制的是3维场景的2维(透视)投影。从一个点,即摄像机位置查看场景。要查找窗口中“拾取”的对象,您必须找到对象所在的查看光线。光线由2个点定义。找到靠近摄像机的点和远离场景深度的点,这些点位于“拾取”窗口位置以定义光线。拾取的对象是最靠近相机的对象。在标准化设备空间中,从相机位置看,具有相同x和y坐标的所有点都在同一条光线上。 窗口空间中的点的第1和第2坐标是像素中的x和y坐标,第3坐标是范围[0,1]中的深度。 因此,从摄像机附近到远深度的光线槽坐标(x,y)由2点p0和p1定义,其中:
p0 = (x, y, 0)
p1 = (x, y, 1)
这点必须由gluUnProject
转换为2世界空间:
ray_near = [GLdouble() for _ in range(3)]
ray_far = [GLdouble() for _ in range(3)]
gluUnProject(x, y, 0, mv_mat, p_mat, v_rect, *ray_near)
gluUnProject(x, y, 1, mv_mat, p_mat, v_rect, *ray_far)
如果距离球体中心点到射线上最近点的距离小于或等于球体半径,则射线与球体相交。
计算光线的标准化方向:
p0 = [v.value for v in ray_near]
p1 = [v.value for v in ray_far]
r_dir = np.subtract(p0, p1)
r_dir = r_dir / np.linalg.norm(r_dir)
计算光线上距离球体中心点的最近点:
p0_cpt = np.subtract(p0, cpt)
near_pt = np.subtract(p0, r_dir * np.dot(p0_cpt, r_dir))
计算光线上的点到中心点的距离:
dist = np.linalg.norm(np.subtract(near_pt, cpt))
如果距离小于或等于球体的半径,则射线会击中球体:
isIntersecting = dist <= radius
请参阅简短的PyGlet示例:
from pyglet.gl import *
from pyglet.window import key
import numpy as np
class Window(pyglet.window.Window):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.sphere = gluNewQuadric()
self.vp_valid = False
self.mouse_pos = (0, 0)
self.mv_mat = (GLdouble * 16)()
self.p_mat = (GLdouble * 16)()
self.v_rect = (GLint * 4)()
def on_resize(self, width, height):
self.vp_valid = False
def isectSphere(self, p0, p1, cpt, radius):
# normalized ray direction
r_dir = np.subtract(p0, p1)
r_dir = r_dir / np.linalg.norm(r_dir)
# nearest point on the ray to the sphere
p0_cpt = np.subtract(p0, cpt)
near_pt = np.subtract(p0, r_dir * np.dot(p0_cpt, r_dir))
# distance to center point
dist = np.linalg.norm(np.subtract(near_pt, cpt))
# intersect if dist less or equal the radius of the sphere
return dist <= radius
def on_draw(self):
if not self.vp_valid:
self.vp_valid = True
glViewport(0, 0, self.width, self.height)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluPerspective(45, self.width/self.height, 0.1, 50.0)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
gluLookAt(0, -8, 0, 0, 0, 0, 0, 0, 1)
glGetDoublev(GL_MODELVIEW_MATRIX, self.mv_mat)
glGetDoublev(GL_PROJECTION_MATRIX, self.p_mat)
glGetIntegerv(GL_VIEWPORT, self.v_rect)
temp_val = [GLdouble() for _ in range(3)]
gluUnProject(*self.mouse_pos, 0, self.mv_mat, self.p_mat, self.v_rect, *temp_val)
self.mouse_near = [v.value for v in temp_val]
gluUnProject(*self.mouse_pos, 1, self.mv_mat, self.p_mat, self.v_rect, *temp_val)
self.mouse_far = [v.value for v in temp_val]
isect_a = self.isectSphere(self.mouse_near, self.mouse_far, [-1.5, 0, 0], 1)
isect_b = self.isectSphere(self.mouse_near, self.mouse_far, [1.5, 0, 0], 1)
glEnable(GL_DEPTH_TEST)
glEnable(GL_LIGHTING)
glShadeModel(GL_SMOOTH)
glEnable(GL_COLOR_MATERIAL)
glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE)
glEnable(GL_LIGHT0)
glLightfv(GL_LIGHT0, GL_AMBIENT, (GLfloat *4)(0.1, 0.1, 0.1, 1))
glLightfv(GL_LIGHT0, GL_DIFFUSE, (GLfloat *4)(1.0, 1.0, 1.0, 1))
glLightfv(GL_LIGHT0, GL_POSITION, (GLfloat *4)(0, -1, 0, 0))
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
glPushMatrix()
glTranslatef(-1.5, 0, 0)
if isect_a:
glColor4f(1.0, 0.5, 0.5, 1)
else:
glColor4f(0.5, 0.2, 0.2, 1)
gluSphere(self.sphere, 1.0, 32, 16)
glTranslatef(3, 0, 0)
if isect_b:
glColor4f(0.5, 0.5, 1.0, 1)
else:
glColor4f(0.2, 0.2, 0.5, 1)
gluSphere(self.sphere, 1.0, 32, 16)
glPopMatrix()
def on_mouse_motion(self,x,y,dx,dy):
self.mouse_pos = (x, y)
if __name__ == "__main__":
window = Window(width=800, height=600, resizable=True)
pyglet.app.run()