【问题标题】:Why is this glDrawElements call failing?为什么这个 glDrawElements 调用失败?
【发布时间】:2020-09-13 17:06:10
【问题描述】:

我正在尝试整合来自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 提供的上下文中并使用辅助类来呈现四边形。 (我也是,至少目前,依靠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 上使用显式 .bind() 调用而不是 with
  • 使用空数据
  • glDrawElements 调用中的size 参数更改为我能想象到的所有其他数字

【问题讨论】:

    标签: python glfw pyopengl


    【解决方案1】:

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

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

    在核心配置文件上下文中,您必须使用Vertex Array Object,因为默认的 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
    )
    

    注意Index buffers是在VAO中声明的,因此必须在绑定ELEMENT_ARRAY_BUFFER之前绑定VAO。
    glVertexAttribPointer将当前绑定的ARRAY_BUFFER关联到指定的属性索引当前绑定的 VAO。因此必须先绑定 VAO 和 Vertex Buffer Object

    例子:

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

    【讨论】:

    • 这解决了异常,但我仍然没有输出(就像最初抱怨的其他 StackOverflow 海报一样)。我希望窗口用绿色填充,因为三角形覆盖了整个坐标空间,并且颜色在片段着色器中指定。
    • 还有其他想法吗?我复制并粘贴在原始setup_windowget_program 定义下方,但仍然只有一个黑色窗口。而且我之前用其他方法(不涉及索引缓冲区)成功地绘制了东西。 ://
    • 我尝试了另一种方法,将glfw.OPENGL_CORE_PROFILE 更改为glfw.OPENGL_COMPAT_PROFILE,而 also 并没有给我任何渲染输出。
    • @KarlKnechtel 索引必须是 0、1、2、0、2、3
    • 啊哈!就是这样(将 W 坐标设置为 1)- 实际上,我遇到了像以前一样的问题。在实际代码中,我将使用 ZW 坐标作为纹理,并由着色器正确设置它们。我第一次为索引数组设置了正确的索引值和类型,但在生成 SSCCE 时却搞砸了。鸡蛋在我脸上!感谢您的所有帮助。
    【解决方案2】:

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

    • 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 渲染,并且顶点数据将使用这些值作为纹理坐标)。

    【讨论】:

      猜你喜欢
      • 2012-03-14
      • 2014-11-13
      • 1970-01-01
      • 1970-01-01
      • 2016-08-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多