Путаница при переносе PyOpenGL из PyGame в PyQt5 - PullRequest
1 голос
/ 15 февраля 2020

Я пытаюсь собрать пример кода для PyGame PyOpenGL учебник в пример кода для PyQt5 QOpenGLWidget. Цель этого кода - установить куб с одним углом, наклоненным вверх, чтобы определить угол камеры. Он отлично работает в PyGame, но есть несколько проблем с версией PyQt5:

Во-первых, соотношение сторон, кажется, отключено. Во-вторых, окно вызывает paintGL каждый раз, когда я снова активирую его. В-третьих, большинство переменных не передает одно и то же в отношении glTranslatef и glRotatef.

Код, который я использую для PyGame:

import pygame
from pygame.locals import *

from OpenGL.GL import *
from OpenGL.GLU import *

verticies = (
    (1, -1, -1),
    (1, 1, -1),
    (-1, 1, -1),
    (-1, -1, -1),
    (1, -1, 2),
    (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)
    )

colors = (
    (1,0,0),
    (0,1,0),
    (0,0,1),
    (0,1,0),
    (1,1,1),
    (0,1,1),
    (1,0,0),
    (0,1,0),
    (0,0,1),
    (1,0,0),
    (1,1,1),
    (0,1,1),
    )

surfaces = (
    (0,1,2,3),
    (3,2,7,6),
    (6,7,5,4),
    (4,5,1,0),
    (1,5,7,2),
    (4,0,3,6)
    )

def Cube():
    glBegin(GL_QUADS)
    for surface in surfaces:
        x = 0
        for vertex in surface:
            x+=1
            glColor3fv(colors[x])
            glVertex3fv(verticies[vertex])
    glEnd()

    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)
    gluPerspective(45, (display[0]/display[1]), 0.1, 50.0)
    glTranslatef(0,0, -10)    #these two lines set the camera facing at the cube from the position 0, -10, 0.
    glRotatef(-90, 2, 0, 0)

    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()

            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_LEFT:
                    glTranslatef(-0.5,0,0)
                if event.key == pygame.K_RIGHT:
                    glTranslatef(0.5,0,0)

                if event.key == pygame.K_UP:
                    glTranslatef(0,1,0)
                if event.key == pygame.K_DOWN:
                    glTranslatef(0,-1,0)

            if event.type == pygame.MOUSEBUTTONDOWN:
                if event.button == 4:
                    glTranslatef(0,0,1.0)

                if event.button == 5:
                    glTranslatef(0,0,-1.0)

        #glRotatef(1, 3, 1, 1)    #rotation code that was commented out.
        glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
        Cube()
        pygame.display.flip()
        pygame.time.wait(10)
main()

Результат: PyGame version of the cube

Код PyQt5:

import sys

from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.uic import *

from OpenGL.GL import *
from OpenGL.GLUT import *
from OpenGL.GLU import *

class mainWindow(QMainWindow):    #Main class.

    verticies = (
                 (1, -1, -1),
                 (1, 1, -1),
                 (-1, 1, -1),
                 (-1, -1, -1),
                 (1, -1, 2),
                 (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)
            )

    colors = (
              (1,0,0),
              (0,1,0),
              (0,0,1),
              (0,1,0),
              (1,1,1),
              (0,1,1),
              (1,0,0),
              (0,1,0),
              (0,0,1),
              (1,0,0),
              (1,1,1),
              (0,1,1),
             )

    surfaces = (
                (0,1,2,3),
                (3,2,7,6),
                (6,7,5,4),
                (4,5,1,0),
                (1,5,7,2),
                (4,0,3,6)
               )

    def __init__(self):
        super(mainWindow, self).__init__()
        self.width = 700    #Variables used for the setting of the size of everything
        self.height = 600
        self.setGeometry(0, 0, self.width, self.height)    #Set the window size

    def setupUI(self):
        self.openGLWidget = QOpenGLWidget(self)    #Create the GLWidget
        self.openGLWidget.setGeometry(0, 0, self.width, self.height)    #Size it the same as the window.
        self.openGLWidget.initializeGL()
        self.openGLWidget.resizeGL(self.width, self.height)    #Resize GL's knowledge of the window to match the physical size?
        self.openGLWidget.paintGL = self.paintGL    #override the default function with my own?

    def paintGL(self):
        gluPerspective(45, self.width / self.height, 0.1, 50.0)    #set perspective?
        glTranslatef(0, 0, -2)    #I used -10 instead of -2 in the PyGame version.
        glRotatef(-90, 1, 0, 0)    #I used 2 instead of 1 in the PyGame version.

        glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)    #Straight from the PyGame version, with 'self' inserted occasionally

        glBegin(GL_QUADS)    #tell GL to draw surfaces
        for surface in self.surfaces:
            x = 0
            for vertex in surface:
                x+=1
                glColor3fv(self.colors[x])
                glVertex3fv(self.verticies[vertex])
        glEnd()    #tell GL to stop drawing surfaces

        glBegin(GL_LINES)    #tell GL to draw lines
        for edge in self.edges:
            for vertex in edge:
                glVertex3fv(self.verticies[vertex])
        glEnd()    #tell GL to stop drawing lines.

