Я не понимаю, какова цель связывания точек (таких как GL_ARRAY_BUFFER
) в OpenGL. Насколько я понимаю glGenBuffers()
создает своего рода указатель на объект буфера вершин, расположенный где-то в памяти GPU.
Так:
glGenBuffers(1, &bufferID)
означает, что теперь у меня есть дескриптор bufferID для 1 объекта вершины на видеокарте. Теперь я знаю, что следующим шагом будет привязка bufferID к точке привязки
glBindBuffer(GL_ARRAY_BUFFER, bufferID)
так что я могу использовать эту точку привязки для отправки данных вниз, используя glBufferData()
функционировать так:
glBufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_STATIC_DRAW)
Но почему я не могу просто использовать bufferID, чтобы указать, куда я хочу вместо этого отправлять данные? Что-то вроде:
glBufferData(bufferID, sizeof(data), data, GL_STATIC_DRAW)
Затем, при вызове функции рисования, я также просто вставляю какой ID в любой VBO, который я хочу, чтобы функция рисования рисовала. Что-то вроде:
glDrawArrays(bufferID, GL_TRIANGLES, 0, 3)
Зачем нам нужен дополнительный шаг косвенного обращения с glBindBuffers
?
OpenGL использует точки привязки объекта для двух целей: для обозначения объекта, который будет использоваться как часть процесса рендеринга, и для возможности изменения объекта.
Почему он использует их для первого, просто: OpenGL требует много объектов для визуализации.
Рассмотрим ваш чрезмерно упрощенный пример:
glDrawArrays(bufferID, GL_TRIANGLES, 0, 3)
Этот API не позволяет мне иметь отдельные атрибуты вершин из отдельных буферов. Конечно, вы можете предложить glDrawArrays(GLint count, GLuint *object_array, ...)
, Но как вы соединяете определенный буферный объект с определенным атрибутом вершины? Или как у вас есть 2 атрибута из буфера 0 и третий атрибут из буфера 1? Это то, что я могу сделать прямо сейчас с текущим API. Но ваш предложенный не может справиться с этим.
И даже это откладывает много другие объекты, которые вам нужно визуализировать: объекты программы / конвейера, объекты текстуры, UBO, SSBO, объекты обратной связи преобразования, объекты запросов и т. д. Наличие всех необходимых объектов, указанных в одной команде, было бы принципиально неработоспособным (и это оставляло бы в стороне производительность расходы).
И каждый раз, когда API нужно будет добавить новый тип объекта, вам придется добавлять новые варианты glDraw*
функции. И сейчас есть через дюжина такие функции. Твой путь дал бы нам сотни.
Таким образом, вместо этого OpenGL определяет способы для вас сказать «в следующий раз, когда я рендеринг, используйте этот объект таким образом для этого процесса». Вот что означает привязка объекта для использования.
Но почему я не могу просто использовать bufferID, чтобы указать, куда я хочу вместо этого отправлять данные?
Речь идет о привязке объекта с целью изменение объекта, не говорю, что это будет использовано. Это … другое дело.
Очевидный ответ: «Вы не можете сделать это, потому что API OpenGL (до 4.5) не имеет функции, позволяющей вам это сделать». Но я скорее подозреваю, что вопрос в том, почему OpenGL не имеет таких API (до 4.5, где glNamedBufferStorage
и такие существуют).
Действительно, тот факт, что 4.5 имеет такие функции, доказывает, что нет технический причина для pre-4.5 API привязки объекта к модификации OpenGL. Это действительно было «решение», которое появилось в результате эволюции OpenGL API от 1.0, благодаря тому, что мы пошли по пути наименьшего сопротивления. Несколько раз.
Действительно, почти каждое плохое решение, принятое OpenGL, может быть прослежено на пути наименьшего сопротивления в API. Но я отвлекся.
В OpenGL 1.0 был только один вид объектов: отображать объекты списка. Это означает, что даже текстуры не были сохранены в объектах. Поэтому каждый раз, когда вы переключали текстуры, вам приходилось переопределять всю текстуру с помощью glTexImage*D
, Это означает, что его нужно снова загрузить. Теперь вы можете (и это делали люди) обернуть создание каждой текстуры в список отображения, что позволило вам переключать текстуры, выполняя этот список отображения. И, надеюсь, водитель поймет, что вы делаете это, и вместо этого выделит видеопамять и так далее.
Поэтому, когда появился 1.1, ARB OpenGL понял, насколько это глупо. Таким образом, они создали объекты текстуры, которые инкапсулируют в памяти текстуру и различные состояния внутри. Когда вы хотели использовать текстуру, вы связали ее. Но была загвоздка. А именно как менять Это.
Видите ли, у 1.0 была куча уже существующих функций, таких как glTexImage*D
, glTexParamter
и тому подобное. Они изменяют состояние текстуры. Теперь ARB мог бы добавить новые функции, которые делают то же самое, но принимают объекты текстуры в качестве параметров.
Но это означало бы разделение всех пользователей OpenGL на 2 лагеря: тех, кто использовал объекты текстуры, и тех, кто этого не делал. Это означало, что, если вы хотите использовать текстурные объекты, вам придется переписать все вашего существующего кода, который изменил текстуры. Если бы у вас была какая-то функция, которая сделала кучу glTexParameter
вызывает текущую текстуру, вам нужно изменить эту функцию, чтобы вызвать новую функцию объекта текстуры. Но вы бы также Вы должны изменить свою функцию, которая ее вызывает, чтобы она принимала в качестве параметра объект текстуры, с которым она работает.
И если эта функция не принадлежит вам (потому что она была частью библиотеки, которую вы использовали), то вы даже не могли бы сделать тот.
Поэтому ARB решил сохранить эти старые функции и просто заставить их вести себя по-разному в зависимости от того, была ли текстура связана с контекстом или нет. Если кто-то был связан, то glTexParameter
/ etc изменяет связанную текстуру, а не обычную текстуру контекста.
Это одно решение установленный общая парадигма, разделяемая почти всеми объектами OpenGL.
ARB_vertex_buffer_object использовал эту парадигму по той же причине. Обратите внимание, как различные gl*Pointer
функции (glVertexAttribPointer
и тому подобное) работают по отношению к буферам. Вы должны привязать буфер к GL_ARRAY_BUFFER
, затем вызовите одну из этих функций для настройки массива атрибутов. Когда буфер связан с этим слотом, функция подберет его и обработает указатель как смещение в буфере, который был связан во время *Pointer
функция была вызвана.
Зачем? По той же причине: простота совместимости (или продвижение лени, в зависимости от того, как вы хотите это видеть). ATI_vertex_array_object должен был создать новые аналоги gl*Pointer
функции. Принимая во внимание, что ARB_vertex_buffer_object просто совмещал существующие точки входа.
Пользователи не должны были отказаться от использования glVertexPointer
в glVertexBufferOffset
или какая-то другая функция. Все, что им нужно было сделать, — это связать буфер перед вызовом функции, которая устанавливает информацию о вершине (и, конечно, изменить указатели на смещения байтов).
Это также означает, что им не нужно было добавлять кучу glDrawElementsWithBuffer
-тип-функции для рендеринга с индексами, которые берутся из буферных объектов.
Так что это не плохая идея в краткосрочной перспективе. Но, как и в случае большинства краткосрочных решений, со временем оно становится менее разумным.
Конечно, если у вас есть доступ к GL 4.5 / ARB_direct_state_access, вы можете делать вещи так, как следовало бы сделать изначально.
Других решений пока нет …