Вопрос или проблема
Я пытаюсь создать приложение с использованием Qt6 и QOpenGLWidget. Я всегда понимал концепцию VAO и EBO как загрузку данных в память GPU, чтобы эти данные не приходилось передавать каждый раз (исправьте меня, если я не прав). Далее мы используем glDrawElements, в котором указываем количество вершин и смещение в EBO, но по какой-то причине это не работает в Qt.
В теории это должно выглядеть следующим образом:
drawer->glGenVertexArrays(1, &vao);
drawer->glGenBuffers(1, &vbo);
drawer->glGenBuffers(1, &ebo);
drawer->glBindVertexArray(vao);
float * vertices = new float[9]{-0.5f, -0.5f, 0.0f, 0.5f, -0.5f, 0.0f, 0.0f, 0.5f, 0.0f};
indices = new unsigned int[3]{0,1,2};
drawer->glBindBuffer(GL_ARRAY_BUFFER, vbo);
drawer->glBufferData(GL_ARRAY_BUFFER, 9*sizeof(float), vertices, GL_STATIC_DRAW);
drawer->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
drawer->glBufferData(GL_ELEMENT_ARRAY_BUFFER, 3 * sizeof(unsigned int), indices, GL_STATIC_DRAW);
drawer->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
drawer->glEnableVertexAttribArray(0);
//РИСУЕМ
drawer->glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, 0);
Но это не работает в Qt, потому что, как я выяснил, изменив свой код, по какой-то причине glDrawElements здесь принимает указатель на данные в качестве последнего аргумента, что очень странно, потому что последний аргумент должен содержать смещение в EBO, так как сами данные уже загружены. Поэтому эти строки здесь совершенно бессмысленны:
drawer->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
drawer->glBufferData(GL_ELEMENT_ARRAY_BUFFER, 3 * sizeof(unsigned int), indices, GL_STATIC_DRAW);
glDrawElements работает только так (не имеет значения были ли закомментированы две строки, упомянутые выше):
drawer->glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, indices);
Существует ли способ заставить glDrawElements принимать смещение в качестве последнего аргумента, а не указатель на индексы?
Ответ или решение
Вопрос, с которым вы столкнулись в вашем приложении на Qt6, касается использования функции glDrawElements
, а именно почему она, похоже, не использует данные из элемента массива (EBO) так, как ожидалось. Давайте разберем это подробнее.
Понимание VAO и EBO
Вы совершенно правы, подразумевая, что Vertex Array Object (VAO) и Element Buffer Object (EBO) предназначены для загрузки данных в память GPU, чтобы минимизировать количество передаваемых данных за каждую итерацию рендеринга. При правильно настроенных VAO и EBO вызов glDrawElements
должен использовать индексы, находящиеся в EBO, а не требовать указателя на данные индексов.
Стандартное использование glDrawElements
Обычно, при вызове glDrawElements
в OpenGL, последний параметр определяет смещение в данных индексов, если они хранятся в EBO. Однако, если вы указываете указатель на данные индексов, OpenGL интерпретирует это как загрузку индексов непосредственно из заданного указателя в памяти.
Ваш код:
drawer->glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, indices);
вызывает типичное поведение, когда вы передаете адрес, где находятся ваши индексы. Это работает, но не соответствует стандартному использованию EBO.
Почему это может происходить в Qt6
-
Контекст OpenGL: Возможно, что контекст OpenGL, используемый в Qt6, настроен таким образом, что не позволяет оптимально использовать EBO. Убедитесь, что вы используете совместимую версию OpenGL с поддержкой VAO и EBO.
-
Драйверы / Платформа: Различные драйверы графики могут интерпретировать вызовы OpenGL по-разному. Это может вызвать неожиданные результаты. Убедитесь, что вы используете последние версии драйверов для вашей графической карты.
-
Ошибки при связывании: Убедитесь, что в вашем коде нет ошибок в связывании VAO и EBO перед рендерингом. Например, проверьте, что:
- Вы вызываете
glBindVertexArray(vao)
передglDrawElements
. - Вы вызываете
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
передglDrawElements
.
- Вы вызываете
Как использовать glDrawElements правильно
Если вы хотите, чтобы glDrawElements
использовал информацию из EBO, ваш вызов должен выглядеть следующим образом:
// Обязательная установка
drawer->glBindVertexArray(vao);
drawer->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
// Рендеринг с использованием индексов из EBO
drawer->glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, (void*)0);
Заключение
Если после выполнения всех проверок у вас все еще не работает правильно, рассмотрите возможность создания минимального воспроизводимого примера, чтобы процесс отладки был более эффективным. Сообщение об ошибках и их контекст могут также помочь в нахождении решения. Возможно, другие разработчики и эксперты в OpenGL смогут предоставить вам необходимую помощь в сообществе, если ваш вопрос будет должным образом переформулирован и уточнен.
Если вам нужно больше разъяснений или дополнительных примером кода, не стесняйтесь задавать вопросы.