Reputation: 379
I have the below test code below where I am trying to get a basic opengl Gtk3 GLArea example working.
The error below is currently my sticking point, from what i have read this potentially means the features are not availabale to the context but reading about GLArea it does not seem to let you pick the context and sounds like it should default to the correct one.
The issue could be with GLArea or PyOpenGL unfortunately all examples i can fine are currently in C be great to get a basic example working in python.
Anyway spent a lot of time trying to figure this issue out so would be great-full if anyone can help get past at least this error.
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
Example, its also on a gist 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()
Upvotes: 3
Views: 3341
Reputation: 1
I had recently been developing a software using pyopengl. It had been working good until I decided to upgrade my PC to SSD.
I had to install windows again. But after installing the python again on my SSD, my same code that was running perfectly fine till now, threw the same error (OpenGL.error.NullFunctionError: Attempt to call an undefined function glGenVertexArrays, check for bool(glGenVertexArrays) before calling).
God, I wasted my entire day figuring out what is wrong. As suggested by some community members, even installed different version of PyOpenGL and PyOpenGL accelerate but all in vain. I even updated Graphic drivers (NVIDIA in may case).
Finally, what solved the problem was Windows 10 to Windows 11 upgradation. All other troubleshooting was of no use to me. So I would suggest to upgrade your Windows operating system to latest.
Upvotes: 0
Reputation: 1547
Ran into this error when using PyOpenGL and glfw and the fix was glfw.make_context_current(window)
.
Upvotes: 1
Reputation: 5240
For me, the problem seemed to be related to running under Wayland. It seems like if PyOpenGL is running under Wayland and there's an X11 server running, it will use GLX rather than Wayland's EGL support. Something about how GtkGLArea sets up the GL context means that GLX works but doesn't have any GL extensions, which includes VAOs. This seems like a bug in PyOpenGL.
There's two ways to work around this:
Set PYOPENGL_PLATFORM
to force PyOpenGL to use EGL instead of GLX.
E.g. before importing OpenGL:
if 'WAYLAND_DISPLAY' in os.environ and 'PYOPENGL_PLATFORM' not in os.environ:
os.environ['PYOPENGL_PLATFORM'] = 'egl'
Unset WAYLAND_DISPLAY
to force Gtk to use GLX instead of EGL.
E.g. before importing Gtk:
if 'WAYLAND_DISPLAY' in os.environ:
del os.environ['WAYLAND_DISPLAY']
Upvotes: 3
Reputation: 379
Worked it out below is the complete working example, after the comment from @derhass I did some searching and found Gdk.Screen why this was not used in the example I found previously I do not know.
The missing piece to the puzzle are these 3 lines
screen = Gdk.Screen.get_default()
visual = Gdk.Screen.get_rgba_visual(screen)
self.window = Gtk.Window()
Gtk.Widget.set_visual(self.window, visual)
Complete working sample, should display your basic triangle in a window which looks like this.
#!/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()
Upvotes: 3