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

2024-01-26

我正在创建一个第一人称视角 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 https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glRotate.xml绕轴旋转,旋转量由相对鼠标移动给出(pygame.mouse.get_rel() https://www.pygame.org/docs/ref/mouse.html#pygame.mouse.get_focused):

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

但这不会让您满意,因为如果鼠标离开窗口,该解决方案将不再起作用。
您必须将鼠标置于屏幕中央pygame.mouse.set_pos() https://www.pygame.org/docs/ref/mouse.html#pygame.mouse.set_pos在每一帧中。通过以下方式获取鼠标移动pygame.MOUSEMOTION https://www.pygame.org/docs/ref/event.html事件。例如。:

# 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 https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glRotate.xml and glTranslate https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glTranslate.xml设置一个矩阵并将当前矩阵乘以新矩阵。

currentMatrix = currentMatrix * newMatrix

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

viewMatrix = viewTransformMatrix * viewMatrix

进行这样的操作glGetFloatv(GL_MODELVIEW_MATRIX) https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glGet.xml and glMultMatrixf https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glMultMatrix.xml.
初始化视图矩阵gluLookAt https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/gluLookAt.xml并将视图矩阵加载到变量(viewMatrix) by glGetFloatv(GL_MODELVIEW_MATRIX),在主循环之前。

在每一帧的主循环中:

  • 加载单位矩阵 https://en.wikipedia.org/wiki/Identity_matrix (glLoadIdentity https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glLoadIdentity.xml)
  • 对视图进行新的转换(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)

See the following example program, which demonstrates the process.
Note, the program keeps the mouse in the center of the window, so you can't "move" the mouse any more. Therefore the application can be stopped by ESC or return.
The application can be paused by pause or p. When the application is paused, the mouse is not centered to the 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()
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在 PyOpenGL 上创建一个可以对鼠标移动进行“透视旋转”的相机? 的相关文章

随机推荐