2017-02-12 31 views
1

У меня ниже приведен тестовый код, где я пытаюсь получить базовый пример работы Gtk3 GLArea.Почему glGenVertexArrays не определен в PyOpenGL при использовании GTKGLArea

Ошибка, приведенная ниже в настоящее время является моей точкой привязки, из того, что я прочитал, это потенциально означает, что функции не доступны для контекста, но чтение о GLArea, похоже, не позволяет вам выбрать контекст и звучит так, правильный.

Проблема может быть в GLArea или PyOpenGL, к сожалению, все примеры, которые я могу оценить, в настоящее время находятся на C, чтобы получить базовый пример, работающий в python.

В любом случае потратил много времени, пытаясь понять эту проблему, так что было бы отлично, если кто-то может помочь пропустить хотя бы эту ошибку.

Traceback (most recent call last): 
File "gtkglarea.py", line 91, in on_configure_event 
self.vertex_array_object = glGenVertexArrays(1) 
File "/usr/lib/python3/dist-packages/OpenGL/platform/baseplatform.py", line 407, in call 
self.name, self.name, 
OpenGL.error.NullFunctionError: Attempt to call an undefined function glGenVertexArrays, check for bool(glGenVertexArrays) before calling 

Пример, его также на сути https://gist.github.com/olymk2/5b3e49ac83130e580bd9983f2e5d49c3

#!/usr/bin/python 
import os 
import sys 

from OpenGL.GLU import * 
from OpenGL import GLX 
from OpenGL import GL as GL 
from ctypes import * 
import gi 
gi.require_version('Gtk', '3.0') 
from gi.repository import Gtk, Gdk 

from OpenGL.arrays import vbo 
from OpenGL.GL import shaders 
from OpenGL.raw.GL.ARB.vertex_array_object import glGenVertexArrays, \ 
                glBindVertexArray 

from numpy import array 
import numpy as np 

VERTEX_SHADER = """ 
    #version 330 
    in vec4 position; 
    void main() 
    { 
     gl_Position = position; 
    }""" 

FRAGMENT_SHADER = """ 
    #version 330 
    out vec4 fragColor; 
    void main() 
    { 
     fragColor = vec4(1.0, 0.0, 0.0, 1.0); 
    } 
    """ 


class application_gui: 
    """Tutorial 01 Create and destroy a window""" 
    # glwrap = gtkglarea() 
    def __init__(self): 
     self.window = Gtk.Window() 
     self.canvas = Gtk.GLArea() 
     self.canvas.set_required_version(3, 3) 
     self.test_features() 

     self.vertices = [ 
      0.6, 0.6, 0.0, 1.0, 
      -0.6, 0.6, 0.0, 1.0, 
      0.0, -0.6, 0.0, 1.0] 

     self.vertices = np.array(self.vertices, dtype=np.float32) 

     self.canvas.connect('realize', self.on_configure_event) 
     self.canvas.connect('render', self.on_draw) 
     self.canvas.set_double_buffered(False) 

     self.window.connect('delete_event', Gtk.main_quit) 
     self.window.connect('destroy', lambda quit: Gtk.main_quit()) 

     self.window.add(self.canvas) 
     self.window.show_all() 

     self.on_configure_event(self.canvas) 

    def test_features(self): 
     print('Testing features') 
     print('glGenVertexArrays Available %s' % bool(glGenVertexArrays)) 
     print('Alpha Available %s' % bool(self.canvas.get_has_alpha())) 
     print('Depth buffer Available %s' % bool(self.canvas.get_has_depth_buffer())) 



    def on_configure_event(self, widget): 
     print('realize event') 

     widget.make_current() 
     # widget.attach_buffers() 
     context = widget.get_context() 

     print('is legacy context %s' % Gdk.GLContext.is_legacy(context)) 
     print('configure errors') 
     print(widget.get_error()) 


     vs = shaders.compileShader(VERTEX_SHADER, GL.GL_VERTEX_SHADER) 
     fs = shaders.compileShader(FRAGMENT_SHADER, GL.GL_FRAGMENT_SHADER) 
     self.shader = shaders.compileProgram(vs, fs) 

     self.vertex_array_object = glGenVertexArrays(1) 
     GL.glBindVertexArray(self.vertex_array_object) 

     # Generate buffers to hold our vertices 
     self.vertex_buffer = GL.glGenBuffers(1) 
     GL.glBindBuffer(GL.GL_ARRAY_BUFFER, self.vertex_buffer) 

     # Get the position of the 'position' in parameter of our shader and bind it. 
     self.position = GL.glGetAttribLocation(self.shader, 'position') 
     GL.glEnableVertexAttribArray(self.position) 

     # Describe the position data layout in the buffer 
     GL.glVertexAttribPointer(self.position, 4, GL.GL_FLOAT, False, 0, ctypes.c_void_p(0)) 

     # Send the data over to the buffer 
     GL.glBufferData(GL.GL_ARRAY_BUFFER, 48, self.vertices, GL.GL_STATIC_DRAW) 

     # Unbind the VAO first (Important) 
     GL.glBindVertexArray(0) 

     # Unbind other stuff 
     GL.glDisableVertexAttribArray(self.position) 
     GL.glBindBuffer(GL.GL_ARRAY_BUFFER, 0) 

     print('errors') 
     print(widget.get_error()) 

     return True 

    def on_draw(self, widget, *args): 
     print('render event') 
     print(widget.get_error()) 
     #Create the VBO 

     widget.attach_buffers() 

     GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT) 
     GL.glUseProgram(self.shader) 

     GL.glBindVertexArray(self.vertex_array_object) 
     GL.glDrawArrays(GL.GL_TRIANGLES, 0, 3) 
     GL.glBindVertexArray(0) 

     GL.glUseProgram(0) 
     glFlush() 
     return True 

