Как PyOpenGL выбирает цвета для рисования каждой фигуры? - PullRequest
1 голос
/ 19 февраля 2020

Я нахожусь в центре проекта PyQt5 PyOpenGL. Я пытаюсь нарисовать белый каркасный куб с кучей цветных solid кубов. Каркасные кубы взяты из списка точек кортежей и списка ссылок на эти точки. Solid кубы взяты из списка кортежей ссылок на точки. Вот код куба:

class cube():
    render = True
    solid = False
    color = (1, 1, 1)

    def config(self, x, y, z, size = 0.1, solid = False, color = (1, 1, 1)):
        self.solid = solid
        self.color = color
        self.size = size / 2
        s = self.size
        self.vertices = [
                         (-s + x, s + y, -s + z),
                         (s + x, s + y, -s + z),
                         (s + x, -s + y, -s + z),
                         (-s + x, -s + y, -s + z),
                         (-s + x, s + y, s + z),
                         (s + x, s + y, s + z),
                         (s + x, -s + y, s + z),
                         (-s + x, -s + y, s + z)
                       ]
        self.edges = [
                      (0,1), (0,3), (0,4), (2,1),
                      (2,3), (2,6), (7,3), (7,4),
                      (7,6), (5,1), (5,4), (5,6)
                     ]
        self.facets = [
                       (0, 1, 2, 3), (0, 1, 6, 5),
                       (0, 3, 7, 4), (6, 5, 1, 2),
                       (6, 7, 4, 5), (6, 7, 3, 2)
                      ]
    def show(self):
        self.render = True
    def hide(self):
        self.render = False

Чтобы отобразить куб, я получаю размер списка, который содержится в моем классе mainWindow, а затем добавляю экземпляр класса куба в этот список. Затем я могу ссылаться на этот экземпляр, используя размер перед добавлением. Вот код для функции рендеринга:

def paintGL(self):
    glLoadIdentity()
    gluPerspective(45, self.width / self.height, 0.1, 110.0)    #set perspective?
    glTranslatef(0, 0, self.zoomLevel)    #I used -10 instead of -2 in the PyGame version.
    glRotatef(self.rotateDegreeV, 1, 0, 0)    #I used 2 instead of 1 in the PyGame version.
    glRotatef(self.rotateDegreeH, 0, 0, 1)
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)

    if len(self.shapes) != 0:
        glBegin(GL_LINES)
        for s in self.shapes:
            if s.render and not s.solid:
                for e in s.edges:
                    for v in e:
                        glVertex3fv(s.vertices[v])
        glEnd()

        glBegin(GL_QUADS)
        for s in self.shapes:
            if s.render and s.solid:
                for f in s.facets:
                    for v in f:
                        glColor3fv(s.color)
                        glVertex3fv(s.vertices[v])
        glEnd()

Если я рендерину только один куб в каркасном режиме, он будет белым. Если я добавлю красный solid куб и синий solid куб после него, каркасный куб будет окрашен в последний использованный цвет, каким бы он ни был. Например:

    self.shapes.append(self.cube())
    self.shapes.append(self.cube())
    self.shapes.append(self.cube())
    self.shapes[0].config(-1, 0, 0, size = 0.5, solid = False)
    self.shapes[1].config(0, 0, 0, size = 0.5, solid = True, color = (1, 0, 0))
    self.shapes[2].config(1, 0, 0, size = 0.5, solid = True, color = (0, 0, 1))

Результат:

cubes

Как сделать так, чтобы мои каркасы отображались по умолчанию белым или другим цветом ? Я ожидал бы, что glClear() сбросит его и нарисует каркас белым, учитывая, что он первый.

Вот полный код:

import sys
from PyQt5.QtWidgets import (
                             QApplication, QMainWindow, QSlider,
                             QOpenGLWidget, QLabel, QPushButton
                            )
from PyQt5.QtCore import Qt
from OpenGL.GL import (
                       glLoadIdentity, glTranslatef, glRotatef,
                       glClear, glBegin, glEnd,
                       glColor3fv, glVertex3fv,
                       GL_COLOR_BUFFER_BIT, GL_DEPTH_BUFFER_BIT,
                       GL_QUADS, GL_LINES
                      )
from OpenGL.GLU import gluPerspective

