wxPython glCanvas intermittent display - python

A python translation of the code in chapter 5 of Joey de Vries "Learn Opengl - Graphics Programming" works about half the time.
That is, the orange triangle is seen about 50% of the time otherwise it's a dark gray screen. No error messages are generated.
#!/usr/bin/env python
# Python ---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---
import os
# https://github.com/Amulet-Team/Amulet-Map-Editor/issues/247 (see Cebtenzzre)
# The following line addresses a wxPython 4.1.1 issue
os.environ['PYOPENGL_PLATFORM'] = 'egl'
# wxPython -+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---
import wx
from wx import glcanvas
# PyPi -+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---
import numpy as np
from OpenGL.GL import *
# My Files -+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---
# Shaders --+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---
vs = """
#version 330 core
layout (location = 0) in vec3 aPos ;
void main () {
gl_Position = vec4 (aPos.x, aPos.y, aPos.z, 1.0) ;
}
"""
fs = """
#version 330 core
out vec4 FragColor ;
void main () {
FragColor = vec4 (1.0f, 0.5f, 0.2f, 1.0f) ;
}
"""
#---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---
class View (glcanvas.GLCanvas):
def __init__ (self, parent):
cnvsAttr = glcanvas.GLAttributes () # Set the canvas attributes
cnvsAttr.PlatformDefaults ().MinRGBA (8, 8, 8, 8).DoubleBuffer ().Depth (24).Stencil (8).EndList ()
super ().__init__ (parent, cnvsAttr)
self.context = glcanvas.GLContext (self)
self.Bind (wx.EVT_SIZE, self.on_size)
self.Bind (wx.EVT_PAINT, self.on_draw)
def on_draw (self, event):
if self.IsShownOnScreen ():
result = self.SetCurrent (self.context)
# print ('\n', result, '\n') #x
vShader = glCreateShader (GL_VERTEX_SHADER)
glShaderSource (vShader, vs)
glCompileShader (vShader)
if glGetShaderiv (vShader, GL_COMPILE_STATUS) != GL_TRUE:
print (f"SHADER COMPILER ERROR: Shader index {vShader} did not compile")
print (glGetShaderInfoLog (vShader))
fShader = glCreateShader (GL_FRAGMENT_SHADER)
glShaderSource (fShader, fs)
glCompileShader (fShader)
if glGetShaderiv (fShader, GL_COMPILE_STATUS) != GL_TRUE:
print (f"SHADER COMPILER ERROR: Shader index {fShader} did not compile")
print (glGetShaderInfoLog (fShader))
program = glCreateProgram ()
glAttachShader (program, vShader)
glAttachShader (program, fShader)
glLinkProgram (program)
if glGetProgramiv (program, GL_LINK_STATUS) != GL_TRUE:
print (f"PROGRAM COMPILER ERROR: Program index {program} did not compile")
print (glGetProgramInfoLog (program))
glDeleteShader (vShader)
glDeleteShader (fShader)
####################################################################
VAO = glGenVertexArrays (1) # This MUST precede VBO creation
glBindVertexArray (VAO)
vertices = np.array ([-0.5, -0.5, 0.0,
0.5, -0.5, 0.0,
0.0, 0.5, 0.0], dtype = np.float32)
VBO = glGenBuffers (1)
glBindBuffer (GL_ARRAY_BUFFER, VBO)
glBufferData (GL_ARRAY_BUFFER, vertices.nbytes, vertices, GL_STATIC_DRAW)
glVertexAttribPointer (0, 3, GL_FLOAT, GL_FALSE, 0, 0)
glEnableVertexAttribArray (0)
glClearColor (0.25, 0.25, 0.25, 1.0)
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glUseProgram (program)
glBindVertexArray (VAO)
glDrawArrays (GL_TRIANGLES, 0, 3)
glBindVertexArray (0)
self.SwapBuffers ()
self.Refresh ()
def on_size (self, event):
size = event.GetSize ()
glViewport (0, 0, size.x, size.y)
#---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---
class stub_5a (wx.Frame):
def __init__ (self):
super ().__init__ (None)
self.SetTitle ("stub_5a ()")
self.SetClientSize ((400, 300))
panel = wx.Panel (self)
sizer = wx.BoxSizer (wx.VERTICAL)
self.View = View (panel)
sizer.Add (self.View, 1, wx.ALL | wx.EXPAND)
panel.SetSizer (sizer)
#---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---
if __name__ == '__main__':
app = wx.App (False)
frame = stub_5a ()
frame.Centre (wx.BOTH)
frame.Show ()
app.MainLoop ()
the environment for the above code is:
OS: Linux (5.4.0-132-generic) (Ubuntu 20.04)
Python: 3.8.10
wxPython: 4.1.1 gtk3 (phoenix) wxWidgets 3.1.5
PyOpenGL: 3.1.6
Numpy: 1.22.4
Pillow: 9.1.1
SQLite3: 3.31.1
and:
OpenGL version: b'4.6 (Compatibility Profile) Mesa 21.2.6'
GLSL version: b'4.60'
Vendor: b'Intel'
Renderer: b'Mesa Intel(R) Iris(R) Plus Graphics 640 (Kaby Lake GT3e) (KBL GT3)'
GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: 192
GL_MAX_CUBE_MAP_TEXTURE_SIZE: 16384
GL_MAX_DRAW_BUFFERS: 8
GL_MAX_FRAGMENT_UNIFORM_COMPONENTS: 16384
GL_MAX_TEXTURE_IMAGE_UNITS: 32
GL_MAX_TEXTURE_SIZE: 16384
GL_MAX_VARYING_FLOATS: 128
GL_MAX_VERTEX_ATTRIBS: 16
GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS: 32
GL_MAX_VERTEX_UNIFORM_COMPONENTS: 16384
GL_MAX_RENDERBUFFER_SIZE: 16384
GL_MAX_VIEWPORTS: 16
GL_MAX_VIEWPORT_DIMS: 16384, 16384
GL_STEREO: False

