PySide2 OpenGL error when using custom QSurfaceFormat - python

I'm using QSurfaceFormat to explicitly define OpenGL Core profile and set minor/major versions. The thing is, assignment of a custom surface format gives me the following error:
Traceback (most recent call last):
File "/home/artem/PycharmProjects/PolyEdit3D/PolyEdit3D/Widgets/PlyViewport.py", line 22, in initializeGL
gl.glEnable(gl.GL_COLOR_BUFFER_BIT)
File "/home/artem/.local/lib/python3.7/site-packages/OpenGL/platform/baseplatform.py", line 415, in __call__
return self( *args, **named )
File "src/errorchecker.pyx", line 58, in OpenGL_accelerate.errorchecker._ErrorChecker.glCheckError
OpenGL.error.GLError: GLError(
err = 1280,
description = b'invalid enumerant',
baseOperation = glEnable,
cArguments = (GL_COLOR_BUFFER_BIT,)
)
Traceback (most recent call last):
File "/home/artem/PycharmProjects/PolyEdit3D/PolyEdit3D/Widgets/PlyViewport.py", line 69, in paintGL
gl.glDrawElements(gl.GL_TRIANGLES, 6, gl.GL_UNSIGNED_INT, ctypes.c_void_p(0))
File "src/latebind.pyx", line 39, in OpenGL_accelerate.latebind.LateBind.__call__
File "src/wrapper.pyx", line 318, in OpenGL_accelerate.wrapper.Wrapper.__call__
File "src/wrapper.pyx", line 311, in OpenGL_accelerate.wrapper.Wrapper.__call__
File "/home/artem/.local/lib/python3.7/site-packages/OpenGL/platform/baseplatform.py", line 415, in __call__
return self( *args, **named )
File "src/errorchecker.pyx", line 58, in OpenGL_accelerate.errorchecker._ErrorChecker.glCheckError
OpenGL.error.GLError: GLError(
err = 1282,
description = b'invalid operation',
baseOperation = glDrawElements,
pyArgs = (
GL_TRIANGLES,
6,
GL_UNSIGNED_INT,
c_void_p(None),
),
cArgs = (
GL_TRIANGLES,
6,
GL_UNSIGNED_INT,
c_void_p(None),
),
cArguments = (
GL_TRIANGLES,
6,
GL_UNSIGNED_INT,
c_void_p(None),
)
)
As you can see there's an error in gl.glEnable(gl.GL_COLOR_BUFFER_BIT) and other simple things. Without specifying QSurfaceFormat everything works like a charm.
Here's my surface format class:
class GLSurfaceFormat(QtGui.QSurfaceFormat):
def __init__(self):
super(GLSurfaceFormat, self).__init__()
self.__initSurface()
def __initSurface(self):
self.setRenderableType(QtGui.QSurfaceFormat.OpenGL)
self.setMinorVersion(3)
self.setMajorVersion(4)
self.setProfile(QtGui.QSurfaceFormat.CoreProfile)
self.setColorSpace(QtGui.QSurfaceFormat.sRGBColorSpace)
self.setSwapBehavior(QtGui.QSurfaceFormat.DoubleBuffer)
Here's how I assign it:
QApplication.setAttribute(Qt.AA_UseDesktopOpenGL)
QSurfaceFormat.setDefaultFormat(GLSurfaceFormat())
app = QApplication(sys.argv)
...
Could you tell me, is there any mistake in QSurfaceFormat usage or maybe there's something else?
!UPDATE!
Here's a quick example where you can reproduce the same behavior:
from PySide2 import QtCore, QtGui, QtWidgets
from OpenGL import GL as gl
from OpenGL.GL.shaders import compileProgram, compileShader
import numpy as np
import ctypes
import sys
import glm
class PlyViewportWidget(QtWidgets.QOpenGLWidget):
def __init__(self):
super(PlyViewportWidget, self).__init__(parent=None)
self.vao = None
self.vbo = None
self.ebo = None
self.shaderProg = None
self.isWireframe = False
def initializeGL(self):
gl.glEnable(gl.GL_COLOR_BUFFER_BIT)
gl.glClearColor(0.4, 0.4, 0.4, 1)
with open("fragment.glsl", 'r') as f:
fragment = compileShader(f.read(), gl.GL_FRAGMENT_SHADER)
with open("vertex.glsl", "r") as f:
vertex = compileShader(f.read(), gl.GL_VERTEX_SHADER)
self.shaderProg = compileProgram(vertex, fragment)
vertices = np.array(
[
0.5, 0.5, 0.0,
0.5, -0.5, 0.0,
-0.5, -0.5, 0.0,
-0.5, 0.5, 0.0
], dtype=ctypes.c_float
)
indices = np.array(
[
0, 1, 3,
1, 2, 3
], dtype=ctypes.c_uint
)
self.vbo = gl.glGenBuffers(1)
self.ebo = gl.glGenBuffers(1)
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, self.vbo)
gl.glBufferData(gl.GL_ARRAY_BUFFER, vertices.nbytes, vertices, gl.GL_STATIC_DRAW)
gl.glBindBuffer(gl.GL_ELEMENT_ARRAY_BUFFER, self.ebo)
gl.glBufferData(gl.GL_ELEMENT_ARRAY_BUFFER, indices.nbytes, indices, gl.GL_STATIC_DRAW)
gl.glEnableVertexAttribArray(0)
gl.glVertexAttribPointer(0, 3, gl.GL_FLOAT, gl.GL_FALSE, 3 * ctypes.sizeof(ctypes.c_float), ctypes.c_void_p(0))
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, 0)
gl.glBindVertexArray(0)
gl.glUseProgram(self.shaderProg)
def paintGL(self):
gl.glClearColor(0.4, 0.4, 0.4, 1.0)
gl.glClear(gl.GL_COLOR_BUFFER_BIT)
trans = self.getTransformMatrix()
#u_transform = gl.glGetUniformLocation(self.shaderProg, "u_transform")
#gl.glUniformMatrix4fv(u_transform, 1, gl.GL_FALSE, u_transform)
gl.glBindVertexArray(self.vao)
gl.glDrawElements(gl.GL_TRIANGLES, 6, gl.GL_UNSIGNED_INT, ctypes.c_void_p(0))
def resizeGL(self, w:int, h:int):
gl.glViewport(0, 0, w, h)
def keyPressEvent(self, event:QtGui.QKeyEvent):
self.makeCurrent()
if event.key() == QtCore.Qt.Key_Z and not self.isWireframe:
gl.glPolygonMode(gl.GL_FRONT_AND_BACK, gl.GL_LINE)
self.isWireframe = True
self.update()
elif event.key() == QtCore.Qt.Key_Z and self.isWireframe:
gl.glPolygonMode(gl.GL_FRONT_AND_BACK, gl.GL_FILL)
self.isWireframe = False
self.update()
event.accept()
def getTransformMatrix(self):
return glm.rotate(glm.mat4(1.0), glm.radians(90.0), glm.vec3(1.0, 0.0, 0.0))
class GLSurfaceFormat(QtGui.QSurfaceFormat):
def __init__(self):
super(GLSurfaceFormat, self).__init__()
self.__initSurface()
def __initSurface(self):
self.setRenderableType(QtGui.QSurfaceFormat.OpenGL)
self.setMinorVersion(3)
self.setMajorVersion(4)
self.setProfile(QtGui.QSurfaceFormat.CoreProfile)
self.setColorSpace(QtGui.QSurfaceFormat.sRGBColorSpace)
self.setSwapBehavior(QtGui.QSurfaceFormat.DoubleBuffer)
if __name__ == '__main__':
QtWidgets.QApplication.setAttribute(QtCore.Qt.AA_UseDesktopOpenGL)
# Uncomment to get this damn error!!!
#QtGui.QSurfaceFormat.setDefaultFormat(GLSurfaceFormat())
app = QtWidgets.QApplication(sys.argv)
window = PlyViewportWidget()
window.show()
sys.exit(app.exec_())
vertex.glsl
#version 330 core
layout (location = 0) in vec3 aPos;
void main()
{
gl_Position = vec4(aPos, 1.0);
}
fragment.glsl
#version 330 core
out vec4 FragColor;
void main()
{
FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);
}

