如何在 PyOpenGL 上创建一个可以对鼠标移动执行 "perspective rotations" 的相机?
How create a camera on PyOpenGL that can do "perspective rotations" on mouse movements?
我正在创建第一人称视角 RPG,我想在移动鼠标时在 PyOpenGL 中旋转相机(就像其他一些游戏,如 Minecraft)。我可以使用什么函数来执行此操作以及如何执行此操作?
我尝试使用 gluLookAt()
,但我不明白它是如何工作的,尽管我查阅了不同的来源。不知道能不能帮上忙
import sys,pygame
from OpenGL.GL import *
from OpenGL.GLU import *
cmddown = False
#...
keypress = pygame.key.get_pressed()#Move using WASD
if keypress[pygame.K_w]:
glTranslatef(0,0,0.1)
if keypress[pygame.K_s]:
glTranslatef(0,0,-0.1)
if keypress[pygame.K_d]:
glTranslatef(-0.1,0,0)
if keypress[pygame.K_a]:
glTranslatef(0.1,0,0)
mouse_movement = pygame.mouse.get_rel()#Get mouse event
#This is where the "look around" should be happen
pygame.display.flip()
您可以使用 glRotate
to rotate around an axis, by an amount which is given by the relative mouse movement (pygame.mouse.get_rel()
):
mouseMove = pygame.mouse.get_rel()
glRotatef(mouseMove[0]*0.1, 0.0, 1.0, 0.0)
但这不会让您满意,因为如果鼠标离开 window,解决方案将不再有效。
您必须通过 pygame.mouse.set_pos()
in every frame. Get the mouse movement by the pygame.MOUSEMOTION
事件将鼠标置于屏幕中央。例如:
# init mouse movement and center mouse on screen
displayCenter = [scree.get_size()[i] // 2 for i in range(2)]
mouseMove = [0, 0]
pygame.mouse.set_pos(displayCenter)
paused = False
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE or event.key == pygame.K_RETURN:
run = False
if event.key == pygame.K_PAUSE or event.key == pygame.K_p:
paused = not paused
pygame.mouse.set_pos(displayCenter)
if event.type == pygame.MOUSEMOTION:
mouseMove = [event.pos[i] - displayCenter[i] for i in range(2)]
if not paused:
pygame.mouse.set_pos(displayCenter)
请注意,像 glRotate
and glTranslate
这样的操作会设置一个矩阵并将当前矩阵乘以新矩阵。
currentMatrix = currentMatrix * newMatrix
这对于模型动画和变换来说是完美的,但对于第一人称运动来说这是错误的方式,因为必须改变相机位置和视角。
viewMatrix = viewTransformMatrix * viewMatrix
做这样的操作glGetFloatv(GL_MODELVIEW_MATRIX)
and glMultMatrixf
。
通过 gluLookAt
初始化视图矩阵,并在主循环之前通过 glGetFloatv(GL_MODELVIEW_MATRIX)
将视图矩阵加载到变量 (viewMatrix
)。
在每帧的主循环中:
- 加载 Identity matrix (
glLoadIdentity
)
- 对视图进行新的转换 (
glRotatef
, glTranslatef
)
- 将当前视图矩阵乘以
viewMatrix
(glMultMatrixf
)
- 为下一帧将新的视图矩阵加载到
viewMatrix
glMatrixMode(GL_MODELVIEW)
gluLookAt(0, -8, 0, 0, 0, 0, 0, 0, 1)
viewMatrix = glGetFloatv(GL_MODELVIEW_MATRIX)
glLoadIdentity()
# [...]
run = True
while run:
# [...]
# init the view matrix
glLoadIdentity()
# apply the movment
if keypress[pygame.K_w]:
glTranslatef(0,0,0.1)
if keypress[pygame.K_s]:
glTranslatef(0,0,-0.1)
if keypress[pygame.K_d]:
glTranslatef(-0.1,0,0)
if keypress[pygame.K_a]:
glTranslatef(0.1,0,0)
# apply the roation
glRotatef(mouseMove[0]*0.1, 0.0, 1.0, 0.0)
# multiply the current matrix by the get the new view matrix and store the final vie matrix
glMultMatrixf(viewMatrix)
viewMatrix = glGetFloatv(GL_MODELVIEW_MATRIX)
为了向上和向下看,您必须围绕 x 轴应用最后的旋转。旋转的枢轴取决于视点。
必须总结角度,并且必须在视图矩阵之后应用旋转,否则移动会根据角度改变(“高度”)级别。
viewMatrix = viewTransformMatrix * viewMatrix
finlalMatrix = lookUpDownMatrix * viewMatrix
为此,您必须结合上下旋转矩阵并将其乘以 viewMatrix
up_down_angle = 0.0
run = True
while run:
# [...]
# init model view matrix
glLoadIdentity()
# apply the look up and down
up_down_angle += mouseMove[1]*0.1
glRotatef(up_down_angle, 1.0, 0.0, 0.0)
# init the view matrix
glPushMatrix()
glLoadIdentity()
# calculate new `viewMatrix`
# [...]
# apply view matrix
glPopMatrix()
glMultMatrixf(viewMatrix)
请参阅下面的示例程序,它演示了该过程。
请注意,程序 将鼠标保持在 window 的中心 ,因此您不能再“移动”鼠标。
因此,应用程序 可以通过 ESC 或 return.
停止
应用程序可以通过 pause 或 p 暂停。当应用程序暂停时,鼠标未居中到window。
import pygame
from pygame.locals import *
from OpenGL.GL import *
from OpenGL.GLU import *
import math
pygame.init()
display = (400, 300)
scree = pygame.display.set_mode(display, DOUBLEBUF | OPENGL)
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, [0.5, 0.5, 0.5, 1])
glLightfv(GL_LIGHT0, GL_DIFFUSE, [1.0, 1.0, 1.0, 1])
sphere = gluNewQuadric()
glMatrixMode(GL_PROJECTION)
gluPerspective(45, (display[0]/display[1]), 0.1, 50.0)
glMatrixMode(GL_MODELVIEW)
gluLookAt(0, -8, 0, 0, 0, 0, 0, 0, 1)
viewMatrix = glGetFloatv(GL_MODELVIEW_MATRIX)
glLoadIdentity()
# init mouse movement and center mouse on screen
displayCenter = [scree.get_size()[i] // 2 for i in range(2)]
mouseMove = [0, 0]
pygame.mouse.set_pos(displayCenter)
up_down_angle = 0.0
paused = False
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE or event.key == pygame.K_RETURN:
run = False
if event.key == pygame.K_PAUSE or event.key == pygame.K_p:
paused = not paused
pygame.mouse.set_pos(displayCenter)
if not paused:
if event.type == pygame.MOUSEMOTION:
mouseMove = [event.pos[i] - displayCenter[i] for i in range(2)]
pygame.mouse.set_pos(displayCenter)
if not paused:
# get keys
keypress = pygame.key.get_pressed()
#mouseMove = pygame.mouse.get_rel()
# init model view matrix
glLoadIdentity()
# apply the look up and down
up_down_angle += mouseMove[1]*0.1
glRotatef(up_down_angle, 1.0, 0.0, 0.0)
# init the view matrix
glPushMatrix()
glLoadIdentity()
# apply the movment
if keypress[pygame.K_w]:
glTranslatef(0,0,0.1)
if keypress[pygame.K_s]:
glTranslatef(0,0,-0.1)
if keypress[pygame.K_d]:
glTranslatef(-0.1,0,0)
if keypress[pygame.K_a]:
glTranslatef(0.1,0,0)
# apply the left and right rotation
glRotatef(mouseMove[0]*0.1, 0.0, 1.0, 0.0)
# multiply the current matrix by the get the new view matrix and store the final vie matrix
glMultMatrixf(viewMatrix)
viewMatrix = glGetFloatv(GL_MODELVIEW_MATRIX)
# apply view matrix
glPopMatrix()
glMultMatrixf(viewMatrix)
glLightfv(GL_LIGHT0, GL_POSITION, [1, -1, 1, 0])
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
glPushMatrix()
glColor4f(0.5, 0.5, 0.5, 1)
glBegin(GL_QUADS)
glVertex3f(-10, -10, -2)
glVertex3f(10, -10, -2)
glVertex3f(10, 10, -2)
glVertex3f(-10, 10, -2)
glEnd()
glTranslatef(-1.5, 0, 0)
glColor4f(0.5, 0.2, 0.2, 1)
gluSphere(sphere, 1.0, 32, 16)
glTranslatef(3, 0, 0)
glColor4f(0.2, 0.2, 0.5, 1)
gluSphere(sphere, 1.0, 32, 16)
glPopMatrix()
pygame.display.flip()
pygame.time.wait(10)
pygame.quit()
我正在创建第一人称视角 RPG,我想在移动鼠标时在 PyOpenGL 中旋转相机(就像其他一些游戏,如 Minecraft)。我可以使用什么函数来执行此操作以及如何执行此操作?
我尝试使用 gluLookAt()
,但我不明白它是如何工作的,尽管我查阅了不同的来源。不知道能不能帮上忙
import sys,pygame
from OpenGL.GL import *
from OpenGL.GLU import *
cmddown = False
#...
keypress = pygame.key.get_pressed()#Move using WASD
if keypress[pygame.K_w]:
glTranslatef(0,0,0.1)
if keypress[pygame.K_s]:
glTranslatef(0,0,-0.1)
if keypress[pygame.K_d]:
glTranslatef(-0.1,0,0)
if keypress[pygame.K_a]:
glTranslatef(0.1,0,0)
mouse_movement = pygame.mouse.get_rel()#Get mouse event
#This is where the "look around" should be happen
pygame.display.flip()
您可以使用 glRotate
to rotate around an axis, by an amount which is given by the relative mouse movement (pygame.mouse.get_rel()
):
mouseMove = pygame.mouse.get_rel()
glRotatef(mouseMove[0]*0.1, 0.0, 1.0, 0.0)
但这不会让您满意,因为如果鼠标离开 window,解决方案将不再有效。
您必须通过 pygame.mouse.set_pos()
in every frame. Get the mouse movement by the pygame.MOUSEMOTION
事件将鼠标置于屏幕中央。例如:
# init mouse movement and center mouse on screen
displayCenter = [scree.get_size()[i] // 2 for i in range(2)]
mouseMove = [0, 0]
pygame.mouse.set_pos(displayCenter)
paused = False
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE or event.key == pygame.K_RETURN:
run = False
if event.key == pygame.K_PAUSE or event.key == pygame.K_p:
paused = not paused
pygame.mouse.set_pos(displayCenter)
if event.type == pygame.MOUSEMOTION:
mouseMove = [event.pos[i] - displayCenter[i] for i in range(2)]
if not paused:
pygame.mouse.set_pos(displayCenter)
请注意,像 glRotate
and glTranslate
这样的操作会设置一个矩阵并将当前矩阵乘以新矩阵。
currentMatrix = currentMatrix * newMatrix
这对于模型动画和变换来说是完美的,但对于第一人称运动来说这是错误的方式,因为必须改变相机位置和视角。
viewMatrix = viewTransformMatrix * viewMatrix
做这样的操作glGetFloatv(GL_MODELVIEW_MATRIX)
and glMultMatrixf
。
通过 gluLookAt
初始化视图矩阵,并在主循环之前通过 glGetFloatv(GL_MODELVIEW_MATRIX)
将视图矩阵加载到变量 (viewMatrix
)。
在每帧的主循环中:
- 加载 Identity matrix (
glLoadIdentity
) - 对视图进行新的转换 (
glRotatef
,glTranslatef
) - 将当前视图矩阵乘以
viewMatrix
(glMultMatrixf
) - 为下一帧将新的视图矩阵加载到
viewMatrix
glMatrixMode(GL_MODELVIEW)
gluLookAt(0, -8, 0, 0, 0, 0, 0, 0, 1)
viewMatrix = glGetFloatv(GL_MODELVIEW_MATRIX)
glLoadIdentity()
# [...]
run = True
while run:
# [...]
# init the view matrix
glLoadIdentity()
# apply the movment
if keypress[pygame.K_w]:
glTranslatef(0,0,0.1)
if keypress[pygame.K_s]:
glTranslatef(0,0,-0.1)
if keypress[pygame.K_d]:
glTranslatef(-0.1,0,0)
if keypress[pygame.K_a]:
glTranslatef(0.1,0,0)
# apply the roation
glRotatef(mouseMove[0]*0.1, 0.0, 1.0, 0.0)
# multiply the current matrix by the get the new view matrix and store the final vie matrix
glMultMatrixf(viewMatrix)
viewMatrix = glGetFloatv(GL_MODELVIEW_MATRIX)
为了向上和向下看,您必须围绕 x 轴应用最后的旋转。旋转的枢轴取决于视点。 必须总结角度,并且必须在视图矩阵之后应用旋转,否则移动会根据角度改变(“高度”)级别。
viewMatrix = viewTransformMatrix * viewMatrix
finlalMatrix = lookUpDownMatrix * viewMatrix
为此,您必须结合上下旋转矩阵并将其乘以 viewMatrix
up_down_angle = 0.0
run = True
while run:
# [...]
# init model view matrix
glLoadIdentity()
# apply the look up and down
up_down_angle += mouseMove[1]*0.1
glRotatef(up_down_angle, 1.0, 0.0, 0.0)
# init the view matrix
glPushMatrix()
glLoadIdentity()
# calculate new `viewMatrix`
# [...]
# apply view matrix
glPopMatrix()
glMultMatrixf(viewMatrix)
请参阅下面的示例程序,它演示了该过程。
请注意,程序 将鼠标保持在 window 的中心 ,因此您不能再“移动”鼠标。
因此,应用程序 可以通过 ESC 或 return.
停止
应用程序可以通过 pause 或 p 暂停。当应用程序暂停时,鼠标未居中到window。
import pygame
from pygame.locals import *
from OpenGL.GL import *
from OpenGL.GLU import *
import math
pygame.init()
display = (400, 300)
scree = pygame.display.set_mode(display, DOUBLEBUF | OPENGL)
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, [0.5, 0.5, 0.5, 1])
glLightfv(GL_LIGHT0, GL_DIFFUSE, [1.0, 1.0, 1.0, 1])
sphere = gluNewQuadric()
glMatrixMode(GL_PROJECTION)
gluPerspective(45, (display[0]/display[1]), 0.1, 50.0)
glMatrixMode(GL_MODELVIEW)
gluLookAt(0, -8, 0, 0, 0, 0, 0, 0, 1)
viewMatrix = glGetFloatv(GL_MODELVIEW_MATRIX)
glLoadIdentity()
# init mouse movement and center mouse on screen
displayCenter = [scree.get_size()[i] // 2 for i in range(2)]
mouseMove = [0, 0]
pygame.mouse.set_pos(displayCenter)
up_down_angle = 0.0
paused = False
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE or event.key == pygame.K_RETURN:
run = False
if event.key == pygame.K_PAUSE or event.key == pygame.K_p:
paused = not paused
pygame.mouse.set_pos(displayCenter)
if not paused:
if event.type == pygame.MOUSEMOTION:
mouseMove = [event.pos[i] - displayCenter[i] for i in range(2)]
pygame.mouse.set_pos(displayCenter)
if not paused:
# get keys
keypress = pygame.key.get_pressed()
#mouseMove = pygame.mouse.get_rel()
# init model view matrix
glLoadIdentity()
# apply the look up and down
up_down_angle += mouseMove[1]*0.1
glRotatef(up_down_angle, 1.0, 0.0, 0.0)
# init the view matrix
glPushMatrix()
glLoadIdentity()
# apply the movment
if keypress[pygame.K_w]:
glTranslatef(0,0,0.1)
if keypress[pygame.K_s]:
glTranslatef(0,0,-0.1)
if keypress[pygame.K_d]:
glTranslatef(-0.1,0,0)
if keypress[pygame.K_a]:
glTranslatef(0.1,0,0)
# apply the left and right rotation
glRotatef(mouseMove[0]*0.1, 0.0, 1.0, 0.0)
# multiply the current matrix by the get the new view matrix and store the final vie matrix
glMultMatrixf(viewMatrix)
viewMatrix = glGetFloatv(GL_MODELVIEW_MATRIX)
# apply view matrix
glPopMatrix()
glMultMatrixf(viewMatrix)
glLightfv(GL_LIGHT0, GL_POSITION, [1, -1, 1, 0])
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
glPushMatrix()
glColor4f(0.5, 0.5, 0.5, 1)
glBegin(GL_QUADS)
glVertex3f(-10, -10, -2)
glVertex3f(10, -10, -2)
glVertex3f(10, 10, -2)
glVertex3f(-10, 10, -2)
glEnd()
glTranslatef(-1.5, 0, 0)
glColor4f(0.5, 0.2, 0.2, 1)
gluSphere(sphere, 1.0, 32, 16)
glTranslatef(3, 0, 0)
glColor4f(0.2, 0.2, 0.5, 1)
gluSphere(sphere, 1.0, 32, 16)
glPopMatrix()
pygame.display.flip()
pygame.time.wait(10)
pygame.quit()