Related

Why glVertexAttribPointer throws 1282 error while trying to draw one point on screen with pyOpenGL and glfw?

I have separated the program to three different files, but I don't understand why I get error on glVertexAttribPointer on line 70. I'm using Python 3.10.8
main.py
import glfw
import Shaders
from OpenGL.GL import *
from OpenGL.GLUT import *
from Math_3d import Vector2f
class Window:
def __init__(self, width: int, height: int, title: str):
if not glfw.init():
raise Exception("glfw can not be initialized")
glfw.window_hint(glfw.CONTEXT_VERSION_MAJOR, 3)
glfw.window_hint(glfw.CONTEXT_VERSION_MINOR, 3)
glfw.window_hint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)
self._win = glfw.create_window(width, height, title, None, None)
if not self._win:
glfw.terminate()
raise Exception("glfw window can not be created")
glfw.set_window_pos(self._win, 400, 200)
glfw.make_context_current(self._win)
def createshaders(self):
# Request program and shader slots from the GPU
program = glCreateProgram()
vertex = glCreateShader(GL_VERTEX_SHADER)
fragment = glCreateShader(GL_FRAGMENT_SHADER)
# Set shader sources
glShaderSource(vertex, Shaders.vertex_code)
glShaderSource(fragment, Shaders.fragment_code)
# Compile shaders
glCompileShader(vertex)
glCompileShader(fragment)
if not glGetShaderiv(vertex, GL_COMPILE_STATUS):
report_shader = glGetShaderInfoLog(vertex)
print(report_shader)
raise RuntimeError("Vertex shader compilation error")
if not glGetShaderiv(fragment, GL_COMPILE_STATUS):
report_frag = glGetShaderInfoLog(fragment)
print(report_frag)
raise RuntimeError("Fragment shader compilation error")
# Link objects to program
glAttachShader(program, vertex)
glAttachShader(program, fragment)
glLinkProgram(program)
if not glGetProgramiv(program, GL_LINK_STATUS):
print(glGetProgramInfoLog(program))
raise RuntimeError('Linking error')
# Get rid of shaders
glDetachShader(program, vertex)
glDetachShader(program, fragment)
# Make default program to run
glUseProgram(program)
# Vertex Buffer Object
# Create point vertex data
v2f_1 = Vector2f(0.0, 0.0)
# Request a buffer slot from GPU
buffer = glGenBuffers(1)
# Make this buffer the default one
glBindBuffer(GL_ARRAY_BUFFER, buffer)
strides = v2f_1.data.strides[0]
loc = glGetAttribLocation(program, 'position')
glEnableVertexAttribArray(loc)
glVertexAttribPointer(loc, 2, GL_FLOAT, False, strides, None)
# glBufferData(GL_ARRAY_BUFFER, v2f_1, v2f_1, GL_DYNAMIC_DRAW)
def renderscene(self):
while not glfw.window_should_close(self._win):
glfw.poll_events()
glClear(GL_COLOR_BUFFER_BIT)
glDrawArrays(GL_POINTS, 0, 1)
glfw.swap_buffers(self._win)
glfw.terminate()
if __name__ == '__main__':
win = Window(1024, 768, "GLFW Window")
win.createshaders() # Create and initialize shaders and initialize Vertex Buffer Object
win.renderscene() # Swap buffer and render scene
Shaders.py
vertex_code = """
attribute vec2 position;
void main()
{
gl_Position = vec4(position, 0.0, 1.0);
}
"""
fragment_code = """
void main()
{
gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
}
"""
Math_3d.py
import numpy as np
class Vector2f:
def __init__(self, x, y):
self.data = np.array([x, y], dtype=np.float32)
if __name__ == '__main__':
vec2 = Vector2f(0.0, 0.0)
print(vec2.data)
print(type(vec2.data.strides[0]))
print(vec2.data.strides[0])
I have tried to debug the line 70, but did not get any good result while using PyCharm.
Any recommendations on this? Closest answers would be according to 61491497 and 56957118 what I am aiming for.
You're using a Core profile OpenGL Context (glfw.OPENGL_CORE_PROFILE). Therefore you have to create a Vertex Array Obejct:
class Window:
# [...]
def createshaders(self):
# [...]
v2f_1 = Vector2f(0.0, 0.0)
# Request a buffer slot from GPU
buffer = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, buffer)
strides = v2f_1.data.strides[0]
glBufferData(GL_ARRAY_BUFFER, v2f_1.data, GL_DYNAMIC_DRAW)
vao = glGenVertexArrays(1)
glBindVertexArray(vao)
loc = glGetAttribLocation(program, 'position')
glEnableVertexAttribArray(loc)
glVertexAttribPointer(loc, 2, GL_FLOAT, False, strides, None)
Additionally, you need to change either the background color or the fragment color, because you won't be able to see a black point on a black background. e.g. red:
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);

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)