GL_COLOR_BUFFER_BIT is not a valid enumeration constant for glEnable, but it is a proper argument for glClear.
Invoke glClear, after the clear color is set by glClearColor:
class PlyViewportWidget(QtWidgets.QOpenGLWidget):
# [...]
def initializeGL(self):
#gl.glEnable(gl.GL_COLOR_BUFFER_BIT) <---- DELETE
gl.glClearColor(0.4, 0.4, 0.4, 1)
gl.glClear(gl.GL_COLOR_BUFFER_BIT) # <---- ADD
You missed to create the named Vertex Array Object:
class PlyViewportWidget(QtWidgets.QOpenGLWidget):
# [...]
def initializeGL(self):
# [...]
self.vao = gl.glGenVertexArrays(1) # <---
gl.glBindVertexArray(self.vao) # <---
self.vbo = gl.glGenBuffers(1)
self.ebo = gl.glGenBuffers(1)
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, self.vbo)
gl.glBufferData(gl.GL_ARRAY_BUFFER, vertices.nbytes, vertices, gl.GL_STATIC_DRAW)
gl.glBindBuffer(gl.GL_ELEMENT_ARRAY_BUFFER, self.ebo)
gl.glBufferData(gl.GL_ELEMENT_ARRAY_BUFFER, indices.nbytes, indices, gl.GL_STATIC_DRAW)
gl.glEnableVertexAttribArray(0)
gl.glVertexAttribPointer(0, 3, gl.GL_FLOAT, gl.GL_FALSE, 3 * ctypes.sizeof(ctypes.c_float), ctypes.c_void_p(0))
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, 0)
gl.glBindVertexArray(0)

