使用自定义 QSurfaceFormat 时出现 PySide2 OpenGL 错误

PySide2 OpenGL error when using custom QSurfaceFormat

我正在使用 QSurfaceFormat 明确定义 OpenGL Core 配置文件 并设置 minor/major 版本。问题是,自定义表面格式的分配给我以下错误:

Traceback (most recent call last):
  File "/home/artem/PycharmProjects/PolyEdit3D/PolyEdit3D/Widgets/PlyViewport.py", line 22, in initializeGL
    gl.glEnable(gl.GL_COLOR_BUFFER_BIT)
  File "/home/artem/.local/lib/python3.7/site-packages/OpenGL/platform/baseplatform.py", line 415, in __call__
    return self( *args, **named )
  File "src/errorchecker.pyx", line 58, in OpenGL_accelerate.errorchecker._ErrorChecker.glCheckError
OpenGL.error.GLError: GLError(
    err = 1280,
    description = b'invalid enumerant',
    baseOperation = glEnable,
    cArguments = (GL_COLOR_BUFFER_BIT,)
)
Traceback (most recent call last):
  File "/home/artem/PycharmProjects/PolyEdit3D/PolyEdit3D/Widgets/PlyViewport.py", line 69, in paintGL
    gl.glDrawElements(gl.GL_TRIANGLES, 6, gl.GL_UNSIGNED_INT, ctypes.c_void_p(0))
  File "src/latebind.pyx", line 39, in OpenGL_accelerate.latebind.LateBind.__call__
  File "src/wrapper.pyx", line 318, in OpenGL_accelerate.wrapper.Wrapper.__call__
  File "src/wrapper.pyx", line 311, in OpenGL_accelerate.wrapper.Wrapper.__call__
  File "/home/artem/.local/lib/python3.7/site-packages/OpenGL/platform/baseplatform.py", line 415, in __call__
    return self( *args, **named )
  File "src/errorchecker.pyx", line 58, in OpenGL_accelerate.errorchecker._ErrorChecker.glCheckError
OpenGL.error.GLError: GLError(
    err = 1282,
    description = b'invalid operation',
    baseOperation = glDrawElements,
    pyArgs = (
        GL_TRIANGLES,
        6,
        GL_UNSIGNED_INT,
        c_void_p(None),
    ),
    cArgs = (
        GL_TRIANGLES,
        6,
        GL_UNSIGNED_INT,
        c_void_p(None),
    ),
    cArguments = (
        GL_TRIANGLES,
        6,
        GL_UNSIGNED_INT,
        c_void_p(None),
    )
)

如您所见,gl.glEnable(gl.GL_COLOR_BUFFER_BIT) 和其他简单内容中存在错误。没有指定 QSurfaceFormat 一切都很好。

这是我的表面格式 class:

class GLSurfaceFormat(QtGui.QSurfaceFormat):
    def __init__(self):
        super(GLSurfaceFormat, self).__init__()
        self.__initSurface()

    def __initSurface(self):
        self.setRenderableType(QtGui.QSurfaceFormat.OpenGL)
        self.setMinorVersion(3)
        self.setMajorVersion(4)
        self.setProfile(QtGui.QSurfaceFormat.CoreProfile)
        self.setColorSpace(QtGui.QSurfaceFormat.sRGBColorSpace)
        self.setSwapBehavior(QtGui.QSurfaceFormat.DoubleBuffer)

我是这样分配的:

    QApplication.setAttribute(Qt.AA_UseDesktopOpenGL)
    QSurfaceFormat.setDefaultFormat(GLSurfaceFormat())

    app = QApplication(sys.argv)
    ...

你能告诉我,QSurfaceFormat 用法有什么错误吗?还是有其他问题?

!更新! 这是一个简单的示例,您可以在其中重现相同的行为:

from PySide2 import QtCore, QtGui, QtWidgets
from OpenGL import GL as gl
from OpenGL.GL.shaders import compileProgram, compileShader
import numpy as np
import ctypes
import sys
import glm