PyOpengGL raises error when I compile shaders

I am running through a cumulation of OpenGL shader tutorials and mixing and matching stuff, trying to get a custom shader implemented. I have the following Python code and traceback:
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QOpenGLWidget
from PyQt5.QtCore import Qt
from OpenGL.GL import (
glLoadIdentity, glTranslatef, glRotatef,
glClear, glBegin, glEnd,
glColor3fv, glVertex3fv,
GL_COLOR_BUFFER_BIT, GL_DEPTH_BUFFER_BIT,
GL_QUADS, GL_LINES,
shaders, GL_VERTEX_SHADER, GL_FRAGMENT_SHADER
)
from OpenGL.GLU import gluPerspective
class mainWindow(QMainWindow): #Main class.
def keyPressEvent(self, event): #This is the keypress detector.
try:
key = event.key()
except:
key = -1
#print(key)
if key == 16777216:
exit()
vertices = [
(-1, 1, 0),
(1, 1, 0),
(1, -1, 0),
(-1, -1, 0)
]
wires = [
(0, 1),
(1, 2),
(2, 3),
(0, 3)
]
facets = [
(0, 1, 2, 3)
]
zoomLevel = -5
rotateDegreeH = 0
rotateDegreeV = -45
vertShaderCode = """#version 120
void main() {
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}"""
fragShaderCode = """#version 120
void main() {
gl_FragColor = vec4( 0, 1, 0, 1 );
}"""
def __init__(self):
super(mainWindow, self).__init__()
self.sizeX = 700 #Variables used for the setting of the size of everything
self.sizeY = 600
self.setGeometry(0, 0, self.sizeX + 50, self.sizeY) #Set the window size
#make shaders
VERTEX_SHADER = shaders.compileShader(self.vertShaderCode, GL_VERTEX_SHADER)
FRAGMENT_SHADER = shaders.compileShader(self.fragShaderCode, GL_FRAGMENT_SHADER)
self.shader = shaders.compileProgram(VERTEX_SHADER,FRAGMENT_SHADER)
self.openGLWidget = QOpenGLWidget(self) #Create the GLWidget
self.openGLWidget.setGeometry(0, 0, self.sizeX, self.sizeY)
self.openGLWidget.initializeGL()
self.openGLWidget.resizeGL(self.sizeX, self.sizeY) #Resize GL's knowledge of the window to match the physical size?
self.openGLWidget.paintGL = self.paintGL #override the default function with my own?
def nav(self, hVal = 0, vVal = 0, zVal = 0):
self.zoomLevel += zVal
self.rotateDegreeH += hVal
self.rotateDegreeV += vVal
self.openGLWidget.update()
def paintGL(self):
#This function uses shape objects, such as cube() or mesh(). Shape objects require the following:
#a list named 'vertices' - This list is a list of points, from which edges and faces are drawn.
#a list named 'wires' - This list is a list of tuples which refer to vertices, dictating where to draw wires.
#a list named 'facets' - This list is a list of tuples which refer to vertices, ditating where to draw facets.
#a bool named 'render' - This bool is used to dictate whether or not to draw the shape.
#a bool named 'drawWires' - This bool is used to dictate whether wires should be drawn.
#a bool named 'drawFaces' - This bool is used to dictate whether facets should be drawn.
shaders.glUseProgram(self.shader)
glLoadIdentity()
gluPerspective(45, self.sizeX / self.sizeY, 0.1, 110.0) #set perspective?
glTranslatef(0, 0, self.zoomLevel) #I used -10 instead of -2 in the PyGame version.
glRotatef(self.rotateDegreeV, 1, 0, 0) #I used 2 instead of 1 in the PyGame version.
glRotatef(self.rotateDegreeH, 0, 0, 1)
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
glBegin(GL_LINES)
for w in self.wires:
for v in w:
glVertex3fv(self.vertices[v])
glEnd()
glBegin(GL_QUADS)
for f in self.facets:
for v in f:
glVertex3fv(self.vertices[v])
glEnd()
app = QApplication([])
window = mainWindow()
window.show()
sys.exit(app.exec_())
C:\Users\ccronk22\Documents\Python\glShaders>pygl002.py
Traceback (most recent call last):
File "C:\Users\ccronk22\AppData\Local\Programs\Python\Python38\lib\site-packages\OpenGL\latebind.py", line 43, in __call__
return self._finalCall( *args, **named )
TypeError: 'NoneType' object is not callable
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "C:\Users\ccronk22\Documents\Python\glShaders\pyGL002.py", line 109, in <module>
window = mainWindow()
File "C:\Users\ccronk22\Documents\Python\glShaders\pyGL002.py", line 59, in __init__
VERTEX_SHADER = shaders.compileShader(self.vertShaderCode, GL_VERTEX_SHADER)
File "C:\Users\ccronk22\AppData\Local\Programs\Python\Python38\lib\site-packages\OpenGL\GL\shaders.py", line 228, in compileShader
shader = glCreateShader(shaderType)
File "C:\Users\ccronk22\AppData\Local\Programs\Python\Python38\lib\site-packages\OpenGL\latebind.py", line 46, in __call__
self._finalCall = self.finalise()
File "C:\Users\ccronk22\AppData\Local\Programs\Python\Python38\lib\site-packages\OpenGL\extensions.py", line 242, in finalise
raise error.NullFunctionError(
OpenGL.error.NullFunctionError: Attempt to call an undefined alternate function (glCreateShader, glCreateShaderObjectARB), check for bool(glCreateShader) before calling
C:\Users\ccronk22\Documents\Python\glShaders>
The above code works well if you comment out the compileShader, compileProgram, and useProgram lines. It produces a white square, viewed from an above angle:
def __init__(self):
super(mainWindow, self).__init__()
self.sizeX = 700 #Variables used for the setting of the size of everything
self.sizeY = 600
self.setGeometry(0, 0, self.sizeX + 50, self.sizeY) #Set the window size
#make shaders
#VERTEX_SHADER = shaders.compileShader(self.vertShaderCode, GL_VERTEX_SHADER)
#FRAGMENT_SHADER = shaders.compileShader(self.fragShaderCode, GL_FRAGMENT_SHADER)
#self.shader = shaders.compileProgram(VERTEX_SHADER,FRAGMENT_SHADER)
[…]
def paintGL(self):
#This function uses shape objects, such as cube() or mesh(). Shape objects require the following:
#a list named 'vertices' - This list is a list of points, from which edges and faces are drawn.
#a list named 'wires' - This list is a list of tuples which refer to vertices, dictating where to draw wires.
#a list named 'facets' - This list is a list of tuples which refer to vertices, ditating where to draw facets.
#a bool named 'render' - This bool is used to dictate whether or not to draw the shape.
#a bool named 'drawWires' - This bool is used to dictate whether wires should be drawn.
#a bool named 'drawFaces' - This bool is used to dictate whether facets should be drawn.
#shaders.glUseProgram(self.shader)
Some additional information:
I am attempting to make the code from http://pyopengl.sourceforge.net/context/tutorials/shader_1.html work in the PyQt5 context, with only the OpenGL and PyQt5 APIs. The tutorial was written in Python2. I am using Python3.
So, what am I doing wrong with my shader compilation? Is it in the Python or GLSL?
To compile and link the shader you need a valid and current OpenGL Context. Note the OpenGL context have to be current when any OpenGL instruction is invoked.
QOpenGLWidget provides the virtual methods
def initializeGL ()
def paintGL ()
def resizeGL (w, h)
where the OpenGL context is active. This methods are callbacks and invoked by Qts event handling. Don't call them in your code.
Create the shader in the initializeGL callback:
class mainWindow(QMainWindow):
def __init__(self):
super(mainWindow, self).__init__()
self.sizeX = 700 #Variables used for the setting of the size of everything
self.sizeY = 600
self.setGeometry(0, 0, self.sizeX + 50, self.sizeY) #Set the window size
self.openGLWidget = QOpenGLWidget(self) #Create the GLWidget
self.openGLWidget.setGeometry(0, 0, self.sizeX, self.sizeY)
self.openGLWidget.resizeGL(self.sizeX, self.sizeY) #Resize GL's knowledge of the window to match the physical size?
self.openGLWidget.initializeGL = self.initializeGL
self.openGLWidget.paintGL = self.paintGL #override the default function with my own?
self.shader = None
def nav(self, hVal = 0, vVal = 0, zVal = 0):
self.zoomLevel += zVal
self.rotateDegreeH += hVal
self.rotateDegreeV += vVal
self.openGLWidget.update()
def initializeGL(self):
#make shaders
VERTEX_SHADER = shaders.compileShader(self.vertShaderCode, GL_VERTEX_SHADER)
FRAGMENT_SHADER = shaders.compileShader(self.fragShaderCode, GL_FRAGMENT_SHADER)
self.shader = shaders.compileProgram(VERTEX_SHADER,FRAGMENT_SHADER)
def paintGL(self):
glUseProgram(self.shader)
# [...]
(The import of glUseProgram is missing in your code)

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.

Looking for a simple OpenGL (3.2+) Python example that uses GLFW [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking us to recommend or find a tool, library or favorite off-site resource are off-topic for Stack Overflow as they tend to attract opinionated answers and spam. Instead, describe the problem and what has been done so far to solve it.
Closed 9 years ago.
Improve this question
I am looking for a simple modern OpenGL (3.2+)example in Python.
I tried with GLUT and freeGLUT, but I am not able to get a 3.2 context on OS X (Mavericks). (This seems to be a known issue with GLUT/freeGLUT).
GLFW seems to be a modern lightweight alternative to GLUT, but it doesn't seem to have an official Python binding, and I could not find a simple example that uses 3.2 core profile features of OpenGL with GLFW and Python.
(I struggled with this problem, and so it could be useful for others, I am answering below as per SO guidelines.)
The code below uses PyOpenGL, PIL (for textures), numpy, GLFW and the corresponding Python binding cyglfw3.
Here is a screenshot of the output:
The main code is appended below. It uses some utility methods from a file called glutils.py (for loading texture, compiling shaders, etc.) which you can find here:
https://github.com/electronut/pp/tree/master/simplegl
Code listing follows:
import OpenGL
from OpenGL.GL import *
from OpenGL.GLUT import *
import numpy, math, sys, os
import glutils
import cyglfw3 as glfw
strVS = """
#version 330 core
layout(location = 0) in vec3 aVert;
uniform mat4 uMVMatrix;
uniform mat4 uPMatrix;
uniform vec4 uColor;
uniform float uTheta;
out vec4 vCol;
out vec2 vTexCoord;
void main() {
// rotational transform
mat4 rot = mat4(
vec4( cos(uTheta), sin(uTheta), 0.0, 0.0),
vec4(-sin(uTheta), cos(uTheta), 0.0, 0.0),
vec4(0.0, 0.0, 1.0, 0.0),
vec4(0.0, 0.0, 0.0, 1.0)
);
// transform vertex
gl_Position = uPMatrix * uMVMatrix * rot * vec4(aVert, 1.0);
// set color
vCol = vec4(uColor.rgb, 1.0);
// set texture coord
vTexCoord = aVert.xy + vec2(0.5, 0.5);
}
"""
strFS = """
#version 330 core
in vec4 vCol;
in vec2 vTexCoord;
uniform sampler2D tex2D;
uniform bool showCircle;
out vec4 fragColor;
void main() {
if (showCircle) {
// discard fragment outside circle
if (distance(vTexCoord, vec2(0.5, 0.5)) > 0.5) {
discard;
}
else {
fragColor = texture(tex2D, vTexCoord);
}
}
else {
fragColor = texture(tex2D, vTexCoord);
}
}
"""
class Scene:
""" OpenGL 3D scene class"""
# initialization
def __init__(self):
# create shader
self.program = glutils.loadShaders(strVS, strFS)
glUseProgram(self.program)
self.pMatrixUniform = glGetUniformLocation(self.program,
'uPMatrix')
self.mvMatrixUniform = glGetUniformLocation(self.program,
"uMVMatrix")
self.colorU = glGetUniformLocation(self.program, "uColor")
# color
self.col0 = [1.0, 0.0, 0.0, 1.0]
# texture
self.tex2D = glGetUniformLocation(self.program, "tex2D")
# define quad vertices
quadV = [
-0.5, -0.5, 0.0,
0.5, -0.5, 0.0,
-0.5, 0.5, 0.0,
0.5, 0.5, 0.0
]
# set up vertex array object (VAO)
self.vao = glGenVertexArrays(1)
glBindVertexArray(self.vao)
# vertices
self.vertexBuffer = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, self.vertexBuffer)
vertexData = numpy.array(quadV, numpy.float32)
glBufferData(GL_ARRAY_BUFFER, 4*len(vertexData), vertexData,
GL_STATIC_DRAW)
# enable vertex array
glEnableVertexAttribArray(0)
# set buffer data
glBindBuffer(GL_ARRAY_BUFFER, self.vertexBuffer)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, None)
# unbind VAO
glBindVertexArray(0)
# time
self.t = 0
# texture
self.texId = glutils.loadTexture('test.png')
# show circle?
self.showCircle = False
# step
def step(self):
# increment angle
self.t = (self.t + 1) % 360
# set shader angle in radians
glUniform1f(glGetUniformLocation(self.program, 'uTheta'),
math.radians(self.t))
# render
def render(self, pMatrix, mvMatrix):
# use shader
glUseProgram(self.program)
# set proj matrix
glUniformMatrix4fv(self.pMatrixUniform, 1, GL_FALSE, pMatrix)
# set modelview matrix
glUniformMatrix4fv(self.mvMatrixUniform, 1, GL_FALSE, mvMatrix)
# show circle?
glUniform1i(glGetUniformLocation(self.program, 'showCircle'),
self.showCircle)
# enable texture
glActiveTexture(GL_TEXTURE0)
glBindTexture(GL_TEXTURE_2D, self.texId)
glUniform1i(self.tex2D, 0)
# bind VAO
glBindVertexArray(self.vao)
# draw
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)
# unbind VAO
glBindVertexArray(0)
class RenderWindow:
"""GLFW Rendering window class"""
def __init__(self):
# save current working directory
cwd = os.getcwd()
# initialize glfw - this changes cwd
glfw.Init()
# restore cwd
os.chdir(cwd)
# version hints
glfw.WindowHint(glfw.CONTEXT_VERSION_MAJOR, 3)
glfw.WindowHint(glfw.CONTEXT_VERSION_MINOR, 3)
glfw.WindowHint(glfw.OPENGL_FORWARD_COMPAT, GL_TRUE)
glfw.WindowHint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)
# make a window
self.width, self.height = 640, 480
self.aspect = self.width/float(self.height)
self.win = glfw.CreateWindow(self.width, self.height, "test")
# make context current
glfw.MakeContextCurrent(self.win)
# initialize GL
glViewport(0, 0, self.width, self.height)
glEnable(GL_DEPTH_TEST)
glClearColor(0.5, 0.5, 0.5,1.0)
# set window callbacks
glfw.SetMouseButtonCallback(self.win, self.onMouseButton)
glfw.SetKeyCallback(self.win, self.onKeyboard)
glfw.SetWindowSizeCallback(self.win, self.onSize)
# create 3D
self.scene = Scene()
# exit flag
self.exitNow = False
def onMouseButton(self, win, button, action, mods):
#print 'mouse button: ', win, button, action, mods
pass
def onKeyboard(self, win, key, scancode, action, mods):
#print 'keyboard: ', win, key, scancode, action, mods
if action == glfw.PRESS:
# ESC to quit
if key == glfw.KEY_ESCAPE:
self.exitNow = True
else:
# toggle cut
self.scene.showCircle = not self.scene.showCircle
def onSize(self, win, width, height):
#print 'onsize: ', win, width, height
self.width = width
self.height = height
self.aspect = width/float(height)
glViewport(0, 0, self.width, self.height)
def run(self):
# initializer timer
glfw.SetTime(0.0)
t = 0.0
while not glfw.WindowShouldClose(self.win) and not self.exitNow:
# update every x seconds
currT = glfw.GetTime()
if currT - t > 0.1:
# update time
t = currT
# clear
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
# build projection matrix
pMatrix = glutils.perspective(45.0, self.aspect, 0.1, 100.0)
mvMatrix = glutils.lookAt([0.0, 0.0, -2.0], [0.0, 0.0, 0.0],
[0.0, 1.0, 0.0])
# render
self.scene.render(pMatrix, mvMatrix)
# step
self.scene.step()
glfw.SwapBuffers(self.win)
# Poll for and process events
glfw.PollEvents()
# end
glfw.Terminate()
# main() function
def main():
print 'starting simpleglfw...'
rw = RenderWindow()
rw.run()
# call main
if __name__ == '__main__':
main()

Categories