首页 > 解决方案 > 如何在 PyOpenGL 上创建一个可以对鼠标移动进行“透视旋转”的相机?

问题描述

我正在创建一个第一人称视角的 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()

标签: pythonopenglpygamepyopenglopengl-compat

解决方案


您可以使用glRotate相对鼠标移动 ( ) 给定的量围绕轴旋转pygame.mouse.get_rel()

mouseMove = pygame.mouse.get_rel()
glRotatef(mouseMove[0]*0.1, 0.0, 1.0, 0.0)

但这不会让您满意,因为如果鼠标离开窗口,该解决方案将不再有效。
您必须pygame.mouse.set_pos()在每一帧中将鼠标置于屏幕中间。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 并将glTranslate当前矩阵乘以新矩阵。

currentMatrix = currentMatrix * newMatrix

这对于模型动画和变换来说是完美的,但对于第一人称运动来说,这是错误的方式,因为必须改变相机的位置和视角。

viewMatrix = viewTransformMatrix * viewMatrix

做这样的操作glGetFloatv(GL_MODELVIEW_MATRIX)glMultMatrixf. 在主循环之前,
通过 初始化视图矩阵gluLookAt并将视图矩阵加载到变量 ( viewMatrix)中。glGetFloatv(GL_MODELVIEW_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)

请参阅以下示例程序,它演示了该过程。
请注意,程序将鼠标保持在窗口的中心,因此您不能再“移动”鼠标。因此应用程序可以通过ESCreturn停止。
应用程序可以通过暂停pausep。当应用程序暂停时,鼠标不在窗口的中心。

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

推荐阅读