class PlyViewportWidget(QtWidgets.QOpenGLWidget):
    def __init__(self):
        super(PlyViewportWidget, self).__init__(parent=None)
        self.vao = None
        self.vbo = None
        self.ebo = None
        self.shaderProg = None
        self.isWireframe = False

    def initializeGL(self):
        gl.glEnable(gl.GL_COLOR_BUFFER_BIT)
        gl.glClearColor(0.4, 0.4, 0.4, 1)

        with open("fragment.glsl", 'r') as f:
            fragment = compileShader(f.read(), gl.GL_FRAGMENT_SHADER)
        with open("vertex.glsl", "r") as f:
            vertex = compileShader(f.read(), gl.GL_VERTEX_SHADER)
        self.shaderProg = compileProgram(vertex, fragment)

        vertices = np.array(
            [
                0.5, 0.5, 0.0,
                0.5, -0.5, 0.0,
                -0.5, -0.5, 0.0,
                -0.5, 0.5, 0.0
            ], dtype=ctypes.c_float
        )
        indices = np.array(
            [
                0, 1, 3,
                1, 2, 3
            ], dtype=ctypes.c_uint
        )

        self.vbo = gl.glGenBuffers(1)
        self.ebo = gl.glGenBuffers(1)

        gl.glBindBuffer(gl.GL_ARRAY_BUFFER, self.vbo)
        gl.glBufferData(gl.GL_ARRAY_BUFFER, vertices.nbytes, vertices, gl.GL_STATIC_DRAW)

        gl.glBindBuffer(gl.GL_ELEMENT_ARRAY_BUFFER, self.ebo)
        gl.glBufferData(gl.GL_ELEMENT_ARRAY_BUFFER, indices.nbytes, indices, gl.GL_STATIC_DRAW)

        gl.glEnableVertexAttribArray(0)
        gl.glVertexAttribPointer(0, 3, gl.GL_FLOAT, gl.GL_FALSE, 3 * ctypes.sizeof(ctypes.c_float), ctypes.c_void_p(0))

        gl.glBindBuffer(gl.GL_ARRAY_BUFFER, 0)
        gl.glBindVertexArray(0)

        gl.glUseProgram(self.shaderProg)

    def paintGL(self):
        gl.glClearColor(0.4, 0.4, 0.4, 1.0)
        gl.glClear(gl.GL_COLOR_BUFFER_BIT)

        trans = self.getTransformMatrix()
        #u_transform = gl.glGetUniformLocation(self.shaderProg, "u_transform")
        #gl.glUniformMatrix4fv(u_transform, 1, gl.GL_FALSE, u_transform)

        gl.glBindVertexArray(self.vao)
        gl.glDrawElements(gl.GL_TRIANGLES, 6, gl.GL_UNSIGNED_INT, ctypes.c_void_p(0))

    def resizeGL(self, w:int, h:int):
        gl.glViewport(0, 0, w, h)

    def keyPressEvent(self, event:QtGui.QKeyEvent):
        self.makeCurrent()
        if event.key() == QtCore.Qt.Key_Z and not self.isWireframe:
            gl.glPolygonMode(gl.GL_FRONT_AND_BACK, gl.GL_LINE)
            self.isWireframe = True
            self.update()
        elif event.key() == QtCore.Qt.Key_Z and self.isWireframe:
            gl.glPolygonMode(gl.GL_FRONT_AND_BACK, gl.GL_FILL)
            self.isWireframe = False
            self.update()

        event.accept()

    def getTransformMatrix(self):
        return glm.rotate(glm.mat4(1.0), glm.radians(90.0), glm.vec3(1.0, 0.0, 0.0))


class GLSurfaceFormat(QtGui.QSurfaceFormat):
    def __init__(self):
        super(GLSurfaceFormat, self).__init__()
        self.__initSurface()

    def __initSurface(self):
        self.setRenderableType(QtGui.QSurfaceFormat.OpenGL)
        self.setMinorVersion(3)
        self.setMajorVersion(4)
        self.setProfile(QtGui.QSurfaceFormat.CoreProfile)
        self.setColorSpace(QtGui.QSurfaceFormat.sRGBColorSpace)
        self.setSwapBehavior(QtGui.QSurfaceFormat.DoubleBuffer)


if __name__ == '__main__':

    QtWidgets.QApplication.setAttribute(QtCore.Qt.AA_UseDesktopOpenGL)
    # Uncomment to get this damn error!!!
    #QtGui.QSurfaceFormat.setDefaultFormat(GLSurfaceFormat())

    app = QtWidgets.QApplication(sys.argv)
    window = PlyViewportWidget()
    window.show()
    sys.exit(app.exec_())

vertex.glsl

#version 330 core

layout (location = 0) in vec3 aPos;

void main()
{
    gl_Position = vec4(aPos, 1.0);
}

fragment.glsl

#version 330 core

out vec4 FragColor;

void main()
{
    FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);
} 

GL_COLOR_BUFFER_BIT 不是 glEnable, but it is a proper argument for glClear 的有效枚举常量。
调用glClear,清除颜色由glClearColor:

设置后
class PlyViewportWidget(QtWidgets.QOpenGLWidget):
    # [...]

    def initializeGL(self):
        
        #gl.glEnable(gl.GL_COLOR_BUFFER_BIT) <---- DELETE
        
        gl.glClearColor(0.4, 0.4, 0.4, 1)

        gl.glClear(gl.GL_COLOR_BUFFER_BIT) # <---- ADD

您错过了创建命名 Vertex Array Object:

class PlyViewportWidget(QtWidgets.QOpenGLWidget):
    # [...]

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

        self.vao = gl.glGenVertexArrays(1) # <--- 
        gl.glBindVertexArray(self.vao)     # <--- 

        self.vbo = gl.glGenBuffers(1)
        self.ebo = gl.glGenBuffers(1)

        gl.glBindBuffer(gl.GL_ARRAY_BUFFER, self.vbo)
        gl.glBufferData(gl.GL_ARRAY_BUFFER, vertices.nbytes, vertices, gl.GL_STATIC_DRAW)

        gl.glBindBuffer(gl.GL_ELEMENT_ARRAY_BUFFER, self.ebo)
        gl.glBufferData(gl.GL_ELEMENT_ARRAY_BUFFER, indices.nbytes, indices, gl.GL_STATIC_DRAW)

        gl.glEnableVertexAttribArray(0)
        gl.glVertexAttribPointer(0, 3, gl.GL_FLOAT, gl.GL_FALSE, 3 * ctypes.sizeof(ctypes.c_float), ctypes.c_void_p(0))

        gl.glBindBuffer(gl.GL_ARRAY_BUFFER, 0)
        gl.glBindVertexArray(0)