Vertex Buffer Objects
Введение
Преимущество VBO в том, что мы можем заставить OpenGL сохранять информацию, используемую для рисования – такую как позиция, цвет, координаты текстур и вектора нормали, непосредственно в памяти видеокарты и передавать напрямую каждый раз когда мы хотим отобразить. До сих пор это можно было сделать с помощью Display List, и по сравнению с ним, VBO имеет ряд преимуществ в получении указателя на данные в видеопамяти и в случае необходимости изменять эти данные. Это приводит к сильному повышению производительности для динамических массивов точек и на протяжении многих лет является лучшим решением для хранения статических и динамических массивов.
Как создать
Работа с VBO схожа с объектами текстурами, мы должны вначале создать/удалить указатель на объект, связать его с существующими данными или заполнить новыми данными. В этом уроке нам понадобиться два объекта. Первый это VBO содержащий подробную информацию о Вершинах (в нашем случае это координаты и цвет) и IBO ссылающийся на данные из VBO для создания из вершин треугольников. Преимущество заключается в том, что после загрузки VBO и IBO данными мы можем отобразить всю фигуру с помощью одного вызова GL.DrawElements().
Сначала мы создаем для Объекта для использования
uint[] VBOid = new uint[ 2 ];
GL.GenBuffers( 2, out VBOid );
Маловероятно, но возможно что OpenGL не хватит памяти для загрузки данных или то, что расширение не поддерживается. Это можно проверить вызовом GL.GetError(). Если всё прошло гладко то мы имеем два доступных для работы объекта.
Как удалить
OpenGL должен удалять все свои данные при удалении контекста воспроизведения. Хорошей идеей служит самостоятельное удаление данных после того как они больше не нужны. Мы удаляем созданные объекты путем вызова
GL.DeleteBuffers( 2, ref VBOid );
Как связать
Для того чтобы выбрать с каким объектом мы будем работать, достаточно связать с Version15.ArrayBuffer или Version15.ElementArrayBuffer. Первый используется для хранения положения, UV, нормалей и так далее (VBO), а второй для определения геометрии (IBO).
GL.BindBuffer( Version15.ArrayBuffer, VBOid[ 0 ] );
GL.BindBuffer( Version15.ElementArrayBuffer, VBOid[ 1 ] );
Совсем необязательно использовать два буфера, к примеру ты можешь использовать только VBO и хранить информацию о геометрии в системной памяти. Также, созданные два объекта не обязательно должны быть связаны вместе, ты можешь связать VBO с другим IBO.
Здесь есть две вещи о которых нельзя забывать
1) Во время работы с VBO GL.EnableClientState(EnableCap.VertexArray); должен быть задействован. Если используешь цвет GL.EnableClientState(EnableCap.ColorArray), просто как обычный Vertex Array.
2) Все относящиеся к Vertex Array команды должны использоваться для задействованных объектов, перед использованием необходимо указать на ноль для отключения аппаратного VBO
GL.BindBuffer( Version15.ArrayBuffer, 0 );
GL.BindBuffer( Version15.ElementArrayBuffer, 0 );
Заполнение данными
Есть несколько путей для заполнения объектов данными, в этом уроке мы сфокусируемся на GL.InterleavedArrays и прямой записью в память видеокарты.
1. GL.InterleavedArrays
Начнем с подготовки IBO, здесь нет разницы с чего начинать первыми. Для простоты сделаем то, что быстрее всего.
Мы должны удостоверитьтся в том что правильно связали объекты (этого не требуется если объекты уже были связаны. Здесь мы просто вносим ясность с каким объектом ведется работа)
GL.BindBuffer( Version15.ElementArrayBuffer, VBOid[ 1 ] );
В этом примере используется тип ushort для граней, потому максимальное значение для этого типа 65535 больше чем используемое нами Сторон. При использовании настоящих наборов это число может быть и больше и поэтому стоит задумать об использовании типа наподобие uint. В данном случае используя ushort мы экономим два байта на каждый индекс как если бы мы использовали тип UInt32.
У функции GL.BufferData первый параметр это назначение по которому мы хотим использовать, второй это количество памяти (в байтах) которое мы хотим использовать для хранения своих данных. Третий параметр это указатель на данные которые мы хотим отправить видеокарте, его тип должен быть IntPtr (целочисленный указатель). Здесь можно использовать IntPtr.Zero указатель на ноль если мы заполним данные на последующем этапе с помощью GL.MapBuffer (подробнее об этом позже). Последний параметр это подсказка драйверу видеокарте об оптимизации, это поможет расположить твои данные в наилучшем месте по твоему желанию. В таблице 1 приведен полный список опций.
GL.BufferData( Version15.ElementArrayBuffer, (IntPtr) ( Indices.Length * sizeof( ushort ) ), Indices, Version15.StaticDraw );
На этом всё, OpenGL скопирует доступные грани и мы можем освободить массив на который ссылаясь. Теперь у нас есть Массив Индексов который можно использовать позже.
Итак, мы сохранили индисы в IBO, теперь пора считать Вершины. Еще раз убедимся что связали всё правильно и окончим оптимизацией.
GL.BindBuffer( Version15.ArrayBuffer, VBOid[ 0 ] );
GL.BufferData( Version15.ArrayBuffer, (IntPtr) ( Vertices.Length * 6 * sizeof( float ) ), Vertices, Version15.StaticDraw );
Итак мы сохранили всю информацию о графике и перед вызовом GL.DrawElements должны кое что сделать. Вершины которые мы хотим использовать содержат не только координаты, но и цвета с которыми будут отображаться. GL.InterleavedArrays позволяет включить/выключить необходимое состояние клиента для правильной интерпретации наших загруженных данных OpenGL , первый параметр говорит о том что у нас есть 3 float для описания цвета (C3F) и 3 float для описания позиции (V3F). Второй параметр это шаг с которым должен быть совершен переход от первой вершины ко второй, третьей и так далее. Последний параметр должен быть указатель на Indeces, но мы уже загрузили его в видеопамять, поэтому второй раз указывать не нужно.
GL.InterleavedArrays( InterleavedArrayFormat.C3fV3f, 0, null );
Преимущество этой команды в том что она позволяет ясно объяснить OpenGL с какие данные нужно выводить и также это позволяет оптимизировать память. Запомни что GL.InterleavedArrays сработает только если включены EnableCap.VertexArray, EnableCap.ColorArray или изменялись GL.VertexPointer или GL.ColorPointer. Незабудь включить их обратно иначе ничего не произойдет.
Для более детального описания о состояниях Массива Вершин обращайся к RedBook или к описанию команд OpenGL.
- Printer-friendly version
- Login or register to post comments



