PyOpengGL 在我编译着色器时引发错误

PyOpengGL raises error when I compile shaders

我 运行 通过积累 OpenGL 着色器教程以及混合和匹配的东西,试图实现自定义着色器。我有以下 Python 代码和回溯:

import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QOpenGLWidget
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,
                       shaders, GL_VERTEX_SHADER, GL_FRAGMENT_SHADER
                      )
from OpenGL.GLU import gluPerspective

class mainWindow(QMainWindow):    #Main class.
    def keyPressEvent(self, event):    #This is the keypress detector.
        try:
            key = event.key()
        except:
            key = -1    
        #print(key)
        if key == 16777216:
            exit()

    vertices = [
                (-1, 1, 0),
                (1, 1, 0),
                (1, -1, 0),
                (-1, -1, 0)
               ]
    wires = [
             (0, 1),
             (1, 2),
             (2, 3),
             (0, 3)
            ]
    facets = [
             (0, 1, 2, 3)
            ]
    zoomLevel = -5
    rotateDegreeH = 0
    rotateDegreeV = -45

    vertShaderCode = """#version 120    
void main() {
    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}"""
    fragShaderCode = """#version 120
void main() {
    gl_FragColor = vec4( 0, 1, 0, 1 );
}"""

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

        #make shaders
        VERTEX_SHADER = shaders.compileShader(self.vertShaderCode, GL_VERTEX_SHADER)

        FRAGMENT_SHADER = shaders.compileShader(self.fragShaderCode, GL_FRAGMENT_SHADER)

        self.shader = shaders.compileProgram(VERTEX_SHADER,FRAGMENT_SHADER)

        self.openGLWidget = QOpenGLWidget(self)    #Create the GLWidget
        self.openGLWidget.setGeometry(0, 0, self.sizeX, self.sizeY)
        self.openGLWidget.initializeGL()
        self.openGLWidget.resizeGL(self.sizeX, self.sizeY)    #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 nav(self, hVal = 0, vVal = 0, zVal = 0):
        self.zoomLevel += zVal
        self.rotateDegreeH += hVal
        self.rotateDegreeV += vVal
        self.openGLWidget.update()

    def paintGL(self):
        #This function uses shape objects, such as cube() or mesh(). Shape objects require the following:
        #a list named 'vertices' - This list is a list of points, from which edges and faces are drawn.
        #a list named 'wires'    - This list is a list of tuples which refer to vertices, dictating where to draw wires.
        #a list named 'facets'   - This list is a list of tuples which refer to vertices, ditating where to draw facets.
        #a bool named 'render'   - This bool is used to dictate whether or not to draw the shape.
        #a bool named 'drawWires' - This bool is used to dictate whether wires should be drawn.
        #a bool named 'drawFaces' - This bool is used to dictate whether facets should be drawn.

        shaders.glUseProgram(self.shader)
        glLoadIdentity()
        gluPerspective(45, self.sizeX / self.sizeY, 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)

        glBegin(GL_LINES)

        for w in self.wires:
            for v in w:
                glVertex3fv(self.vertices[v])
        glEnd()

        glBegin(GL_QUADS)

        for f in self.facets:
            for v in f:
                glVertex3fv(self.vertices[v])
        glEnd()

app = QApplication([])
window = mainWindow()
window.show()
sys.exit(app.exec_())
C:\Users\ccronk22\Documents\Python\glShaders>pygl002.py
Traceback (most recent call last):
  File "C:\Users\ccronk22\AppData\Local\Programs\Python\Python38\lib\site-packages\OpenGL\latebind.py", line 43, in __call__
    return self._finalCall( *args, **named )
