Концепция функций OpenGL «Bind» - PullRequest
41 голосов
/ 18 марта 2012

Я изучаю OpenGL из этого урока .Мой вопрос касается спецификации в целом, а не конкретной функции или темы.Когда я вижу код, подобный следующему:

glGenBuffers(1, &positionBufferObject);
glBindBuffer(GL_ARRAY_BUFFER, positionBufferObject);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertexPositions), vertexPositions, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);

Я запутался в полезности вызова функций связывания до и после установки данных буфера.Мне кажется это излишним из-за моей неопытности в OpenGL и компьютерной графике в целом.

Страница руководства говорит, что:

glBindBuffer позволяет вам создавать или использовать именованный буферный объект.Вызов glBindBuffer с целевым объектом, установленным в GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER, GL_PIXEL_PACK_BUFFER или GL_PIXEL_UNPACK_BUFFER и буфером, установленным в имя нового объекта буфера, связывает имя объекта буфера с целью.Когда буферный объект привязан к цели, предыдущая привязка для этой цели автоматически нарушается.

Что такое концепция / утилита «связывания» чего-либо с «целью»?

Ответы [ 5 ]

29 голосов
/ 18 марта 2012

команды в opengl не существуют изолированно.они предполагают существование контекста.Один из способов думать об этом заключается в том, что в фоновом режиме есть объект opengl, и функции являются методами для этого объекта.

, поэтому при вызове функции то, что она делает, зависит от аргументов,конечно, но также и о внутреннем состоянии opengl - в контексте / объекте.

это очень ясно с bind, который говорит: «установите это как текущий X».затем более поздние функции изменяют «текущий X» (например, где X может быть буфером).[update:] и, как вы говорите, то, что устанавливается (атрибут в объекте или «элемент данных»), является первым аргументом для привязки.поэтому GL_ARRAY_BUFFER называет конкретную вещь, которую вы устанавливаете.

и для ответа на вторую часть вопроса - установка его в 0 просто очищает значение, чтобы вы случайно не внесли незапланированные изменения в другом месте.

25 голосов
/ 18 сентября 2013

Техника OpenGL может быть невероятно непрозрачной и запутанной. Я знаю! Я пишу 3D движки на основе OpenGL в течение многих лет (время от времени). В моем случае часть проблемы заключается в том, что я пишу движок, чтобы скрыть базовый 3D API (OpenGL), поэтому, когда я что-то получаю, я больше никогда не вижу код OpenGL.

Но вот одна из техник, которая помогает моему мозгу постичь «путь OpenGL». Я думаю, что этот способ мышления является правдой (но не всей историей).

Подумайте об аппаратной графике / видеокартах. У них есть определенные возможности, реализованные в оборудовании. Например, графический процессор может обновлять (записывать) только одну текстуру за раз. Тем не менее, обязательно, чтобы графический процессор содержал много текстур в оперативной памяти внутри графического процессора, поскольку передача между памятью процессора и памятью графического процессора очень медленная.

Итак, API OpenGL создает понятие «активной текстуры». Затем, когда мы вызываем функцию API OpenGL для копирования изображения в текстуру, мы должны сделать это следующим образом:

1:  generate a texture and assign its identifier to an unsigned integer variable.
2:  bind the texture to the GL_TEXTURE bind point (or some such bind point).
3:  specify the size and format of the texture bound to GL_TEXTURE target.
4:  copy some image we want on the texture to the GL_TEXTURE target.

И если мы хотим нарисовать изображение на другой текстуре, мы должны повторить тот же процесс.

Когда мы наконец готовы что-то отобразить на дисплее, нам нужен наш код, чтобы сделать одну или несколько текстур, которые мы создали, и скопировать изображения, чтобы они стали доступны нашему фрагментному шейдеру.

Как выясняется, фрагментный шейдер может получить доступ к более чем одной текстуре одновременно, используя несколько «текстурных блоков» (по одной текстуре на текстурный блок). Итак, наш код должен связать текстуры, которые мы хотим сделать доступными для текстурных блоков, к которым наши фрагментные шейдеры ожидают их привязки.

Итак, мы должны сделать что-то вроде этого:

glActiveTexture (GL_TEXTURE0);
glBindTexture (GL_TEXTURE_2D, mytexture0);