app = QApplication([])
window = mainWindow()
window.setupUI()
window.show()
sys.exit(app.exec_())

Результат:

PyQt5 version of the cube

Когда я переключаюсь на другое окно, затем переключитесь обратно в окно Qt, сцена обновится и снова будет вызван paintGL. Кроме того, куб выглядит раздавленным, а камера действует по-другому. Что я могу сделать, чтобы исправить это?

Python 3.8 Windows 10

1 Ответ

1 голос
/ 15 февраля 2020

Операции матрицы OpenGL (например, gluPerspective, glTranslate, glRotate, ...) не просто устанавливают матрицу. Операции определяют новую матрицу и умножают текущую матрицу на новую матрицу. Причины того, что матрица непрерывно и постепенно изменяется, каждый раз, когда вызывается paintGL.
Проблема может быть легко решена путем загрузки единичной матрицы на glLoadIdentity в начале paintGL ::

class mainWindow(QMainWindow):
    # [...]

    def paintGL(self):
        glLoadIdentity()
        gluPerspective(45, self.width / self.height, 0.1, 50.0)    #set perspective?
        glTranslatef(0, 0, -10)    #I used -10 instead of -2 in the PyGame version.
        glRotatef(-90, 1, 0, 0)    #I used 2 instead of 1 in the PyGame version.

Но Legacy OpenGL предоставляет различные матрицы (см. glMatrixMode).
Рекомендуется надуть матрицу проекции на текущая матрица GL_PROJECTION и матрица вида модели соответствуют текущей матрице GL_MODELVIEW.
Обновите прямоугольник области просмотра (glViewport) и матрицу проекции в обратном вызове события изменения размера (resizeGL ). Установите матрицу вида модели в paintGL:

class mainWindow(QMainWindow):
    # [...]

    def setupUI(self):
        # [...]

        self.openGLWidget.paintGL  = self.paintGL
        self.openGLWidget.resizeGL = self.resizeGL

    def resizeGL(self, width, height):
        self.width, self.height = width, height
        # update viewport
        glViewport(0, 0, self.width, self.height)
        # set projection matrix
        glMatrixMode(GL_PROJECTION)
        glLoadIdentity()
        gluPerspective(45, self.width / self.height, 0.1, 50.0)    #set perspective?

    def paintGL(self):
        glMatrixMode(GL_MODELVIEW)
        glLoadIdentity()
        glTranslatef(0, 0, -10)    #I used -10 instead of -2 in the PyGame version.
        glRotatef(-90, 1, 0, 0)    #I used 2 instead of 1 in the PyGame version.

        glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)    #Straight from the PyGame version, with 'self' inserted occasionally

        glBegin(GL_QUADS)    #tell GL to draw surfaces
        for surface in self.surfaces:
            x = 0
            for vertex in surface:
                x+=1
                glColor3fv(self.colors[x])
                glVertex3fv(self.verticies[vertex])
        glEnd()    #tell GL to stop drawing surfaces

        glBegin(GL_LINES)    #tell GL to draw lines
        for edge in self.edges:
            for vertex in edge:
                glVertex3fv(self.verticies[vertex])
        glEnd()    #tell GL to stop drawing lines.
...