Related

Drawing an OpenGL triangle in Python

Here is my code so far:
main.py_
from Application import Application
if __name__ == '__main__':
app = Application()
app.run()
Triangle.py_
import contextlib
import logging as log
from OpenGL import GL as gl
import ctypes
import sys
class Triangle:
vertex_array_id = 0
vertex_data = []
program_id = 0
shader_id = 0
def __init__(self):
print('Triangle.__init__(self)')
self.create_vertex_buffer()
self.load_shaders()
def create_vertex_array_object(self):
log.debug('create_vertex_array_object(self):')
self.vertex_array_id = gl.glGenVertexArrays(1)
try:
gl.glBindVertexArray(self.vertex_array_id)
yield
finally:
log.debug('~create_vertex_array_object(self):')
gl.glDeleteVertexArrays(1, [self.vertex_array_id])
def create_vertex_buffer(self):
with self.create_vertex_array_object():
# A triangle
self.vertex_data = [-1, -1, 0,
1, -1, 0,
0, 1, 0]
attr_id = 0 # No particular reason for 0,
# but must match the layout location in the shader.
log.debug('creating and binding the vertex buffer (VBO)')
vertex_buffer = gl.glGenBuffers(1)
try:
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, vertex_buffer)
array_type = (gl.GLfloat * len(self.vertex_data))
gl.glBufferData(gl.GL_ARRAY_BUFFER,
len(self.vertex_data) * ctypes.sizeof(ctypes.c_float),
array_type(*self.vertex_data),
gl.GL_STATIC_DRAW)
log.debug('setting the vertex attributes')
gl.glVertexAttribPointer(
attr_id, # attribute 0.
3, # components per vertex attribute
gl.GL_FLOAT, # type
False, # to be normalized?
0, # stride
None # array buffer offset
)
gl.glEnableVertexAttribArray(attr_id) # use currently bound VAO
yield
finally:
log.debug('cleaning up buffer')
# gl.glDisableVertexAttribArray(attr_id)
# gl.glDeleteBuffers(1, [vertex_buffer])
def load_shaders(self):
shaders = {
gl.GL_VERTEX_SHADER: '''\
#version 330 core
layout(location = 0) in vec3 vertexPosition_modelspace;
void main(){
gl_Position.xyz = vertexPosition_modelspace;
gl_Position.w = 1.0;
}
''',
gl.GL_FRAGMENT_SHADER: '''\
#version 330 core
out vec3 color;
void main(){
color = vec3(1,0,0);
}
'''
}
log.debug('creating the shader program')
self.program_id = gl.glCreateProgram()
try:
shader_ids = []
for shader_type, shader_src in shaders.items():
self.shader_id = gl.glCreateShader(shader_type)
gl.glShaderSource(self.shader_id, shader_src)
log.debug(f'compiling the {shader_type} shader')
gl.glCompileShader(self.shader_id)
# check if compilation was successful
result = gl.glGetShaderiv(self.shader_id, gl.GL_COMPILE_STATUS)
info_log_len = gl.glGetShaderiv(self.shader_id, gl.GL_INFO_LOG_LENGTH)
if info_log_len:
logmsg = gl.glGetShaderInfoLog(self.shader_id)
log.error(logmsg)
sys.exit(10)
gl.glAttachShader(self.program_id, self.shader_id)
shader_ids.append(self.shader_id)
log.debug('linking shader program')
gl.glLinkProgram(self.program_id)
# check if linking was successful
result = gl.glGetProgramiv(self.program_id, gl.GL_LINK_STATUS)
info_log_len = gl.glGetProgramiv(self.program_id, gl.GL_INFO_LOG_LENGTH)
if info_log_len:
logmsg = gl.glGetProgramInfoLog(self.program_id)
log.error(logmsg)
sys.exit(11)
log.debug('installing shader program into rendering state')
gl.glUseProgram(self.program_id)
yield
finally:
log.debug('cleaning up shader program')
for self.shader_id in self.shader_ids:
gl.glDetachShader(self.program_id, self.shader_id)
gl.glDeleteShader(self.shader_id)
gl.glUseProgram(0)
gl.glDeleteProgram(self.program_id)
def render(self):
gl.glDrawArrays(gl.GL_TRIANGLES, 0, 3) # Starting from vertex 0
Application.py_
import contextlib
import glfw
import sys
from OpenGL import GL as gl
import logging as log
from Triangle import Triangle
class Application:
tri = Triangle()
def __init__(self):
print("Application.__init__(self)")
#contextlib.contextmanager
def create_main_window(self):
log.debug('create_main_window(self):')
if not glfw.init():
sys.exit(1)
try:
glfw.window_hint(glfw.CONTEXT_VERSION_MAJOR, 3)
glfw.window_hint(glfw.CONTEXT_VERSION_MINOR, 3)
glfw.window_hint(glfw.OPENGL_FORWARD_COMPAT, True)
glfw.window_hint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)
title = 'Tutorial 2: First Triangle'
window = glfw.create_window(500, 400, title, None, None)
if not window:
sys.exit(2)
glfw.make_context_current(window)
gl.glClearColor(0, 0, 0.4, 0)
yield window
finally:
log.debug('~create_main_window(self):')
glfw.terminate()
def main_loop(self, window):
while (
glfw.get_key(window, glfw.KEY_ESCAPE) != glfw.PRESS and
not glfw.window_should_close(window)
):
gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)
self.tri.render()
glfw.swap_buffers(window)
glfw.poll_events()
def run(self):
log.basicConfig(level=log.DEBUG)
with self.create_main_window() as window:
self.main_loop(window)
From the console I have this coming out:
DEBUG:root:create_main_window(self):
Triangle.__init__(self)
Application.__init__(self)
DEBUG:root:~create_main_window(self):
Traceback (most recent call last):
File "C:\Users\prussos\PycharmProjects\pythonProject3\main.py", line 5, in <module>
app.run()
File "C:\Users\prussos\PycharmProjects\pythonProject3\Application.py", line 51, in run
self.main_loop(window)
File "C:\Users\prussos\PycharmProjects\pythonProject3\Application.py", line 44, in main_loop
self.tri.render()
File "C:\Users\prussos\PycharmProjects\pythonProject3\Triangle.py", line 131, in render
gl.glDrawArrays(gl.GL_TRIANGLES, 0, 3) # Starting from vertex 0
File "C:\Users\prussos\PycharmProjects\pythonProject3\venv\lib\site-packages\OpenGL\platform\baseplatform.py", line 415, in __call__
return self( *args, **named )
File "C:\Users\prussos\PycharmProjects\pythonProject3\venv\lib\site-packages\OpenGL\error.py", line 230, in glCheckError
raise self._errorClass(
OpenGL.error.GLError: GLError(
err = 1282,
description = b'invalid operation',
baseOperation = glDrawArrays,
cArguments = (GL_TRIANGLES, 0, 3)
)
Process finished with exit code 1
There seems to be a drawing error that is happening but I really I not quite exactly sure how to start to troubleshoot this thing yet. The code was moved over from gitlabs into a class based system for python. So a draw arrays that is an invalid operation what could that be coming from in OpenGL library is anyone familiar with it enough to know what place to edit here?
When you call an OpenGL instruction, you need a valid and up-to-date OpenGL context. The OpenGL Context is created with the OpenGl window. The OpenGL objects (VAO, VBO and shaders) are create in the constructor of the Triangle class. Therefore you can construct an object of this class only after creating the OpenGL window:
class Application:
# tri = Triangle() # <-- DELETE
def __init__(self):
print("Application.__init__(self)")
#contextlib.contextmanager
def create_main_window(self):
log.debug('create_main_window(self):')
if not glfw.init():
sys.exit(1)
try:
glfw.window_hint(glfw.CONTEXT_VERSION_MAJOR, 3)
glfw.window_hint(glfw.CONTEXT_VERSION_MINOR, 3)
glfw.window_hint(glfw.OPENGL_FORWARD_COMPAT, True)
glfw.window_hint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)
title = 'Tutorial 2: First Triangle'
window = glfw.create_window(500, 400, title, None, None)
if not window:
sys.exit(2)
glfw.make_context_current(window)
gl.glClearColor(0, 0, 0.4, 0)
yield window
finally:
log.debug('~create_main_window(self):')
glfw.terminate()
def main_loop(self, window):
self.tri = Triangle() # IINSERT
while (
glfw.get_key(window, glfw.KEY_ESCAPE) != glfw.PRESS and
not glfw.window_should_close(window)
):
gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)
self.tri.render()
glfw.swap_buffers(window)
glfw.poll_events()
def run(self):
log.basicConfig(level=log.DEBUG)
with self.create_main_window() as window:
self.main_loop(window)

