旋转后的 OpenGL python 和 pygame 平移不适用于鼠标外观和移动
OpenGL python and pygame translation after rotation not working for mouselook and movement
我正在尝试使用标准箭头键移动来制作简单的鼠标外观,鼠标外观可以正常工作,但旋转点的平移似乎沿正交基础移动,但不是对齐的随着鼠标外观的旋转。我不知道我的数学是否有问题,或者 opengl 是否正在做一些额外的事情来转换这些点,我需要进行调整。我查看了模型视图矩阵,它似乎遵循相同的旋转顺序,但我只是被困在这里,我不确定它是否与视角或实际发生的事情有关。我不是最擅长线性代数的,所以有点卡住了。
import pygame
import pygameMenu as pgm
from pygame.locals import *
from OpenGL.GL import *
from OpenGL.GLU import *
import numpy as np
verticies = (
(1, -1, -1),
(1, 1, -1),
(-1, 1, -1),
(-1, -1, -1),
(1, -1, 1),
(1, 1, 1),
(-1, -1, 1),
(-1, 1, 1)
)
edges = (
(0,1),
(0,3),
(0,4),
(2,1),
(2,3),
(2,7),
(6,3),
(6,4),
(6,7),
(5,1),
(5,4),
(5,7)
)
class OGl():
def three_func(a,b,func):
return (func(a[0],b[0]),func(a[1],b[1]),func(a[2],b[2]))
class GLCamera():
def __init__(self):
self.pos = [0.0,0.0,10.0]
self.rot = [0.0,0.0,0.0]
self.rotating = False
self.mouse_pos = [0,0]
def add_to_scene(self):
#buffer = glGetDouble( GL_MODELVIEW_MATRIX )
#print(buffer)
glRotatef(self.rot[2], 0, 0, 1); # roll
glRotatef(self.rot[1], 0, 1, 0); # heading
glRotatef(self.rot[0], 1, 0, 0); # pitch
glTranslatef(-self.pos[0],-self.pos[1],-self.pos[2]);
def change_of_basis(self):
#skip roll for now
c=np.cos(self.rot[1]*(np.pi/180))
s=np.sin(self.rot[1]*(np.pi/180))
m1=np.array([[c,0,s],[0,1,0],[-s,0,c]])
c=np.cos(self.rot[0]*(np.pi/180))
s=np.sin(self.rot[0]*(np.pi/180))
m2=np.array([[1,0,0],[0,c,-s],[0,s,c]])
m=m1.dot(m2)
return m
def handle_camera_events(self,event):
if event.type == pygame.KEYDOWN:
cb = self.change_of_basis()
if event.key == pygame.K_f:
m=cb.dot(np.array([0,0,-0.5]))
self.pos=OGl.three_func(self.pos,m, lambda x,y : x+y )
if event.key == pygame.K_g:
m=cb.dot(np.array([0,0,0.5]))
self.pos=OGl.three_func(self.pos,m, lambda x,y : x+y )
if event.key == pygame.K_LEFT:
m=cb.dot(np.array([-0.5,0,0]))
self.pos=OGl.three_func(self.pos,m, lambda x,y : x+y )
if event.key == pygame.K_RIGHT:
m=cb.dot(np.array([0.5,0,0]))
self.pos=OGl.three_func(self.pos,m, lambda x,y : x+y )
if event.key == pygame.K_DOWN:
m=cb.dot(np.array([0,-0.5,0]))
self.pos=OGl.three_func(self.pos,m, lambda x,y : x+y )
if event.key == pygame.K_UP:
m=cb.dot(np.array([0,0.5,0]))
self.pos=OGl.three_func(self.pos,m, lambda x,y : x+y )
if event.type == pygame.MOUSEMOTION and self.rotating:
tmp_pos = pygame.mouse.get_pos()
x,y = self.mouse_pos[0] - tmp_pos[0], self.mouse_pos[1] - tmp_pos[1]
if x != 0 or y != 0:
self.rot[1] = (self.rot[1] + x)
self.rot[0] = (self.rot[0] + y)
self.mouse_pos = tmp_pos
if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
if self.rotating == False:
self.rotating = True
self.mouse_pos = pygame.mouse.get_pos()
if event.type == pygame.MOUSEBUTTONUP and event.button == 1:
self.rotating = False
def Cube():
glBegin(GL_LINES)
for edge in edges:
for vertex in edge:
glVertex3fv(verticies[vertex])
glEnd()
def main():
pygame.init()
display = (800,600)
pygame.display.set_mode(display, DOUBLEBUF|OPENGL)
camera = GLCamera()
while True:
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
gluPerspective(45, (display[0]/display[1]), 0.1, 50.0)
camera.add_to_scene()
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
camera.handle_camera_events(event)
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
Cube()
pygame.display.flip()
pygame.time.wait(10)
main()
你很接近,但计算change_of_basis
不正确。视图矩阵定义为:
glRotatef(self.rot[2], 0, 0, 1); # roll
glRotatef(self.rot[1], 0, 1, 0); # heading
glRotatef(self.rot[0], 1, 0, 0); # pitch
glTranslatef(-self.pos[0],-self.pos[1],-self.pos[2])
如果要相对于视图 space 移动对象,则必须将视图 space 中的运动向量转换为世界 space。然后改变对象的世界space位置。
由于视图矩阵从世界 space 转换为视图 space,向量必须由 inverse 视图矩阵转换。
重建视图矩阵的方向并计算视图矩阵的逆矩阵。
使用 NumPy, the inverse matrix can be computed by numpy.linalg.inv(a)
. The rotation matrices matrices for the x, y and z axis can be concatenated by either numpy.matmul
or an operator. Note, for array, *
means element-wise multiplication, while @
means matrix multiplication. See array
.
更改change_of_basis
以解决问题:
class GLCamera():
# [...]
def change_of_basis(self):
#skip roll for now
rx, ry, rz = [self.rot[i]*np.pi/180 for i in range(3)]
s, c = np.sin(rx), np.cos(rx)
mx = np.array([[1,0,0],[0,c,-s],[0,s,c]])
s, c = np.sin(ry), np.cos(ry)
my = np.array([[c,0,s],[0,1,0],[-s,0,c]])
s, c = np.sin(rz), np.cos(rz)
mz = np.array([[c,-s,0],[s,c,0],[0,0,1]])
m = my @ mx @ mz
inv_m = np.linalg.inv(m)
return inv_m
我正在尝试使用标准箭头键移动来制作简单的鼠标外观,鼠标外观可以正常工作,但旋转点的平移似乎沿正交基础移动,但不是对齐的随着鼠标外观的旋转。我不知道我的数学是否有问题,或者 opengl 是否正在做一些额外的事情来转换这些点,我需要进行调整。我查看了模型视图矩阵,它似乎遵循相同的旋转顺序,但我只是被困在这里,我不确定它是否与视角或实际发生的事情有关。我不是最擅长线性代数的,所以有点卡住了。
import pygame
import pygameMenu as pgm
from pygame.locals import *
from OpenGL.GL import *
from OpenGL.GLU import *
import numpy as np
verticies = (
(1, -1, -1),
(1, 1, -1),
(-1, 1, -1),
(-1, -1, -1),
(1, -1, 1),
(1, 1, 1),
(-1, -1, 1),
(-1, 1, 1)
)
edges = (
(0,1),
(0,3),
(0,4),
(2,1),
(2,3),
(2,7),
(6,3),
(6,4),
(6,7),
(5,1),
(5,4),
(5,7)
)
class OGl():
def three_func(a,b,func):
return (func(a[0],b[0]),func(a[1],b[1]),func(a[2],b[2]))
class GLCamera():
def __init__(self):
self.pos = [0.0,0.0,10.0]
self.rot = [0.0,0.0,0.0]
self.rotating = False
self.mouse_pos = [0,0]
def add_to_scene(self):
#buffer = glGetDouble( GL_MODELVIEW_MATRIX )
#print(buffer)
glRotatef(self.rot[2], 0, 0, 1); # roll
glRotatef(self.rot[1], 0, 1, 0); # heading
glRotatef(self.rot[0], 1, 0, 0); # pitch
glTranslatef(-self.pos[0],-self.pos[1],-self.pos[2]);
def change_of_basis(self):
#skip roll for now
c=np.cos(self.rot[1]*(np.pi/180))
s=np.sin(self.rot[1]*(np.pi/180))
m1=np.array([[c,0,s],[0,1,0],[-s,0,c]])
c=np.cos(self.rot[0]*(np.pi/180))
s=np.sin(self.rot[0]*(np.pi/180))
m2=np.array([[1,0,0],[0,c,-s],[0,s,c]])
m=m1.dot(m2)
return m
def handle_camera_events(self,event):
if event.type == pygame.KEYDOWN:
cb = self.change_of_basis()
if event.key == pygame.K_f:
m=cb.dot(np.array([0,0,-0.5]))
self.pos=OGl.three_func(self.pos,m, lambda x,y : x+y )
if event.key == pygame.K_g:
m=cb.dot(np.array([0,0,0.5]))
self.pos=OGl.three_func(self.pos,m, lambda x,y : x+y )
if event.key == pygame.K_LEFT:
m=cb.dot(np.array([-0.5,0,0]))
self.pos=OGl.three_func(self.pos,m, lambda x,y : x+y )
if event.key == pygame.K_RIGHT:
m=cb.dot(np.array([0.5,0,0]))
self.pos=OGl.three_func(self.pos,m, lambda x,y : x+y )
if event.key == pygame.K_DOWN:
m=cb.dot(np.array([0,-0.5,0]))
self.pos=OGl.three_func(self.pos,m, lambda x,y : x+y )
if event.key == pygame.K_UP:
m=cb.dot(np.array([0,0.5,0]))
self.pos=OGl.three_func(self.pos,m, lambda x,y : x+y )
if event.type == pygame.MOUSEMOTION and self.rotating:
tmp_pos = pygame.mouse.get_pos()
x,y = self.mouse_pos[0] - tmp_pos[0], self.mouse_pos[1] - tmp_pos[1]
if x != 0 or y != 0:
self.rot[1] = (self.rot[1] + x)
self.rot[0] = (self.rot[0] + y)
self.mouse_pos = tmp_pos
if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
if self.rotating == False:
self.rotating = True
self.mouse_pos = pygame.mouse.get_pos()
if event.type == pygame.MOUSEBUTTONUP and event.button == 1:
self.rotating = False
def Cube():
glBegin(GL_LINES)
for edge in edges:
for vertex in edge:
glVertex3fv(verticies[vertex])
glEnd()
def main():
pygame.init()
display = (800,600)
pygame.display.set_mode(display, DOUBLEBUF|OPENGL)
camera = GLCamera()
while True:
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
gluPerspective(45, (display[0]/display[1]), 0.1, 50.0)
camera.add_to_scene()
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
camera.handle_camera_events(event)
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
Cube()
pygame.display.flip()
pygame.time.wait(10)
main()
你很接近,但计算change_of_basis
不正确。视图矩阵定义为:
glRotatef(self.rot[2], 0, 0, 1); # roll glRotatef(self.rot[1], 0, 1, 0); # heading glRotatef(self.rot[0], 1, 0, 0); # pitch glTranslatef(-self.pos[0],-self.pos[1],-self.pos[2])
如果要相对于视图 space 移动对象,则必须将视图 space 中的运动向量转换为世界 space。然后改变对象的世界space位置。
由于视图矩阵从世界 space 转换为视图 space,向量必须由 inverse 视图矩阵转换。
重建视图矩阵的方向并计算视图矩阵的逆矩阵。
使用 NumPy, the inverse matrix can be computed by numpy.linalg.inv(a)
. The rotation matrices matrices for the x, y and z axis can be concatenated by either numpy.matmul
or an operator. Note, for array, *
means element-wise multiplication, while @
means matrix multiplication. See array
.
更改change_of_basis
以解决问题:
class GLCamera():
# [...]
def change_of_basis(self):
#skip roll for now
rx, ry, rz = [self.rot[i]*np.pi/180 for i in range(3)]
s, c = np.sin(rx), np.cos(rx)
mx = np.array([[1,0,0],[0,c,-s],[0,s,c]])
s, c = np.sin(ry), np.cos(ry)
my = np.array([[c,0,s],[0,1,0],[-s,0,c]])
s, c = np.sin(rz), np.cos(rz)
mz = np.array([[c,-s,0],[s,c,0],[0,0,1]])
m = my @ mx @ mz
inv_m = np.linalg.inv(m)
return inv_m