如何在Python GTK中为GtkGLArea设置OpenGL版本?

How do I set OpenGL version for GtkGLArea in Python GTK?

提问人:coolcatco888 提问时间:5/20/2023 最后编辑:Rabbid76coolcatco888 更新时间:6/2/2023 访问量:104

问:

我在下面有这个类,它可以在使用 OpenGL 3.0 的 Windows 中使用。但是,在 Linux 中,这失败了,因为它似乎在 OpenGL 4.6 上下文中使用了 OpenGL 2.0 方法。我收到此错误。如何告诉 Python GTK 中的 GtkGLArea 使用 OpenGL 4.6 库?

Traceback (most recent call last):
  File "/home/user/git/DisplayIn/src/displayin/engine/openglrenderer.py", line 190, in render
    self.setupGraphics()
  File "/home/user/git/DisplayIn/src/displayin/engine/openglrenderer.py", line 120, in setupGraphics
    self.initBuffers()
  File "/home/user/git/DisplayIn/src/displayin/engine/openglrenderer.py", line 133, in initBuffers
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), ctypes.c_void_p(0))
  File "/usr/local/lib/python3.10/dist-packages/OpenGL/latebind.py", line 63, in __call__
    return self.wrapperFunction( self.baseFunction, *args, **named )
  File "/usr/local/lib/python3.10/dist-packages/OpenGL/GL/VERSION/GL_2_0.py", line 469, in glVertexAttribPointer
    contextdata.setValue( key, array )
  File "/usr/local/lib/python3.10/dist-packages/OpenGL/contextdata.py", line 58, in setValue
    context = getContext( context )
  File "/usr/local/lib/python3.10/dist-packages/OpenGL/contextdata.py", line 40, in getContext
    raise error.Error(
OpenGL.error.Error: Attempt to retrieve context when no valid context

这是类的代码

import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
from OpenGL.GL import *
import numpy as np
import gc

VERTEX_SOURCE = '''
#version 330
layout (location=0) in vec3 position;
layout (location=1) in vec3 color;
layout (location=2) in vec2 texCoord;
out vec3 ourColor;
out vec2 TexCoord;
void main()
{
gl_Position = vec4(position,1.0);
ourColor = color;
TexCoord= vec2(texCoord.x,1.0-texCoord.y);
}'''

FRAGMENT_SOURCE ='''
#version 330
in vec3 ourColor;
in vec2 TexCoord;
out vec4 color;
uniform sampler2D ourTexture;
void main(){
color = texture(ourTexture, TexCoord);
};'''

recVertices = np.array([
    # Positions           Colors           Texture Coords
    1.0,  1.0, 0.0,   1.0, 0.0, 0.0,    1.0, 1.0,   # Top Right    0
    1.0, -1.0, 0.0,   0.0, 1.0, 0.0,    1.0, 0.0,   # Bottom Right 1
    -1.0, -1.0, 0.0,   0.0, 0.0, 1.0,   0.0, 0.0,   # Bottom Left  2
    -1.0,  1.0, 0.0,   1.0, 1.0, 0.0,   0.0, 1.0,   # Top Left     3
    1.0,  1.0, 0.0,   1.0, 0.0, 0.0,    1.0, 1.0,   # Top Right    4
], dtype=np.float32)

def checkGlError(op: str):
    error = glGetError()
    if error is not None and error != 0:
        print("after %s() glError (0x%x)", op, error)

# Based on examples:
# https://stackoverflow.com/questions/42153819/how-to-load-and-display-an-image-in-opengl-es-3-0-using-c
# https://stackoverflow.com/questions/47565884/use-of-the-gtk-glarea-in-pygobject-gtk3
class OpenGLRenderer(Gtk.GLArea):
    def __init__(self):
        Gtk.GLArea.__init__(self)
        self.connect("realize", self.onRealize)
        self.connect("render", self.onRender)
        self.ctx = None
        self.frame = self.createBlankScreenFrame()
        self.area = None
        self.shaderProgram = None
        self.positionHandle = None
        self.textureId = None
        self.vao = None
        self.vbos = None
        self.version = None

    def getVersion(self):
        major = glGetIntegerv(GL_MAJOR_VERSION)
        minor = glGetIntegerv(GL_MINOR_VERSION)
        version = glGetString(GL_VERSION)

        return major, minor, version
    
    def createBlankScreenFrame(self):
        frame = np.array([[[0, 0, 0]]], dtype=np.uint8)
        return frame

    def onRealize(self, area):

        error = area.get_error()
        if error != None:
            print("your graphics card is probably too old : ", error)
        else:
            print(area, "realize... fine so far")

        self.ctx = self.get_context()
        self.ctx.make_current()

        major, minor, self.version = self.getVersion()
        print("OpenGL realized", self.ctx)
        print("%s\n", self.version)

    def onRender(self, area, ctx):
        
        # Render will be invoked separately
        # self.render(self.frame)
        return True

    def setupGraphics(self):

        if self.shaderProgram is None:
            # Load Shaders, Create program, Setup Graphics
            vertexShader = glCreateShader(GL_VERTEX_SHADER)
            glShaderSource(vertexShader, VERTEX_SOURCE)
            glCompileShader(vertexShader)
            status = glGetShaderiv(vertexShader, GL_COMPILE_STATUS)
            print("Compile vertexShader status: " + str(status == GL_TRUE))

            pixelShader = glCreateShader(GL_FRAGMENT_SHADER)
            glShaderSource(pixelShader, FRAGMENT_SOURCE)
            glCompileShader(pixelShader)
            status = glGetShaderiv(pixelShader, GL_COMPILE_STATUS)
            print("Compile vertexShader status: " + str(status == GL_TRUE))

            self.shaderProgram = glCreateProgram()
            glAttachShader(self.shaderProgram, vertexShader)
            glAttachShader(self.shaderProgram, pixelShader)
            glLinkProgram(self.shaderProgram)
            glBindFragDataLocation(self.shaderProgram, 0, "color")
            self.positionHandle = glGetAttribLocation(self.shaderProgram, "position")

            # Initalize Vertex Buffers
            self.initBuffers()
    
    def initBuffers(self):
        # Initialize an buffer to store all the verticles and transfer them to the GPU
        self.vao = glGenVertexArrays(1) # Generate VAO
        self.vbos = glGenBuffers(1) # Generate VBO
        glBindVertexArray(self.vao) # Bind the Vertex Array

        glBindBuffer(GL_ARRAY_BUFFER, self.vbos) # Bind verticles array for OpenGL to use
        glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * len(recVertices), recVertices, GL_STATIC_DRAW)
        
        # 1. set the vertex attributes pointers
        # Position Attribute
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), ctypes.c_void_p(0))
        glEnableVertexAttribArray(0)
        # Color Attribute
        glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), ctypes.c_void_p(3 * sizeof(GLfloat)))
        glEnableVertexAttribArray(1)
        # Texture Coordinate Attribute
        glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), ctypes.c_void_p(6 * sizeof(GLfloat)))
        glEnableVertexAttribArray(2)

        glBindVertexArray(0) # 3. Unbind VAO
    
    def generateTexture(self, frame):

        # Delete previous frame
        # del self.frame
        # gc.collect()

        # Update Frame
        self.frame = frame

        # Delete previous textures to avoid memory leak
        if self.textureId is not None:
            glDeleteTextures(1, [self.textureId])

        # If we have a frame to display
        if frame is not None:
            # extract array from Image
            h, w, d = frame.shape

            # Frame is a 3 dimentional array where shape eg. (1920, 1080, 3)
            # Where it is w, h, and 3 values for color
            # https://www.educba.com/numpy-flatten/
            pixels = frame.ravel(order = 'C')

            # Generate Texture
            self.textureId = glGenTextures(1)
            glBindTexture(GL_TEXTURE_2D, self.textureId) # Bind our 2D texture so that following set up will be applied

            # Set texture wrapping parameter
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)

            # Set texture Filtering parameter
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)

            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, pixels)
            glGenerateMipmap(GL_TEXTURE_2D)
            glBindTexture(GL_TEXTURE_2D, 0) # Unbind 2D textures

    def render(self, frame):

        # Set OpenGL Render Context
        if self.ctx is not None and frame is not None:
            self.ctx.make_current()

            # Initialize Graphics
            self.setupGraphics()

            # Generate Texture
            self.generateTexture(frame)

            # Clear Screen
            glClearColor(0, 0, 1, 1)
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

            # Use Shader Program, Bind Vertex Array and Texture
            glUseProgram(self.shaderProgram)
            checkGlError("glUseProgram")
            glActiveTexture(GL_TEXTURE0)
            checkGlError("glActiveTexture")
            glBindTexture(GL_TEXTURE_2D, self.textureId)
            checkGlError("glBindTexture")
            mlocation = glGetUniformLocation(self.shaderProgram, "ourTexture")
            checkGlError("glGetUniformLocation")
            glUniform1i(mlocation, 0)
            checkGlError("glUniform1i")
            glBindVertexArray(self.vao)
            checkGlError("glBindVertexArray")

            # Render Frame
            glDrawArrays(GL_TRIANGLES, 0, 3)
            glDrawArrays(GL_TRIANGLES, 2, 3)
            
            # Queue Draw
            glFlush()
            self.queue_draw()

关于如何为GtkGLArea设置OpenGL版本的任何想法?

蟒蛇 opengl pygtk pygobject

评论


答:

0赞 coolcatco888 6/2/2023 #1

事实证明,我收到此错误是因为我在 Wayland 中使用该应用程序。这段代码在 X11 中运行良好,所以我需要在上面的代码之前添加这段代码,以使这个应用程序也能在 Wayland 中运行。

# Needed for Wayland Support for OpenGL in Linux
if 'WAYLAND_DISPLAY' in os.environ and 'PYOPENGL_PLATFORM' not in os.environ:
    os.environ['PYOPENGL_PLATFORM'] = 'x11'