glActiveTexture (GL_TEXTURE1);
glBindTexture (GL_TEXTURE_2D, mytexture1);

glActiveTexture (GL_TEXTURE2);
glBindTexture (GL_TEXTURE_2D, mytexture2);

glActiveTexture (GL_TEXTURE3);
glBindTexture (GL_TEXTURE_2D, mytexture3);

Теперь я должен сказать, что я люблю OpenGL по многим причинам, но этот подход сводит меня с ума. Это потому, что все программное обеспечение, которое я писал годами, выглядело бы так:

error = glSetTexture (GL_TEXTURE0, GL_TEXTURE_2D, mytexture0);
error = glSetTexture (GL_TEXTURE1, GL_TEXTURE_2D, mytexture1);
error = glSetTexture (GL_TEXTURE2, GL_TEXTURE_2D, mytexture2);
error = glSetTexture (GL_TEXTURE3, GL_TEXTURE_2D, mytexture3);

БАМО. Нет необходимости устанавливать все это состояние снова и снова. Просто укажите, к какому блоку текстуры прикрепить текстуру, плюс тип текстуры, чтобы указать, как получить доступ к текстуре, а также идентификатор текстуры, которую я хочу прикрепить к блоку текстуры.

Мне также не нужно было бы привязывать текстуру как активную текстуру, чтобы скопировать на нее изображение, я бы просто дал идентификатор текстуры, на которую я хотел скопировать. Почему это должно быть связано?

Что ж, есть одна загвоздка, которая заставляет OpenGL структурироваться таким безумным образом. Поскольку аппаратные средства выполняют некоторые функции, а программный драйвер - другие функции, а также то, что делается в зависимости от переменной (зависит от карты GPU), им необходим какой-то способ держать сложность под контролем. По сути, их решение состоит в том, чтобы иметь только одну точку привязки для каждого типа сущности / объекта и требовать, чтобы мы связывали наши сущности с этими точками привязки, прежде чем мы вызовем функции, которые ими манипулируют. И в качестве второй цели, связывание сущностей - это то, что делает их доступными для GPU и наших различных шейдеров, которые выполняются в GPU.


По крайней мере, так я держу "путь OpenGL" прямо в моей голове. Честно говоря, если кто-то действительно, действительно, ДЕЙСТВИТЕЛЬНО понимает все причины, по которым OpenGL (и должен быть) структурирован таким, какой он есть, я бы хотел, чтобы он опубликовал свой собственный ответ. Я считаю, что это важный вопрос и тема, и обоснование редко, если вообще когда-либо описывается, и тем более не так, как мой маленький мозг может понять.

15 голосов
/ 18 марта 2012

Из раздела Введение: что такое OpenGL?

Сложные агрегаты, подобные структурам, никогда не отображаются напрямую в OpenGL.Любые такие конструкции скрыты за API.Это облегчает доступ к API OpenGL для языков, отличных от C, без сложного слоя преобразования.

В C ++, если вы хотите получить объект, который содержит целое число, число с плавающей точкой и строку, вы должны создатьи получить к нему доступ вот так:

struct Object
{
    int count;
    float opacity;
    char *name;
};

//Create the storage for the object.
Object newObject;

//Put data into the object.
newObject.count = 5;
newObject.opacity = 0.4f;
newObject.name = "Some String";

В OpenGL вы бы использовали API, который выглядит примерно так:

//Create the storage for the object
GLuint objectName;
glGenObject(1, &objectName);

//Put data into the object.
glBindObject(GL_MODIFY, objectName);
glObjectParameteri(GL_MODIFY, GL_OBJECT_COUNT, 5);
glObjectParameterf(GL_MODIFY, GL_OBJECT_OPACITY, 0.4f);
glObjectParameters(GL_MODIFY, GL_OBJECT_NAME, "Some String");

Ни одна из них, конечно, не является настоящими командами OpenGL.Это просто пример того, как будет выглядеть интерфейс к такому объекту.

OpenGL владеет хранилищем для всех объектов OpenGL.Из-за этого пользователь может получить доступ к объекту только по ссылке.Почти все объекты OpenGL обозначаются целым числом без знака (GLuint).Объекты создаются функцией вида glGen *, где * - тип объекта.Первый параметр - это количество создаваемых объектов, а второй - массив GLuint *, который получает вновь созданные имена объектов.

