为什么这个 glDrawElements 调用失败?



我正在尝试集成 https://gamedev.stackexchange.com/questions/10727/fastest-way-to-draw-quads-in-opengl-es 的建议以及如何在 PyOpenGL 中使用顶点数组对象和 glDrawElements 进行绘制,以使用OpenGL.arrays.vbo.VBO实现的索引缓冲区渲染四边形,在 GLFW 提供的上下文中并使用辅助类。 (至少目前,我也依赖ctypes获取原始数据,而不是使用 Numpy。

我制作了以下最小示例(如果我能解决这个问题,我应该能够修复实际代码(:

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()

glDrawElements调用失败,并出现完全无用的"无效操作"异常。当注释掉时,其他一切似乎都工作正常(当然除了什么都没有绘制(。

这里到底出了什么问题?我理解,使用VBO实例作为上下文管理器(with块(应该确保数据是绑定的,因此应该可供glDrawElements渲染。

我也尝试了以下方法,但没有效果:

  • 对 VBO 而不是with块使用显式.bind()调用
  • 使用空数据
  • glDrawElements调用中的size参数更改为我能想象到的所有其他数字

您正在使用核心配置文件 OpenGL 上下文:

glfw.window_hint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)

在核心配置文件上下文中,您必须使用顶点数组对象,因为默认的 VAO (0( 无效.
切换到兼容性配置文件 (glfw.OPENGL_COMPAT_PROFILE( 或创建顶点数组对象。我建议创建一个 VAO。

无论如何,缓冲区必须在绑定 VAO 后绑定

indices.bind()
vertices.bind()

索引的类型必须是积分的,在glDrawElements指定的类型必须与此类型相对应。例如c_int16GL_UNSIGNED_SHORT

indices = vbo.VBO(
(ctypes.c_int16 * 6)(
0, 1, 2, 0, 2, 3
),
target=GL_ELEMENT_ARRAY_BUFFER
)

请注意,索引缓冲区在 VAO 中声明,因此必须在绑定ELEMENT_ARRAY_BUFFER之前绑定 VAO.
glVertexAttribPointer将当前绑定的ARRAY_BUFFER关联到当前绑定的 VAO 中的指定属性索引。VAO 和顶点缓冲区对象必须绑定之前。

例:

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()

总结讨论的结果,以及我的测试:

  • VBO 类工作正常;with块工作正常。

  • 必须创建 VAO 才能在OPENGL_CORE_PROFILE中工作。我一直对如何使 VAO 与索引缓冲区一起工作感到困惑,但事实证明它并没有特别的东西 - 只需设置 VAO,其余代码是相同的。它可以像vao = glGenVertexArrays(1); glBindVertexArray(vao)一样简单。这需要在需要 VAO 的任何功能之前发生;在此代码中,这基本上意味着"在输入with块之前"。但是,当然,我也希望在while循环之外使用它,因为不断重新创建它是没有意义的(我也必须清理它(。

  • 我在为SSCCE制作数据时在几个地方搞砸了。索引缓冲区需要具有ctypes.c_int16类型的数据(它最初具有(;缓冲区中的实际索引是错误的(同样,按照gamedev.stackexchange示例,我最初将它们正确(;顶点的 W 坐标需要1.0而不是0.0(在我的实际代码库中,Z 和 W 分量由着色器填充,因为我尝试专门进行 2D 渲染,顶点数据将使用这些值作为纹理坐标(。

最新更新