opengl 为什么这个glDrawElements调用失败?

bksxznpy  于 2023-11-18  发布在  其他
关注(0)|答案(2)|浏览(147)

我试图整合https://gamedev.stackexchange.com/questions/10727/fastest-way-to-draw-quads-in-opengl-esHow to draw with Vertex Array Objects and glDrawElements in PyOpenGL的建议,使用OpenGL.arrays.vbo.VBO实现的索引缓冲区,在GLFW提供的上下文中并使用helper类来渲染quads。(我也依赖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调用失败,出现了一个完全无用的“invalid operation”异常。当注解掉时,其他一切似乎都很好(当然,除了没有绘制任何东西)。
这到底是怎么回事?我已经明白,使用VBO示例作为上下文管理器(with块)应该确保数据是绑定的,因此应该可供glDrawElements渲染。
我也试过以下方法,但没有效果:

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

mklgxw1f1#

您正在使用核心配置文件OpenGL context

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

gjmwrych

gjmwrych2#

总结一下讨论和我的测试结果:

  • VBO类工作正常; with块工作正常。
  • 必须创建一个 VAO 才能在OPENGL_CORE_PROFILE中工作。我一直对如何使VAO与索引缓冲区一起工作感到困惑,但事实证明这并没有什么特别的地方-只需设置 VAO ,其余的代码是相同的。它可以像vao = glGenVertexArrays(1); glBindVertexArray(vao)一样简单。这需要在任何需要 VAO 的函数之前发生;在这段代码中,这基本上意味着“在进入with块之前”。但是当然,我也希望它在while循环之外,因为没有必要不断地重新创建它(而且我还必须清理它)。
  • 在为SSCCE生成数据时,我在几个地方搞砸了。(它原来有);缓冲区中的实际索引是错误的(同样,我最初是正确的,遵循gamedev.stackexchange的例子);顶点的W坐标需要是1.0而不是0.0(在我的实际代码库中,Z和W组件由着色器填充,因为我试图只进行2D渲染,并且顶点数据将使用那些值作为纹理坐标)。

相关问题