Reputation: 61617
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 rightUpvotes: 2
Views: 876
Reputation: 211135
You'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()
Upvotes: 4
Reputation: 61617
To summarize the results from the discussion, and my testing:
The VBO class works fine; the with
block works fine.
A VAO must be created to work in the OPENGL_CORE_PROFILE. I had been confused as to how to make VAOs work with index buffers, but it turns out there isn't particularly anything to it - just set up the VAO and the rest of the code is the same. It can be as simple as vao = glGenVertexArrays(1); glBindVertexArray(vao)
. This needs to happen before any function that needs the VAO; in this code, that essentially means "before the with
block is entered". But of course, I also want it outside the while
loop, since there is no point in re-creating it constantly (and I would also have to clean it up).
I had messed up in several places in producing the data for the SSCCE. The index buffer needs to have data of ctypes.c_int16
type (which it had originally); the actual indices in the buffer were wrong (again, I had them correct originally, following the gamedev.stackexchange example); and the W coordinates of the vertices need to be 1.0
rather than 0.0
(in my actual code base, the Z and W components are filled in by the shader, since I'm trying to do exclusively 2D rendering, and the vertex data will use those values for texture coordinates).
Upvotes: 2