class mainWindow(QMainWindow):    #Main class.
    shapes = []    #this will hold instances of the following classes: 
    zoomLevel = -10
    rotateDegreeV = -90
    rotateDegreeH = 0

    class cube():
        render = True
        solid = False
        color = (1, 1, 1)

        def config(self, x, y, z, size = 0.1, solid = False, color = (1, 1, 1)):
            self.solid = solid
            self.color = color
            self.size = size
            s = self.size / 2
            self.vertices = [
                             (-s + x, s + y, -s + z),
                             (s + x, s + y, -s + z),
                             (s + x, -s + y, -s + z),
                             (-s + x, -s + y, -s + z),
                             (-s + x, s + y, s + z),
                             (s + x, s + y, s + z),
                             (s + x, -s + y, s + z),
                             (-s + x, -s + y, s + z)
                            ]
            self.edges = [
                          (0,1), (0,3), (0,4), (2,1),
                          (2,3), (2,6), (7,3), (7,4),
                          (7,6), (5,1), (5,4), (5,6)
                         ]
            self.facets = [
                           (0, 1, 2, 3), (0, 1, 6, 5),
                           (0, 3, 7, 4), (6, 5, 1, 2),
                           (6, 7, 4, 5), (6, 7, 3, 2)
                          ]
        def show(self):
            self.render = True
        def hide(self):
            self.render = False

    def keyPressEvent(self, event):    #This is the keypress detector. I use this to determine input to edit grids.
        try:
            key = event.key()
            if key == 87:
                self.rotateV(5)
            elif key == 65:
                self.rotateH(5)
            elif key == 83:
                self.rotateV(-5)
            elif key == 68:
                self.rotateH(-5)
            elif key == 67:
                self.zoom(1)
            elif key == 88:
                self.zoom(-1)
        except:
            pass

    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
        self.shapes.append(self.cube())
        self.shapes.append(self.cube())
        self.shapes.append(self.cube())
        self.shapes[0].config(-1, 0, 0, size = 0.5, solid = False)
        self.shapes[1].config(0, 0, 0, size = 0.5, solid = True, color = (1, 0, 0))
        self.shapes[2].config(1, 0, 0, size = 0.5, solid = True, color = (0, 0, 1))

    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 zoom(self, value):
        self.zoomLevel += value
        self.openGLWidget.update()

    def rotateV(self, value):
        self.rotateDegreeV += value
        self.openGLWidget.update()

    def rotateH(self, value):
        self.rotateDegreeH += value
        self.openGLWidget.update()

    def paintGL(self):
        glLoadIdentity()
        gluPerspective(45, self.width / self.height, 0.1, 110.0)    #set perspective?
        glTranslatef(0, 0, self.zoomLevel)    #I used -10 instead of -2 in the PyGame version.
        glRotatef(self.rotateDegreeV, 1, 0, 0)    #I used 2 instead of 1 in the PyGame version.
        glRotatef(self.rotateDegreeH, 0, 0, 1)
        glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)

        if len(self.shapes) != 0:
            glBegin(GL_LINES)
            for s in self.shapes:
                if s.render and not s.solid:
                    for e in s.edges:
                        for v in e:
                            glVertex3fv(s.vertices[v])
            glEnd()

            glBegin(GL_QUADS)
            for s in self.shapes:
                if s.render and s.solid:
                    for f in s.facets:
                        for v in f:
                            glColor3fv(s.color)
                            glVertex3fv(s.vertices[v])
            glEnd()


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

1 Ответ

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

OpenGL - это государственный двигатель. Когда состояние установлено, оно сохраняется до тех пор, пока оно не будет изменено снова, даже за рамками. Текущий цвет является глобальным состоянием. Когда вызывается glColor* , устанавливается текущий цвет.
Когда вызывается glVertex* , тогда координаты текущего цвета, нормали и текстуры связаны с вершина.

Это означает, что надлежащий цвет должен быть установлен перед указанием вершин. Вы пропустили установку атрибута цвета, прежде чем рисовать каркасный куб:

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

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

        if len(self.shapes) != 0:
            glBegin(GL_LINES)
            for s in self.shapes:
                glColor3fv(s.color)  # <------------------------
                if s.render and not s.solid:
                    for e in s.edges:
                        for v in e:
                            glVertex3fv(s.vertices[v])
            glEnd()

            glBegin(GL_QUADS)
            for s in self.shapes:
                glColor3fv(s.color)
                if s.render and s.solid:
                    for f in s.facets:
                        for v in f:
                            glVertex3fv(s.vertices[v])
            glEnd()

Обратите внимание, что нет необходимости устанавливать текущий цвет перед каждым вызовом glVertex3fv. Достаточно один раз установить текущий цвет при его изменении. Новый цвет связан со всеми следующими вершинами.

...