steps3D - Tutorials - Расширение ARB_vertex_attrib_binding

Расширение ARB_vertex_attrib_binding

При рендеринге геометрии вершинные атрибуты обычно берутся из различных буферов (VBO, vertex buffer object). При этом для каждого атрибута задается довольно большое количество разнообразной информации. Если мы хотим изменить значение атрибута, то зачастую всю эту информацию приходится задавать еще раз, хотя большая ее часть при этом вообще не изменялась.

Расширение ARB_vertex_attribute_binding разделяет всю информацию о вершинах на два независимых набора:

Для задания формата данных для конкретного атрибута вершины данное расширение вводят следующие функции:

void glVertexAttribFormat ( GLuint attribIndex,
                            GLint size, GLenum type,
                            GLboolean normalized,
                            GLuint relativeOffset );
void glVertexAttribIFormat ( GLuint attribIndex,
                            GLint size, GLenum type,
                            GLuint relativeOffset );
void glVertexAttribLFormat ( GLuint attribIndex,
                            GLint size, GLenum type,
                            GLuint attribOffset );

Здесь параметр attribIndex идентифицирует конкретный атрибут. Параметр type задает типа данных, используемых для инициализации одной компоненты, параметр size задает число компонент для заданного а атрибута. В следующей таблице приведены допустимые сочетания параметров type и size.

Команда size и порядок компонент Обработка целых чисел Тип
glVertexAttrib 1, 2, 3, 4, GL_BGRA В зависимости от значения flag GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT, GL_UNSIGNED_SHORT, GL_INT, GL_UNSIGNED_INT, GL_FIXED, GL_FLOAT, GL_HALF, GL_INT_2_10_10_10_REV, GL_UNSIGNED_INT_2_10_10_10_REV, GL_UNSIGNED_INT_10F_11F_11F_REV
glVertexAttribIFormat 1, 2, 3, 4 Только целые числа GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT, GL_UNSIGNED_SHORT, GL_INT, GL_UNSIGNED_INT
glVertexAttribLFormat 1, 2, 3, 4 нет GL_DOUBLE

Если исходные данные в буфере являются целочисленными, то при использовании команды glVertexAttribFormat они будут переведены в тип с плавающей точкой. При использовании команды glVertexAttribIFormat они будут переданы в шейдер как значения целого типа.

Для передачи в шейдер 64-битовых значений с плавающей точкой (double) используется функция glVertexAttribLFormat.

Флаг normalized используется для обозначения того, что целое число при передаче в шейдер должно быть нормализовано (т.е. приведено к диапазону [0,1] или [-1,1]), для типов с плавающей точкой он не играет никакого значения.

Для привязки вершинного буфера с заданием точки привязки используется следующая команда:

void glBindVertexBuffer ( GLuint bindingIndex,
                          GLuint buffer, GLintptr offset,
                          GLsizei stride );

При этом часть буфера с идентификатором buffer привязывается к точке привязки bindingIndex начиная со смещения offset. При этом параметр stride задает расстояние в байтах между двумя последовательными вершинами.

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

void glVertexAttribBinding ( GLuint attribIndex, GLuint bindingIndex );

Для задания делителя, т.е. шага с которым происходит смена значений атрибута служит команда glVertexBindingDivisor.

void glVertexBindingDivisor ( GLuint bindingIndex, GLuint divisor );

Если параметр divisor равен 0, то каждому обращению к вершине будет соответствовать новое значение атрибута (это поведение по умолчанию). Если параметр divisor не равен 0, то на каждые divisor обращений к вершине будет происходить один переход к следующему значению.

Ниже приводится код, служащий для задания атрибутов объекта - тора, координаты которого вычисляются в ходе выполнения. При этом мы считаем, что сам объект задается NUM_VERTICES вершин, каждая из которых имеет размер VERTEX_SIZE байт и вначале идут три координаты вершины, задаваемые при помощи числе типа float.

glGenVertexArrays ( 1, &vao );
glBindVertexArray ( vao );
glGenBuffers      ( 1, &vbo );
glBindBuffer      ( GL_ARRAY_BUFFER, vbo );
glBufferData      ( GL_ARRAY_BUFFER, NUM_VERTICES * VERTEX_SIZE, vertices, GL_STATIC_DRAW );

int loc = glGetAttribLocation ( program, "position" );

if ( loc < 0 )
    exit ( 1 );
            
glVertexAttribFormat      ( loc, 3, GL_FLOAT, GL_FALSE, 0 );     // set attribute layout
glBindVertexBuffer        ( 0, vbo, 0, VERTEX_SIZE );
glVertexAttribBinding     ( loc, 0 );
glEnableVertexAttribArray ( loc );

При использовании OpenGL 4.5 вводятся DSA (Direct-State Access) аналоги рассмотренных ранее функций.

void glVertexArrayBindVertexBuffer     ( GLuint vao, GLuint bindingIndex, GLuint buffer,
                                   GLintptr offset, GLsiziei stride );
void glVertexArrayVertexAttribFormat   ( GLuint vao, GLuint attribIndex, GLint size, GLenum type,
                                   GLboolean normalized, GLuint relativeOffset );
void glVertexArrayVertexAttribIFormat  ( GLuint vao, GLuint attribIndex, GLint size, GLenum type,
                                   GLuint relativeOffset );
void glVertexArrayVertexAttribLFormat  ( GLuint vao, GLuint attribIndex, GLint size, GLenum type,
                                   GLboolean normalized, GLuint relativeOffset );
void glVertexArrayVertexAttribBinding  ( GLuint vao, GLuint attribIndex, GLuint bindingIndex );
void glVertexArrayVertexBindingDivisor ( GLuint vao, GLuint bindingIndex, GLuint divisor );

Пример выше можно переписать с использованием описанных выше функций следующим образом:

glVertexArrayVertexAttribFormat  ( vao, loc, 3, GL_FLOAT, GL_FALSE, 0 );     // set attribute layout
glVertexArrayBindVertexBuffer    ( vao, 0, vbo, 0, VERTEX_SIZE );
glVertexArrayVertexAttribBinding ( vao, loc, 0 );
glEnableVertexAttribArray        ( loc );

Здесь через vbo, как и ранее, обозначен вершинный буфер (точнее его идентификатор), а через vao используемый объект Vertecx Array Object (VAO).