Главная Статьи Ссылки Скачать Скриншоты Юмор Почитать Tools Проекты Обо мне Гостевая Форум |
Использование различных вершинных массивов (или VBO) позволяет легко перенести большой объем различных атрибутов вершин (таких как координаты, нормаль/бинормаль, цвет, текстурные координаты и т.п.) непосредственно на GPU и быстро осуществлять рендеринг данных.
Однако чтобы использовать вершинные буфера необходимо осуществить соответствующую настройку и при большом числе вершинных атрибутов эта настройка занимает заметную часть кода и ее необходимо осуществлять перед каждым рендерингом. Ниже приводится фрагмент кода, осуществляющего настройку и рендеринг набора треугольников.
void Torus :: draw () { int vertexStride = sizeof ( Vertex ); int texOffs = ((int)&vertices [0].tex) - ((int)&vertices [0]); // сохранить состояние и разрешить использование массивов glPushClientAttrib ( GL_CLIENT_VERTEX_ARRAY_BIT ); glEnableClientState ( GL_VERTEX_ARRAY ); glEnableClientState ( GL_TEXTURE_COORD_ARRAY ); // задать источники данных для каждого из атрибутов вершины glBindBufferARB ( GL_ARRAY_BUFFER_ARB, vertexId ); glVertexPointer ( 3, GL_FLOAT, vertexStride, (void *)0 ); glTexCoordPointer ( 2, GL_FLOAT, vertexStride, (void *)texOffs ); glBindBufferARB ( GL_ELEMENT_ARRAY_BUFFER_ARB, indexId ); glDrawElements ( GL_TRIANGLES, 3*numFaces, GL_UNSIGNED_INT, 0 ); glBindBufferARB ( GL_ELEMENT_ARRAY_BUFFER_ARB, 0 ); glPopClientAttrib (); }
Обратите внимание, что объем кода для настройки и восстановления всего двух атрибутов (координат вершины и ее текстурных координат) во много раз превосходит код для непосредственного рендеринга данных (одна команда glDrawElements).
Расширение ARB_vertex_array_object вводит новый тип объекта OpenGL - Vertex Array Object, сохраняющего в себе настройки (состояние OpenGL, относящееся к вершинным массивам) для используемых вершинных массивов. Досточно один раз задать все эти настройки, сохранив их в соответствующем VAO. После этого для рендеринга достаточно лишь "активировать" (bind) соответсвтующий VAO, чтобы все эти настройки вступили в силу. Также легко восстановить исходное состояние - достаточно выбрать другой VAO.
Каждый VAO, как и многие другие объекты OpenGL, идентифицируется при помощи беззнаковых целых чисел (GLuint). Идентификатор 0 зарезервирован. Для создания и уничтожения VAO служат следующие функции:
void glGenVertexArrays ( GLsizei n, GLuint * arrays ); void glDeleteVertexArrays ( GLsizei n, GLuint * arrays );
Первая из них создает сразу n VAO и помещает их идентификаторы в массив arrays, вторая функция - уничтожает n VAO из массива arrays.
Для проверки того, является ли заданное число идентификатором какого-либо VAO можно при помощи следующей функции:
GLboolean glIsVertexArray ( GLuint array );
Функция glBindVertexArray "выбирает" (делает текущим) VAO с заданным идентификатором. После этого все команды, задающие параметры вершинныых буферов, изменяют состояние хранимое в данном VAO. Кроме того именно данный VAO и определяет текущие настройки вершинных буферов.
void glBindVertexArray ( GLuint array )
Для того, чтобы получить идентификтор активного в данный момент VAO служит следующий фрагмент кода:
GLuint curArray; glGetIntegerv ( GL_VERTEX_ARRAY_BINDING, (int *)&curArray );
Обратите внимание, что есть определенные ограничения - нельзя "резделять" VAO между несколькими контекстами и единственным способом изменения VBO, ссылка на который хранится в VAO, является команда glBufferData.
Для удобства работы завернем VAO в класс на языке С++:
#ifndef __VERTEX_ARRAY_OBJECT__ #define __VERTEX_ARRAY_OBJECT__ #include "libExt.h" class VertexArray { GLuint id; public: VertexArray () { glGenVertexArrays ( 1, &id ); } ~VertexArray () { glDeleteVertexArrays ( 1, &id ); } bool isOk () const { return glIsVertexArray ( id ); } void bind () { glBindVertexArray ( id ); } void unbind () { glBindVertexArray ( 0 ); } }; #endif
С использованием данного класса можно изменить фрагмент кода, приведенного в начале статьи. Один раз, сразу после создания вершинных буферов, необходимо создать VAO, и записать в него всю требуемую настройку:
int vertexStride = sizeof ( Vertex ); int texOffs = ((int)&vertices [0].tex) - ((int)&vertices [0]); vao = new VertexArray (); vao -> bind (); glPushClientAttrib ( GL_CLIENT_VERTEX_ARRAY_BIT ); glEnableClientState ( GL_VERTEX_ARRAY ); glEnableClientState ( GL_TEXTURE_COORD_ARRAY ); glBindBufferARB ( GL_ARRAY_BUFFER_ARB, vertexId ); glVertexPointer ( 3, GL_FLOAT, vertexStride, (void *)0 ); glTexCoordPointer ( 2, GL_FLOAT, vertexStride, (void *)texOffs ); glBindBufferARB ( GL_ELEMENT_ARRAY_BUFFER_ARB, indexId ); vao -> unbind ();
После этого код для рендеринга станет выглядеть следующим образом:
vao -> bind (); glDrawElements ( GL_TRIANGLES, 3*numFaces, GL_UNSIGNED_INT, 0 ); vao -> unbind ();
Ряд примеров на использование VAO можно найти на nopper.tv/opengl.html.
По этой ссылке можно скачать весь исходный код к этой статье.
Также доступны для скачивания откомпилированные версии для M$ Windows и Linux.
Обратите внимание, что для использование кода из этой статьи потребуется порследняя версия библиотеки libExt.