TypeError: 'NoneType' object is not callable

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Users\ccronk22\Documents\Python\glShaders\pyGL002.py", line 109, in <module>
    window = mainWindow()
  File "C:\Users\ccronk22\Documents\Python\glShaders\pyGL002.py", line 59, in __init__
    VERTEX_SHADER = shaders.compileShader(self.vertShaderCode, GL_VERTEX_SHADER)
  File "C:\Users\ccronk22\AppData\Local\Programs\Python\Python38\lib\site-packages\OpenGL\GL\shaders.py", line 228, in compileShader
    shader = glCreateShader(shaderType)
  File "C:\Users\ccronk22\AppData\Local\Programs\Python\Python38\lib\site-packages\OpenGL\latebind.py", line 46, in __call__
    self._finalCall = self.finalise()
  File "C:\Users\ccronk22\AppData\Local\Programs\Python\Python38\lib\site-packages\OpenGL\extensions.py", line 242, in finalise
    raise error.NullFunctionError(
OpenGL.error.NullFunctionError: Attempt to call an undefined alternate function (glCreateShader, glCreateShaderObjectARB), check for bool(glCreateShader) before calling

C:\Users\ccronk22\Documents\Python\glShaders>

如果您注释掉 compileShader、compileProgram 和 useProgram 行,以上代码运行良好。从上面的角度看,它会产生一个白色的正方形:

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

        #make shaders
        #VERTEX_SHADER = shaders.compileShader(self.vertShaderCode, GL_VERTEX_SHADER)

        #FRAGMENT_SHADER = shaders.compileShader(self.fragShaderCode, GL_FRAGMENT_SHADER)

        #self.shader = shaders.compileProgram(VERTEX_SHADER,FRAGMENT_SHADER)
[…]
    def paintGL(self):
        #This function uses shape objects, such as cube() or mesh(). Shape objects require the following:
        #a list named 'vertices' - This list is a list of points, from which edges and faces are drawn.
        #a list named 'wires'    - This list is a list of tuples which refer to vertices, dictating where to draw wires.
        #a list named 'facets'   - This list is a list of tuples which refer to vertices, ditating where to draw facets.
        #a bool named 'render'   - This bool is used to dictate whether or not to draw the shape.
        #a bool named 'drawWires' - This bool is used to dictate whether wires should be drawn.
        #a bool named 'drawFaces' - This bool is used to dictate whether facets should be drawn.

        #shaders.glUseProgram(self.shader)

一些附加信息:

我正在尝试使 http://pyopengl.sourceforge.net/context/tutorials/shader_1.html 中的代码在 PyQt5 上下文中工作,仅使用 OpenGL 和 PyQt5 API。本教程是在 Python2 中编写的。我正在使用 Python3.

那么,我的着色器编译有什么问题吗?是在 Python 还是 GLSL 中?

要编译和 link 着色器,您需要一个有效的当前 OpenGL Context。请注意,调用任何 OpenGL 指令时,OpenGL 上下文必须是最新的。 QOpenGLWidget 提供虚拟方法

def initializeGL ()
def paintGL ()
def resizeGL (w, h)

OpenGL 上下文处于活动状态。此方法是回调,由 Qts 事件处理调用。不要在您的代码中调用它们。

initializeGL 回调中创建着色器:

class mainWindow(QMainWindow):

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

        self.openGLWidget = QOpenGLWidget(self)    #Create the GLWidget
        self.openGLWidget.setGeometry(0, 0, self.sizeX, self.sizeY)
        self.openGLWidget.resizeGL(self.sizeX, self.sizeY)    #Resize GL's knowledge of the window to match the physical size?
        self.openGLWidget.initializeGL = self.initializeGL 
        self.openGLWidget.paintGL = self.paintGL    #override the default function with my own?
        self.shader = None

    def nav(self, hVal = 0, vVal = 0, zVal = 0):
        self.zoomLevel += zVal
        self.rotateDegreeH += hVal
        self.rotateDegreeV += vVal
        self.openGLWidget.update()

    def initializeGL(self):
        #make shaders
        VERTEX_SHADER = shaders.compileShader(self.vertShaderCode, GL_VERTEX_SHADER)
        FRAGMENT_SHADER = shaders.compileShader(self.fragShaderCode, GL_FRAGMENT_SHADER)
        self.shader = shaders.compileProgram(VERTEX_SHADER,FRAGMENT_SHADER)

    def paintGL(self):

        glUseProgram(self.shader)
        # [...]

(您的代码中缺少 glUseProgram 的导入)