glGetError not catching errors

I am trying to render images with OpenGL using a compute shader. I create a compute shader and program for it, then a vertex shader and fragment shader, as well as a program for them. I then create a texture and two triangles and render the texture on the triangles. So far, I have been unable to get the shaders to do anything properly, since glCompileShader seems to not catch when shaders compile incorrectly, so I can't tell what's wrong. I have added glGetError calls after each of my compiling and linking steps to see what the problem might be, but none of them throw errors; all that happens is glUseProgram raises an invalid operation error.
renderShaderSource:
#version 460 core
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
layout(rgba32f, binding = 0) uniform image2D screen;
void main()
}
vertexShaderSource:
#version 460 core
layout (location = 0) in vec3 pos;
layout (location = 1) in vec2 uvs;
out vec2 UVs;
void main()
{
gl_Position = vec4(pos.x, pos.y, pos.z, 1.0);
UVs = uvs;
}
fragmentShaderSource:
#version 460 core
out vec4 FragColor;
uniform sampler2D screen;
in vec2 UVs;
void main()
{
FragColor = texture(screen, UVs);
}
Python code (with excess comments and irrelevant code removed):
class GLWidget(QOpenGLWidget):
width = 100
height = 100
def initializeGL(self):
print('--initializeGL--')
print('GL version', glGetString(GL_VERSION))
# Render shader
renderShader = glCreateShader(GL_COMPUTE_SHADER)
glShaderSource(renderShader, renderShaderSource)
glCompileShader(renderShader)
status = glGetError()
if status != GL_NO_ERROR: raise RuntimeError('Error {} compiling renderShader'.format(status))
# Render program
renderProgram = glCreateProgram()
glAttachShader(renderProgram, renderShader)
glLinkProgram(renderProgram)
status = glGetError()
if status != GL_NO_ERROR: raise RuntimeError('Error {} linking renderProgram'.format(status))
# Vertex shader
vertexShader = glCreateShader(GL_VERTEX_SHADER)
glShaderSource(vertexShader, vertexShaderSource)
glCompileShader(vertexShader)
status = glGetError()
if status != GL_NO_ERROR: raise RuntimeError('Error {} compiling vertexShader'.format(status))
# Fragment shader
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER)
glShaderSource(fragmentShader, fragmentShaderSource)
glCompileShader(fragmentShader)
status = glGetError()
if status != GL_NO_ERROR: raise RuntimeError('Error {} compiling fragmentShader'.format(status))
# Display program
displayProgram = glCreateProgram()
glAttachShader(displayProgram, vertexShader)
glAttachShader(displayProgram, fragmentShader)
glLinkProgram(displayProgram)
status = glGetError()
if status != GL_NO_ERROR: raise RuntimeError('Error {} linking displayProgram'.format(status))
# Texture to render to
screenTex = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, screenTex)
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA32F, self.width, self.height)
glBindImageTexture(0, screenTex, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA32F)
# Shape to render texture on
self.vertices = numpy.array([
-1.0, -1.0, 0.0, 0.0, 0.0,
-1.0, 1.0, 0.0, 0.0, 1.0,
1.0, 1.0, 0.0, 1.0, 1.0,
1.0, -1.0, 0.0, 1.0, 0.0
], dtype='float32')
# Buffers for rendering shape
vertexArray = glGenVertexArrays(1)
vertexBuffer = glGenBuffers(1)
glBindVertexArray(vertexArray)
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer)
glBufferData(GL_ARRAY_BUFFER, 4 * len(self.vertices), self.vertices, GL_STATIC_DRAW)
glVertexAttribPointer(0, 3, GL_FLOAT, False, 4 * 3, None)
glEnableVertexAttribArray(0)
# Unbind from the vertex buffer and vertex array
glBindBuffer(GL_ARRAY_BUFFER, 0)
glBindVertexArray(0)
self.renderProgram = renderProgram
self.displayProgram = displayProgram
self.vertexArray = vertexArray
self.vertexBuffer = vertexBuffer
def resizeGL(self, width, height):
print('--resizeGL--')
self.width = width
self.height = height
glViewport(0, 0, width, height)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
glOrtho(-1, 1, 1, -1, -1, 1)
def paintGL(self):
print('--paintGL--')
# Run render portion
glUseProgram(self.renderProgram)
glDispatchCompute(self.width, self.height, 1)
glMemoryBarrier(GL_ALL_BARRIER_BITS)
# Run display portion
glClearColor(0.0, 0.0, 0.0, 1.0)
glClear(GL_COLOR_BUFFER_BIT)
glUseProgram(self.displayProgram)
glBindVertexArray(self.vertexArray)
glDrawArrays(GL_TRIANGLES, 0, 6)
It's fairly clear that my compute shader (see renderShaderSource) is the problem in this scenario, I mean it's got mismatched { } things. But when I run the program, glUseProgram errors instead of glGetError:
--initUI--
--initializeGL--
GL version b'4.6 (Compatibility Profile) Mesa 21.2.6'
--resizeGL--
--paintGL--
Traceback (most recent call last):
File "gl.py", line 146, in paintGL
glUseProgram(self.renderProgram)
File "/home/aweso/.local/lib/python3.8/site-packages/OpenGL/platform/baseplatform.py", line 415, in __call__
return self( *args, **named )
File "/home/aweso/.local/lib/python3.8/site-packages/OpenGL/error.py", line 230, in glCheckError
raise self._errorClass(
OpenGL.error.GLError: GLError(
err = 1282,
description = b'invalid operation',
baseOperation = glUseProgram,
cArguments = (2,)
)
--paintGL--
Traceback (most recent call last):
File "gl.py", line 146, in paintGL
glUseProgram(self.renderProgram)
File "/home/aweso/.local/lib/python3.8/site-packages/OpenGL/error.py", line 230, in glCheckError
raise self._errorClass(
OpenGL.error.GLError: GLError(
err = 1282,
description = b'invalid operation',
baseOperation = glUseProgram,
cArguments = (2,)
)
Why did none of the glGetError calls catch that the compute shader failed to compile?
Python 3.8 on Ubuntu 20.04, Intel Core i3-1005G1 with integrated GPU. OpenGL 4.6 Mesa 21.2.6. OpenGL viewport is a PyQt6 QOpenGLWidget
Shader compile errors can't be get with glGetError. You have to call glGetShaderiv/glGetShaderInfoLog to get the compile errors. e.g.:
glCompileShader(renderShader)
if not glGetShaderiv(renderShader, GL_COMPILE_STATUS):
raise RuntimeError(glGetShaderInfoLog(renderShader).replace(b'\\n', b'\n'))
Program link errors can be get with glGetProgramiv/glGetProgramInfoLog. e.g.:
glLinkProgram(displayProgram)
if not glGetProgramiv(displayProgram, GL_LINK_STATUS):
raise RuntimeError(glGetProgramInfoLog(displayProgram).replace(b'\\n', b'\n'))
I also recommend Debug Output. e.g.:
#GLDEBUGPROC
def __CB_OpenGL_DebugMessage(source, type, id, severity, length, message, userParam):
msg = message[0:length]
print(msg.decode("utf-8"))
glDebugMessageCallback(__CB_OpenGL_DebugMessage, None)
glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, None, GL_TRUE)
glEnable(GL_DEBUG_OUTPUT)
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS)

