PyOpenGL 如何选择颜色来绘制每个形状?

Posted

技术标签:

【中文标题】PyOpenGL 如何选择颜色来绘制每个形状?【英文标题】:How does PyOpenGL pick the colors to draw each shape? 【发布时间】:2020-02-18 21:05:47 【问题描述】:

我正在进行一个 PyQt5 PyOpenGL 项目。我正在尝试用一堆彩色实心立方体绘制一个白色线框立方体。线框立方体是从元组点列表和对这些点的元组引用列表中绘制的。实心立方体是从对点的元组引用列表中绘制的。这是立方体代码:

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

如果我只在线框中渲染一个立方体,它会渲染为白色。如果我在其后添加一个红色实心立方体和一个蓝色实心立方体,则线框立方体将以最后使用的颜色着色,无论它是什么颜色。例如:

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

结果:

如何使我的线框呈现为默认的白色或其他颜色?我希望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】:

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之前,不必设置当前颜色。当它被改变时,设置一次当前颜色就足够了。新颜色与以下所有顶点相关联。

【讨论】:

以上是关于PyOpenGL 如何选择颜色来绘制每个形状?的主要内容,如果未能解决你的问题,请参考以下文章

如何绘制响应形状[关闭]

我想在 UIView 上绘制像苹果形状这样的自定义形状

单击按钮时如何显示颜色选择器?

Android:绘制自定义形状

如何绘制不同颜色的填充路径/形状

WPS如何做下面有箭头的流程图