Почему этот вызов glDrawElements не работает? - PullRequest
2 голосов
/ 26 мая 2020

Я пытаюсь интегрировать совет из https://gamedev.stackexchange.com/questions/10727/fastest-way-to-draw-quads-in-opengl-es и Как рисовать с помощью объектов массива вершин и glDrawElements в 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 для рендеринга.

Я также пробовал следующие, но не действуют:

  • с использованием явных .bind() вызовов VBO вместо with блока
  • с использованием пустых данных
  • изменение size аргумент в вызове glDrawElements любого другого номера, который я мог представить, был прав

Ответы [ 2 ]

2 голосов
/ 26 мая 2020

Подведем итоги обсуждения и моего тестирования:

  • Класс VBO работает нормально; блок with работает нормально.

  • VAO должен быть создан для работы в OPENGL_CORE_PROFILE. Я был сбит с толку относительно того, как заставить VAO работать с индексными буферами, но оказалось, что в этом нет ничего особенного - просто установите VAO, и остальной код останется таким же. Это может быть просто vao = glGenVertexArrays(1); glBindVertexArray(vao). Это должно произойти перед любой функцией, которая нуждается в VAO; в этом коде это по существу означает «до того, как будет введен блок with». Но, конечно, я хочу, чтобы он находился за пределами while l oop, так как нет смысла постоянно его воссоздавать (и мне бы также пришлось его очищать).

  • Я напортачил в нескольких местах при создании данных для SSCCE. В индексном буфере должны быть данные типа ctypes.c_int16 (которые были изначально); фактические индексы в буфере были неправильными (опять же, я исправил их изначально, следуя примеру gamedev.stackexchange); а координаты W вершин должны быть 1.0, а не 0.0 (в моей реальной кодовой базе компоненты Z и W заполняются шейдером, так как я пытаюсь выполнять исключительно 2D-рендеринг, а данные вершин будут использовать эти значения для координат текстуры).

2 голосов
/ 26 мая 2020

Вы используете основной профиль Контекст 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_int16 и GL_UNSIGNED_SHORT:

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

Обратите внимание, что Индексные буферы указаны в VAO, поэтому VAO должен быть привязан до привязки ELEMENT_ARRAY_BUFFER. 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()
...