Чтобы изменить большинство объектов, они должны сначала быть привязаны к контексту.Многие объекты могут быть связаны с различными местами в контексте;это позволяет одному и тому же объекту использоваться по-разному.Эти разные места называются целями;все объекты имеют список допустимых целей, а некоторые имеют только одну.В приведенном выше примере фиктивная цель «GL_MODIFY» - это место, где привязано objectName.

Так работает большинство объектов OpenGL, а буферные объекты являются «большинством объектов OpenGL».

И если этого недостаточно, учебник описывает это снова в Глава 1: После данных :

void InitializeVertexBuffer()
{
    glGenBuffers(1, &positionBufferObject);

    glBindBuffer(GL_ARRAY_BUFFER, positionBufferObject);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertexPositions), vertexPositions, GL_STATIC_DRAW);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
}

Первая строка создаетобъект буфера, хранящий дескриптор объекта в глобальной переменной positionBufferObject.Хотя объект теперь существует, он еще не владеет памятью.Это связано с тем, что мы не выделяем ни одного объекта.

Функция glBindBuffer связывает вновь созданный буферный объект с целью связывания GL_ARRAY_BUFFER.Как упоминалось во введении, объекты в OpenGL обычно должны быть связаны с контекстом, чтобы они могли что-либо делать, и буферные объекты не являются исключением.

Функция glBufferData выполняет две операции.Он выделяет память для буфера, в настоящее время привязанного к GL_ARRAY_BUFFER, который мы только что создали и связали.У нас уже есть некоторые данные вершин;проблема в том, что это в нашей памяти, а не в памяти OpenGL.Sizeof (vertexPositions) использует компилятор C ++ для определения размера байта массива vertexPositions.Затем мы передаем этот размер в glBufferData как размер памяти, выделяемой для этого объекта буфера.Таким образом, мы выделяем достаточно памяти GPU для хранения наших данных вершин.

Другая операция, которую выполняет glBufferData, - это копирование данных из нашего массива памяти в буферный объект.Третий параметр контролирует это.Если это значение не равно NULL, как в этом случае, glBufferData скопирует данные, на которые ссылается указатель, в объект буфера.После этого вызова функции буферный объект хранит именно то, что хранит vertexPositions.

Четвертый параметр - это то, что мы рассмотрим в следующих уроках.

Второй вызов буфера связывания - просто очистка.Связывая буферный объект 0 с GL_ARRAY_BUFFER, мы заставляем буферный объект, ранее связанный с этой целью, стать от него свободным.Ноль в этом случае работает так же, как указатель NULL.Это не было строго необходимо, так как любое последующее связывание с этой целью просто отвяжет то, что уже есть.Но если у вас нет очень строгого контроля над рендерингом, обычно хорошей идеей будет открепить привязываемые объекты.

5 голосов
/ 18 марта 2012

Привязка буфера к цели - это что-то вроде установки глобальной переменной.Последующие вызовы функций затем работают с этими глобальными данными.В случае OpenGL все «глобальные переменные» вместе образуют контекст GL .Практически все функции GL читают из этого контекста или каким-то образом модифицируют его.

Вызов glGenBuffers() похож на malloc(), выделяя буфер;мы устанавливаем глобал так, чтобы он указывал на него glBindBuffer();мы вызываем функцию, которая работает с этим глобальным (glBufferData()), а затем устанавливаем глобальное значение на NULL, чтобы он не мог случайно работать с этим буфером снова, используя glBindBuffer().

2 голосов
/ 16 июня 2015

OpenGL - это то, что известно как «конечный автомат», для этого в OpenGL есть несколько «целей привязки», каждая из которых может иметь только одну вещь, связанную одновременно. Привязка чего-то другого заменяет текущую привязку и, таким образом, меняет ее состояние. Таким образом, связывая буферы, вы (пере) определяете состояние машины.

Как конечный автомат, любая информация, которую вы связали, будет влиять на следующий вывод машины, в OpenGL, который является ее следующим вызовом отрисовки. Как только это будет сделано, вы можете связать новые данные вершин, новые пиксельные данные, новые цели и т. Д., А затем инициировать еще один вызов отрисовки. Если вы хотите создать иллюзию движения на экране, когда вы будете удовлетворены тем, что нарисовали всю свою сцену (концепция 3D-движка, а не концепция OpenGL), вы бы перевернули кадровый буфер.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...