application = application_gui() 
Gtk.main() 
+0

Вы уверены, что ваша реализация GL _и_ запрашиваемый контекст на самом деле поддерживают эти функции? – derhass

+0

вполне возможно, и это часть недоразумения ранее, я бы выбрал контекст, но теперь происходит волшебство с GLArea, и я не вижу способа выбрать контекст для виджета, ранее я использовал xsome xlib-код, чтобы помочь выбрать контекст. https://developer.gnome.org/gtk3/unstable/GtkGLArea.html – Oly

+0

Так же, как примечание, я нажимаю это на wayland, используя этот префикс PYOPENGL_PLATFORM = "EGL", чтобы программа python разрешила проблему, я ударил ошибку выше, если она использует osmesa в качестве платформы. – Oly

ответ

1

Для меня проблема, казалось, связанных с запуском под Wayland. Похоже, что PyOpenGL работает под Wayland и работает сервер X11, он будет использовать GLX, а не поддержку EGL Wayland. Что-то о том, как GtkGLArea устанавливает контекст GL, означает, что GLX работает, но не имеет каких-либо расширений GL, включая VAO. Это похоже на ошибку в PyOpenGL.

Там два пути, чтобы обойти это:

  1. Установить PYOPENGL_PLATFORM, чтобы заставить PyOpenGL использовать EGL вместо GLX.

    E.g. перед импортом OpenGL:

    if 'WAYLAND_DISPLAY' in os.environ and 'PYOPENGL_PLATFORM' not in os.environ: 
        os.environ['PYOPENGL_PLATFORM'] = 'egl' 
    
  2. UNSET WAYLAND_DISPLAY заставить Gtk использовать GLX вместо EGL.

    E.g. перед импортом Gtk:

    if 'WAYLAND_DISPLAY' in os.environ: 
        del os.environ['WAYLAND_DISPLAY'] 
    
2

Работал это ниже полный рабочий пример, после того, как комментарий от @derhass я сделал некоторый поиск и нашел Gdk.Screen, почему это было не используется в примере, который я нашел ранее, я не знаю.

Недостающий кусок головоломки эти 3 линии

screen = Gdk.Screen.get_default() 
visual = Gdk.Screen.get_rgba_visual(screen) 
self.window = Gtk.Window() 
Gtk.Widget.set_visual(self.window, visual) 

Полный рабочий образец, должен отображать основной треугольник в окне, которое выглядит следующим образом.

enter image description here

#!/usr/bin/python 
# noqa: E402 
import gi 
gi.require_version('Gtk', '3.0') 
import numpy as np 
from gi.repository import Gtk, Gdk 
from OpenGL.GLU import * 
from OpenGL import GL as GL 

