I am trying to integrate the advice from https://gamedev.stackexchange.com/questions/10727/fastest-way-to-draw-quads-in-opengl-es and How to draw with Vertex Array Objects and glDrawElements in PyOpenGL to render quads using an index buffer implemented by OpenGL.arrays.vbo.VBO
, in a context provided by GLFW and using the helper class. (I am also, at least for now, relying on ctypes
for raw data rather than using Numpy.)
I produced the following minimal example (if I can fix this, I should be able to fix the actual code):
import ctypes
import glfw
from OpenGL.arrays import vbo
from OpenGL.GL import *
from OpenGL.GL import shaders
def setup_window():
glfw.init()
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)
window = glfw.create_window(256,256,'test',None,None)
glfw.make_context_current(window)
glfw.set_input_mode(window, glfw.STICKY_KEYS, True)
return window
def get_program():
VERTEX_SHADER = shaders.compileShader("""
#version 330
layout(location = 0) in vec4 position;
void main()
{
gl_Position = position;
}
""", GL_VERTEX_SHADER)
FRAGMENT_SHADER = shaders.compileShader("""
#version 330
out vec4 outputColor;
void main()
{
outputColor = vec4(0.0f, 1.0f, 0.0f, 1.0f);
}
""", GL_FRAGMENT_SHADER)
return shaders.compileProgram(VERTEX_SHADER, FRAGMENT_SHADER)
window = setup_window()
glUseProgram(get_program())
vertices = vbo.VBO(
(ctypes.c_float * 16)(
-1, -1, 0, 0,
-1, 1, 0, 0,
1, 1, 0, 0,
1, -1, 0, 0
)
)
indices = vbo.VBO(
(ctypes.c_float * 6)(
0, 1, 2, 1, 2, 3
),
target=GL_ELEMENT_ARRAY_BUFFER
)
while (
glfw.get_key(window, glfw.KEY_ESCAPE) != glfw.PRESS
and not glfw.window_should_close(window)
):
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
with vertices, indices:
glVertexAttribPointer(0, 4, GL_FLOAT, False, 0, None)
glEnableVertexAttribArray(0)
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, None)
glfw.swap_buffers(window)
glfw.poll_events()
The glDrawElements
call fails with an entirely unhelpful "invalid operation" exception. When commented out, everything else seems to work fine (except that of course nothing is drawn).
What exactly is wrong here? I had understood that using the VBO
instances as context managers (the with
block) should ensure that the data is bound and thus should be available for glDrawElements
to render.
I have also tried the following, to no effect:
.bind()
calls on the VBOs rather than the with
blocksize
argument in the glDrawElements
call to every other number I could imagine being rightYou're using a core profile OpenGL context:
glfw.window_hint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)
In a core profile context you have to use a Vertex Array Object, because the default VAO (0) is not valid.
Either switch to a compatibility profile (glfw.OPENGL_COMPAT_PROFILE
) or create a Vertex Array Object. I recommend to create an VAO.
Anyway the buffers have to be bound after the VAO has been bound:
indices.bind()
vertices.bind()
And the type of the indices has to be integral and the type which is specified at glDrawElements
has to correspond to this type. For instance c_int16
and GL_UNSIGNED_SHORT
:
indices = vbo.VBO(
(ctypes.c_int16 * 6)(
0, 1, 2, 0, 2, 3
),
target=GL_ELEMENT_ARRAY_BUFFER
)
Note the Index buffers is stated in the VAO, thus the VAO has to be bound before the ELEMENT_ARRAY_BUFFER
is bound.
glVertexAttribPointer
associates, the currently bound ARRAY_BUFFER
, to the specified attribute index in the currently bound VAO. THus the VAO and the Vertex Buffer Object have to be bound before.
Example:
window = setup_window()
program = get_program()
glUseProgram(program)
vao = glGenVertexArrays(1)
glBindVertexArray(vao)
vertices = vbo.VBO(
(ctypes.c_float * 16)(
-1, -1, 0, 1,
-1, 1, 0, 1,
1, 1, 0, 1,
1, -1, 0, 1
)
)
indices = vbo.VBO(
(ctypes.c_int16 * 6)(
0, 1, 2, 0, 2, 3
),
target=GL_ELEMENT_ARRAY_BUFFER
)
indices.bind()
vertices.bind()
glVertexAttribPointer(0, 4, GL_FLOAT, False, 0, None)
glEnableVertexAttribArray(0)
while (
glfw.get_key(window, glfw.KEY_ESCAPE) != glfw.PRESS
and not glfw.window_should_close(window)
):
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, None)
glfw.swap_buffers(window)
glfw.poll_events()