How do I set up PyOpenGL with PyQt5?

I am wanting to set up several 3D mathematical projects with python. The best way I can see to render these is with PyOpenGL. I also want to run it in PyQt5, so that I can have GUI’s along side the render. All the information I can find is either using PyGame or QtDesigner. I would like to work without QtDesigner. Does anyone know where I could find a tutorial on how to set this up?
EDIT:
I managed to get some web scrounging done. I found the following code at https://pythonprogramming.net/community/37/Cube%20rotation%20with%20pyopengl%20and%20pyqt/ where the author asks for help regarding it not running. he says the following about it:
I'm very new to python. I have a problem with my code, it's a very simple rotating cube. I don't have any problem with this cube code with pygame screen but when I use it with pyqt (or Qt designer widgets), it runs but it shows nothing!!!
I copied his code into my IDe, then saved it as cubes.py. I opened up a CMD instance in thae directory of the file and called it. It opened a tiny black Qt window in the middle of the screen:
When I try to resize the window by dragging the corner, it throws a deep traceback:
File "C:\Users\aweso\Documents\Python\cubes\cubes.py", line 1, in <module>
from OpenGL.GL import *
ModuleNotFoundError: No module named 'OpenGL'
C:\Users\aweso\Documents\Python\cubes>cubes.py
Traceback (most recent call last):
File "C:\Users\aweso\Documents\Python\cubes\cubes.py", line 56, in paintGL
glEnd()
File "C:\Program Files (x86)\Microsoft Visual Studio\Shared\Python37_64\lib\site-packages\OpenGL\latebind.py", line 63, in __call__
return self.wrapperFunction( self.baseFunction, *args, **named )
File "C:\Program Files (x86)\Microsoft Visual Studio\Shared\Python37_64\lib\site-packages\OpenGL\GL\exceptional.py", line 45, in glEnd
return baseFunction( )
File "C:\Program Files (x86)\Microsoft Visual Studio\Shared\Python37_64\lib\site-packages\OpenGL\platform\baseplatform.py", line 415, in __call__
return self( *args, **named )
File "C:\Program Files (x86)\Microsoft Visual Studio\Shared\Python37_64\lib\site-packages\OpenGL\error.py", line 234, in glCheckError
baseOperation = baseOperation,
OpenGL.error.GLError: GLError(
err = 1280,
description = b'invalid enumerant',
baseOperation = glEnd,
cArguments = ()
)
Here is his code, unmodified:
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL import *
from PyQt5.QtOpenGL import *
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import *
import sys,time
class MainWindow(QGLWidget):
def __init__(self):
super(MainWindow, self).__init__()
self.widget = glWidget(self)
mainLayout = QVBoxLayout()
mainLayout.addWidget(self.widget)
self.setLayout(mainLayout)
class glWidget(QGLWidget):
def __init__(self, parent):
QGLWidget.__init__(self, parent)
#self.setMinimumSize(400, 400)
self.verticies = (
(1,-1,-1),
(1,1,-1),
(-1,1,-1),
(-1,-1,-1),
(1,-1,1),
(1,1,1),
(-1,-1,1),
(-1,1,1))
self.edges = (
(0,1),
(0,3),
(0,4),
(2,1),
(2,3),
(2,7),
(6,3),
(6,4),
(6,7),
(5,1),
(5,4),
(5,7))
def paintGL(self):
while True:
#glRotatef(1,3,1,1)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glBegin(GL_LINE)
for self.edge in self.edges:
for self.vertex in self.edge:
glVertex3fv(self.verticies[self.vertex])
glEnd()
glFlush()
time.sleep(1)
def resizeGL(self, w, h):
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
glOrtho(-50, 50, -50, 50, -50.0, 50.0)
glViewport(0, 0, w, h)
def initializeGL(self):
#glClearColor(0.0, 0.0, 0.0, 1.0)
gluPerspective(45,800/600,0.1,50.0)
glTranslatef(0.0,0.0,-5)
glRotatef(0,0,0,0)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
I once developed an interactive 3D program using PyOpenGL and PyQt5.
Here's the spike code of that time.
I wish this could be helpful to you.
import sys
import math
from array import array
from OpenGL import GL
from PyQt5.QtCore import pyqtSignal, QPoint, QSize, Qt
from PyQt5.QtGui import QColor, QImage
from PyQt5.QtWidgets import (QApplication, QHBoxLayout, QWidget)
from PyQt5.QtOpenGL import QGLWidget
from shader import ShaderProgram
class Window (QWidget):
def __init__(self):
super(Window, self).__init__()
self.glWidget = GLWidget()
mainLayout = QHBoxLayout()
mainLayout.addWidget(self.glWidget)
self.setLayout(mainLayout)
self.setWindowTitle('Hello')
class GLWidget (QGLWidget):
def __init__(self, parent=None):
super(GLWidget, self).__init__(parent)
def sizeHint(self):
return QSize(1200, 1000)
def initializeGL(self):
GL.glClearColor(0.0, 0.0, 1.0, 0.0) # it's also possible.
GL.glEnable(GL.GL_DEPTH_TEST)
# GL.glEnable(GL.GL_VERTEX_ARRAY)
self._createVertexBuffer()
self.program = ShaderProgram('hello.vert', 'hello.frag')
self.program.use()
self.frontTexture = self._createTexture('tex.png')
self.backTexture = self._createTexture('back-tex.jpg')
# set texture units
GL.glActiveTexture(GL.GL_TEXTURE0)
GL.glBindTexture(GL.GL_TEXTURE_2D, self.frontTexture)
GL.glUniform1i(GL.glGetUniformLocation(self.program.getProgram(), b'frontTexture'), 0)
GL.glActiveTexture(GL.GL_TEXTURE1)
GL.glBindTexture(GL.GL_TEXTURE_2D, self.backTexture)
GL.glUniform1i(GL.glGetUniformLocation(self.program.getProgram(), b'backTexture'), 1)
def paintGL(self):
GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT)
self._draw()
def resizeGL(self, width, height):
side = min(width, height)
if side < 0:
return
GL.glViewport((width - side) // 2, (height - side) // 2, side, side)
def _createTexture(self, texFilePath):
qImage = QImage(texFilePath)
texture = QGLWidget.bindTexture(self, qImage, GL.GL_TEXTURE_2D, GL.GL_RGBA)
GL.glGenerateMipmap(GL.GL_TEXTURE_2D)
return texture
def _createVertexBuffer(self):
vertices = array('f', [-1.0, -1.0, 0.0, 1.0, -1.0, 0.0, 1.0, 1.0, 0.0, -1.0, 1.0, 0.0]).tobytes()
colors = array('f', [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0]).tobytes()
indices = [0, 1, 2, 0, 3, 2]
texCoords = array('f', [0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0]).tobytes()
self.vertices = vertices
self.colors = colors
self.indices = indices
self.texCoords = texCoords
def _draw(self):
GL.glEnableVertexAttribArray(0)
GL.glEnableVertexAttribArray(1)
GL.glEnableVertexAttribArray(2)
GL.glVertexAttribPointer(0, 3, GL.GL_FLOAT, GL.GL_FALSE, 0, self.vertices)
GL.glVertexAttribPointer(1, 3, GL.GL_FLOAT, GL.GL_FALSE, 0, self.colors)
GL.glVertexAttribPointer(2, 2, GL.GL_FLOAT, GL.GL_FALSE, 0, self.texCoords)
GL.glDrawElements(GL.GL_TRIANGLES, 6, GL.GL_UNSIGNED_INT, self.indices)
GL.glDisableVertexAttribArray(0)
GL.glDisableVertexAttribArray(1)
GL.glDisableVertexAttribArray(2)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())

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

