|
Главная
Статьи
Ссылки
Скачать
Скриншоты
Юмор
Почитать
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.