glReadPixels 显示 PyQt5 错误。使用 GLUT Window 它工作正常

glReadPixels is showing error with PyQt5. with GLUT Window it is working properly

我正在开发一个软件,其中有一个 OpenGL window。我正在使用 PyQt5 创建软件的 GUI,对于 opengGL Window 我正在使用 QOpengGLWidget 而对于对象选择,我正在使用 Stencil Buffer 并阅读 STENCIL_INDEX 作为 mousePressEvent(self, event) 上的以下函数:

id = glReadPixels(event.x(), self.height - event.y() - 1, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)

但是当我使用 Qt5 GUI 并显示以下错误时,这不起作用:

Traceback (most recent call last):
  File "/home/akv26/Desktop/internProject/Main/opengl.py", line 92, in mousePressEvent
    val = glReadPixels(event.x(), self.height - event.y() - 1, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)
  File "/home/akv26/.local/lib/python3.7/site-packages/OpenGL/GL/images.py", line 371, in glReadPixels
    imageData
  File "/home/akv26/.local/lib/python3.7/site-packages/OpenGL/platform/baseplatform.py", line 402, in __call__
    return self( *args, **named )
  File "/home/akv26/.local/lib/python3.7/site-packages/OpenGL/error.py", line 232, in glCheckError
    baseOperation = baseOperation,
OpenGL.error.GLError: GLError(
    err = 1282,
    description = b'invalid operation',
    baseOperation = glReadPixels,
    cArguments = (
        183,
        228,
        1,
        1,
        GL_DEPTH_COMPONENT,
        GL_FLOAT,
        array([[0.]], dtype=float32),
    )
)
Aborted (core dumped)

这是我的自定义代码 QOpenGLWidget:

import sys
from PyQt5.QtWidgets import QOpenGLWidget
from PyQt5.QtCore import QSize, Qt
from OpenGL.GL import *
from OpenGL.GLUT import *
from OpenGL.GLU import *
from math import *


class MyGLWidget(QOpenGLWidget):

    def initializeGL(self):
        glutInit()

        glClearColor(1.0, 0.5, 0.2, 0.5)
        glEnable(GL_DEPTH_TEST)
        glEnable(GL_LIGHTING)
        glEnable(GL_COLOR_MATERIAL)
        glEnable(GL_LIGHT0)
        glEnable(GL_LIGHT1)
        glEnable(GL_NORMALIZE)

        glShadeModel(GL_SMOOTH)

    def resizeGL(self, w, h):
        self.height=h
        if h==0:
            h=1
        ratio =  w * 1.0 / h

        # Use the Projection Matrix
        glMatrixMode(GL_PROJECTION)

        # Reset Matrix
        glLoadIdentity()

        # Set the viewport to be the entire window
        glViewport(0, 0, w, h)

        # Set the correct perspective.
        gluPerspective(45.0, ratio, 0.1, 100.0)

        # Get Back to the Modelview
        glMatrixMode(GL_MODELVIEW)

    def paintGL(self):
        glClearStencil(0)
        glClear(GL_STENCIL_BUFFER_BIT)

        glEnable(GL_STENCIL_TEST)
        glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE)

        ambientColor = [0.2,0.2,0.2,1.0]
        glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambientColor)

        lightColor0 = [0.5,0.5,0.5,1.0]
        lightPos0 = [0, 0, -10.0, 1.0]
        glLightfv(GL_LIGHT0, GL_DIFFUSE, lightColor0)
        glLightfv(GL_LIGHT0, GL_POSITION, lightPos0)

        lightColor1 = [0.5, 0.2, 0.2, 1.0]
        lightPos1 = [-1.0, 0.5, 0.5, 0.0]
        glLightfv(GL_LIGHT1, GL_DIFFUSE, lightColor1)
        glLightfv(GL_LIGHT1, GL_POSITION, lightPos1)

        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        glLoadIdentity()
        gluLookAt(-5,  5, -5, 0, 0,  0, 0.0, 1.0,  0.0)

        self.drawSome()

    def drawSome(self):
        glStencilFunc(GL_ALWAYS, 1, -1)

        glBegin(GL_QUADS)
        glColor3f(0.0, 0.5, 0.0)
        glNormal3f(0,0,-1)
        glVertex3f(-1,-1,0)
        glVertex3f(-1,1,0)
        glVertex3f(1,1,0)
        glVertex3f(1,-1,0)

        glEnd()

        glTranslatef(0,0,0)
        glColor3f(0.5,0,0)
        glStencilFunc(GL_ALWAYS, 2, -1)
        glutSolidCube(1.0)

    def mousePressEvent(self, event):

        id = glReadPixels(event.x(), self.height - event.y() - 1, 1, 1, GL_STENCIL_INDEX, GL_UNSIGNED_INT)
        print(id)

当我将它与 GLUT window 一起使用时,同样可以正常工作,如下所示:

from OpenGL.GL import *
from OpenGL.GLUT import *
from OpenGL.GLU import *
from math import *
import numpy as np


window_width=0
window_height=0

def changeSize(w, h):
    global window_height, window_width
    window_width=w
    window_height=h
    # Prevent a divide by zero, when window is too short
    # (you cant make a window of zero width).
    if h==0:
        h=1
    ratio =  w * 1.0 / h

    # Use the Projection Matrix
    glMatrixMode(GL_PROJECTION)

    # Reset Matrix
    glLoadIdentity()

    # Set the viewport to be the entire window
    glViewport(0, 0, w, h)

    # Set the correct perspective.
    gluPerspective(45.0, ratio, 0.1, 100.0)

    # Get Back to the Modelview
    glMatrixMode(GL_MODELVIEW)


def drawSome():

    glStencilFunc(GL_ALWAYS, 1, -1)

    glBegin(GL_QUADS)
    glColor3f(0.0, 0.5, 0.0)
    glNormal3f(0,0,-1)
    glVertex3f(-1,-1,0)
    glVertex3f(-1,1,0)
    glVertex3f(1,1,0)
    glVertex3f(1,-1,0)

    glEnd()

    glTranslatef(0,0,0)
    glColor3f(0.5,0,0)
    glStencilFunc(GL_ALWAYS, 2, -1)
    glutSolidCube(1.0)

def renderScene():

    glClearStencil(0)
    glClear(GL_STENCIL_BUFFER_BIT)

    glEnable(GL_STENCIL_TEST)
    glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE)

    ambientColor = [0.2,0.2,0.2,1.0]
    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambientColor)

    lightColor0 = [0.5,0.5,0.5,1.0] #Color
    lightPos0 = [0, 0, -10.0, 1.0]
    glLightfv(GL_LIGHT0, GL_DIFFUSE, lightColor0)
    glLightfv(GL_LIGHT0, GL_POSITION, lightPos0)

    lightColor1 = [0.5, 0.2, 0.2, 1.0]
    lightPos1 = [-1.0, 0.5, 0.5, 0.0]
    glLightfv(GL_LIGHT1, GL_DIFFUSE, lightColor1)
    glLightfv(GL_LIGHT1, GL_POSITION, lightPos1)

    # Clear Color and Depth Buffers
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

    # Reset transformations
    glLoadIdentity()
    # Set the camera
    # gluLookAt(x,  1.0, z, x+lx, 1.0,  z+lz,   0.0, 1.0,  0.0)
    gluLookAt(-5,  5, -5, 0, 0,  0, 0.0, 1.0,  0.0)

    drawSome()

    glutSwapBuffers()

def mouse(button, state, x, y):

    global window_height
    id = glReadPixels(x, window_height - y - 1, 1, 1, GL_STENCIL_INDEX, GL_UNSIGNED_INT)
    print(id[0][0])

if __name__ == '__main__':
    glutInit()
    glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA)
    # glutInitWindowPosition(100,100)
    glutInitWindowSize(320,320)
    glutCreateWindow("Lighthouse3D - GLUT Tutorial")
    glClearColor(1.0, 0.5, 0.2, 0.5)
    # register callbacks
    glutDisplayFunc(renderScene)
    glutReshapeFunc(changeSize)
    glutMouseFunc(mouse)
    # glutTimerFunc(0, Timer, 0)

    # here are the new entries
    glutIgnoreKeyRepeat(1)

    # OpenGL init
    glEnable(GL_DEPTH_TEST)
    glEnable(GL_LIGHTING)
    glEnable(GL_COLOR_MATERIAL)
    glEnable(GL_LIGHT0)
    glEnable(GL_LIGHT1)
    glEnable(GL_NORMALIZE)
    glShadeModel(GL_SMOOTH)

    glutMainLoop()

如何解决 QT5 GUI 的问题?

glReadPixels 要求渲染上下文在线程上处于当前状态。 Qt 进行自动上下文管理,并且有据可查的是,在名为 …GL 的方法之外,上下文可能不是当前的。

您必须在这些方法之外显式地使上下文成为当前上下文,为此 Qt OpenGL 类 提供了方法 makeCurrentdoneCurrent。通过调用这些函数来支撑你对 glReadPixels 的调用;确保检查 makeCurrent 是否成功,因为上下文可能已经在不同的线程中。

我怀疑当您调用 glReadPixels 时没有当前的 OpenGL 上下文。你 mousePressEvent 可能应该打电话给 QOpenGLWidget::makeCurrent...

def mousePressEvent(self, event):

    # Make the OpenGL context associated with this `QOpenGLWidget` current. and dont forget self
    self.makeCurrent()

    id = glReadPixels(event.x(), self.height - event.y() - 1, 1, 1, GL_STENCIL_INDEX, GL_UNSIGNED_INT)
    print(id)
    self.doneCurrent()