I am developing a software in which I have an OpenGL window. I am creating the GUI of the software using PyQt5 and for the opengGL Window I am using QOpengGLWidget and for object selection, I am using Stencil Buffer and reading STENCIL_INDEX as the following function on mousePressEvent(self, event):
id = glReadPixels(event.x(), self.height - event.y() - 1, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)
but this is not working when I am using with Qt5 GUI and showing following error:
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)
and here is the code for my custom 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)
and the same is working properly when I am using this with GLUT window as following:
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()
How to resolve the problem with QT5 GUI ?
I suspect there's no current OpenGL context when you call glReadPixels. You're mousePressEvent should probably call 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()
glReadPixels requires a render context to be current on the thread. Qt does automatic context managment and it's well documented that outside of methods named …GL the context may not be current.
You must make the context current explicitly outside of those methods, for which the Qt OpenGL classes offer the methods makeCurrent and doneCurrent. Brace your call of glReadPixels with calls to these functions; make sure to check, that makeCurrent succeeds, as the context may already be current in a different thread.

glDrawArrays returning invalid operation 1282 - pyOpenGL

I cannot figure this out for the life of me. I need a second pair of eyes ... or a better brain. I am trying to get this "Hello Triangle" python example working. I've been translating it from a c tutorial. However, I keep getting this error no matter what I do.
Traceback (most recent call last):
File ".../demo/tester.py", line 107, in <module>
if __name__ == '__main__': main()
File ".../demo/tester.py", line 87, in main
glDrawArrays(GL_TRIANGLES, 0, 3)
File ".../venv/lib/python3.6/site packages/OpenGL/platform/baseplatform.py", line 402, in __call__
return self( *args, **named )
File ".../venv/lib/python3.6/site-packages/OpenGL/error.py", line 232, in glCheckError
baseOperation = baseOperation,
OpenGL.error.GLError: GLError(
err = 1282,
description = b'invalid operation',
baseOperation = glDrawArrays,
cArguments = (GL_TRIANGLES, 0, 3)
My code is below. I am running on a Mac so you'll notice some things in there that might not be needed for PC. Everything works fine up until the glDrawArrays. I know that some of the functions between openGL for C vs python using pyOpenGL are a bit different. I've been cross referencing the documentation to make sure I am not trying write like a C programmer in Python.
try:
from AppKit import NSApp, NSApplication
except:
pass
import sys
import cyglfw3 as glfw
import numpy as np
from OpenGL.GL import *
from OpenGL.GLUT import *
from OpenGL.arrays import vbo as glvbo
def main():
glfw.Init()
glfw.WindowHint(glfw.CONTEXT_VERSION_MAJOR, 3)
glfw.WindowHint(glfw.CONTEXT_VERSION_MINOR, 3)
glfw.WindowHint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)
glfw.WindowHint(glfw.OPENGL_FORWARD_COMPAT, GL_TRUE)
window = glfw.CreateWindow(800, 600, 'LearnOpenGL', None)
if window is None:
glfw.Terminate()
exit()
glfw.MakeContextCurrent(window)
glfw.SetFramebufferSizeCallback(window, framebuffer_size_callback())
# Setting up a vertex buffer
verts = glvbo.VBO(
np.array([[0.0, 0.5, 0.0], [0.5, -0.5, 0.0], [-0.5, -0.5, 0.0]], 'f')
)
vbo = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, vbo)
glBufferData(GL_ARRAY_BUFFER, verts.size, verts, GL_STATIC_DRAW)
vao = glGenVertexArrays(1)
glBindVertexArray(vao)
glEnableVertexAttribArray(vao)
glBindBuffer(GL_ARRAY_BUFFER, vbo)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, None)
vertex_shader = """
#version 330 core
in vec3 vp;
void main() {
gl_Position = vec4(vp, 1.0);
}"""
fragment_shader = """
#version 330 core
out vec4 frag_color;
void main() {
frag_color = vec4(0.5, 0.0, 0.5, 1.0);
}"""
vs = glCreateShader(GL_VERTEX_SHADER)
glShaderSource(vs, vertex_shader)
glCompileShader(vs)
if glGetShaderiv(vs, GL_COMPILE_STATUS) != GL_TRUE:
raise Exception("vertex shader did not compile")
fs = glCreateShader(GL_FRAGMENT_SHADER)
glShaderSource(fs, fragment_shader)
glCompileShader(fs)
if glGetShaderiv(fs, GL_COMPILE_STATUS) != GL_TRUE:
raise Exception("fragment shader did not compile")
sp = glCreateProgram()
glAttachShader(sp, fs)
glAttachShader(sp, vs)
glLinkProgram(sp)
if glGetProgramiv(sp, GL_LINK_STATUS) != GL_TRUE:
raise Exception("program did not link")
while not glfw.WindowShouldClose(window):
processInput(window)
glClearColor(0.0, 0.0, 0.0, 1.0)
glClear(GL_COLOR_BUFFER_BIT)
glUseProgram(sp)
glBindVertexArray(vao)
glDrawArrays(GL_TRIANGLES, 0, 3) # This is where things happen
glfw.PollEvents()
glfw.SwapBuffers(window)
glfw.Terminate()
exit()
def framebuffer_size_callback():
glViewport(0, 0, 800, 600)
def processInput(window):
if glfw.GetKey(window, glfw.KEY_ESCAPE) == glfw.PRESS:
glfw.SetWindowShouldClose(window, True)
if __name__ == '__main__': main()
All I want to do at the end of the day, is draw a triangle :(
I determined there were 2 issues.
As BDL mentioned the glEnableVertexAttribArray was not setup appropriately. It needed an index and not a vertex array object.
The glBufferData size was incorrect as the size needed to be provided in bytes. A quick search online revealed that (numpy.size * numpy.itemsize) returns the size of the array.
With these 2 things done... I have a triangle.

Categories