from OpenGL.GL import shaders 
from OpenGL.raw.GL.ARB.vertex_array_object import glGenVertexArrays, \ 
                glBindVertexArray 

# from numpy import array 

VERTEX_SHADER = """ 
    #version 330 
    in vec4 position; 
    void main() 
    { 
     gl_Position = position; 
    }""" 

FRAGMENT_SHADER = """ 
    #version 330 
    out vec4 fragColor; 
    void main() 
    { 
     fragColor = vec4(1.0, 0.0, 0.0, 1.0); 
    } 
    """ 


class application_gui: 
    """Tutorial 01 Create and destroy a window""" 
    # glwrap = gtkglarea() 
    def __init__(self): 
     screen = Gdk.Screen.get_default() 
     visual = Gdk.Screen.get_rgba_visual(screen) 

     print('is composite %s' % Gdk.Screen.is_composited(screen)) 

     self.window = Gtk.Window() 
     Gtk.Widget.set_visual(self.window, visual) 
     self.canvas = Gtk.GLArea() 
     self.canvas.set_required_version(3, 3) 
     self.test_features() 

     self.vertices = [ 
      0.6, 0.6, 0.0, 1.0, 
      -0.6, 0.6, 0.0, 1.0, 
      0.0, -0.6, 0.0, 1.0] 

     self.vertices = np.array(self.vertices, dtype=np.float32) 

     self.canvas.connect('realize', self.on_configure_event) 
     self.canvas.connect('render', self.on_draw) 
     self.canvas.set_double_buffered(False) 

     self.window.connect('delete_event', Gtk.main_quit) 
     self.window.connect('destroy', lambda quit: Gtk.main_quit()) 

     self.window.add(self.canvas) 
     self.window.show_all() 

    def test_features(self): 
     print('Testing features') 
     print('glGenVertexArrays Available %s' % bool(glGenVertexArrays)) 
     print('Alpha Available %s' % bool(self.canvas.get_has_alpha())) 
     print('Depth buffer Available %s' % bool(self.canvas.get_has_depth_buffer())) 

    def on_configure_event(self, widget): 
     print('realize event') 
     widget.make_current() 
     print(widget.get_error()) 

     vs = shaders.compileShader(VERTEX_SHADER, GL.GL_VERTEX_SHADER) 
     fs = shaders.compileShader(FRAGMENT_SHADER, GL.GL_FRAGMENT_SHADER) 
     self.shader = shaders.compileProgram(vs, fs) 

     # Create a new Vertex Array Object 
     self.vertex_array_object = GL.glGenVertexArrays(1) 
     GL.glBindVertexArray(self.vertex_array_object) 

     # Generate a new array buffers for our vertices 
     self.vertex_buffer = GL.glGenBuffers(1) 
     GL.glBindBuffer(GL.GL_ARRAY_BUFFER, self.vertex_buffer) 

     # Get position variable form the shader and store 
     self.position = GL.glGetAttribLocation(self.shader, 'position') 
     GL.glEnableVertexAttribArray(self.position) 

     # describe the data layout 
     GL.glVertexAttribPointer(self.position, 4, GL.GL_FLOAT, False, 0, ctypes.c_void_p(0)) 

     # Copy data to the buffer 
     GL.glBufferData(GL.GL_ARRAY_BUFFER, 48, self.vertices, GL.GL_STATIC_DRAW) 

     # Unbind buffers once done 
     GL.glBindVertexArray(0) 
     GL.glDisableVertexAttribArray(self.position) 
     GL.glBindBuffer(GL.GL_ARRAY_BUFFER, 0) 

     return True 

    def on_draw(self, widget, *args): 
     print('render event') 
     print(widget.get_error()) 

     # clear screen and select shader for drawing 
     GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT) 
     GL.glUseProgram(self.shader) 

     # bind and draw vertices 
     GL.glBindVertexArray(self.vertex_array_object) 
     GL.glDrawArrays(GL.GL_TRIANGLES, 0, 3) 
     GL.glBindVertexArray(0) 

     GL.glUseProgram(0) 
     GL.glFlush() 
     return True 

application = application_gui() 
Gtk.main()