Как использовать объекты буфера вершин (VBO) вместо вызова glDrawArrays тысячи раз в OpenGL ES 1.0 на iOS?

Для моделирования мы создали представление OpenGL1.1 с сеткой из прямоугольников 32 x 48.

Мы рисуем эту сетку каждый раз, когда CADisplayLink вызывает нашу функцию рисования, и позиции вершин никогда не меняются. Единственное, что меняется от кадра к кадру, - это цвет вершины.

Это упрощенный пример того, как мы это делаем:

- (void)drawFrame {
    // draw grid
    for (int i = 0; i < numRectangles; i++) {
        // ... calculate CGPoint values for vertices ...

        GLshort vertices[ ] = {
            bottomLeft.x, bottomLeft.y,
            bottomRight.x, bottomRight.y,
            topLeft.x, topLeft.y,
            topRight.x, topRight.y
        };

        glVertexPointer(2, GL_SHORT, 0, vertices);           
        glColor4f(r, g, b, 1);

        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    }
}

Инструмент OpenGL рекомендовал использовать объекты буфера вершин (VBO) для повышения производительности.

Есть ли пример того, как настроить очень простое и простое использование объектов буфера вершин в случае, когда вершины не меняются от кадра к кадру?

Apple предоставляет пример Использование объектов буфера вершин для управления копированием данных вершин, но он неполный.

GLuint    vertexBuffer;
GLuint    indexBuffer;

void CreateVertexBuffers()
{

    glGenBuffers(1, &vertexBuffer);
    glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    glGenBuffers(1, &indexBuffer);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

}

Он не показывает, как на самом деле создавать данные. Предыдущий листинг (который должен быть «плохим примером») содержит эти две строки:

const vertexStruct vertices[] = {...};
const GLubyte indices[] = {...};

Итак, эти два массива или структуры должны быть переданы в:

    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

и

    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

?

Является ли iOS предпочтительным форматом «Interleaved (массив структур)», согласно Apple в разделе Use Interleaved Vertex Data section?


person dontWatchMyProfile    schedule 30.11.2011    source источник


Ответы (1)


Вы должны рисовать не отдельные примитивы с помощью glDrawArrays, а большие партии. Пока вы используете только обычные массивы вершин, а не объекты буфера вершин.

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

Цвет вершины можно поместить в массив вершин, а значит, и в VBO.

Обновлять

Скажем, у вас есть шестиугольник:

GLfloat vertices[2][] = {
    {0, 0},         // 0
    {1, 0},         // 1
    {0.5, 0.866},   // 2
    {-0.5, 0.866},  // 3
    {-1, 0},        // 4
    {0.5, -0.866},  // 5
    {-0.5, -0.866}, // 6
};

и вы хотите нарисовать только часть треугольников, скажем, треугольники, состоящие из вершин [0,1,2], [0,3,4] и [0,5,6], тогда вы должны создать следующий массив индексов

GLushort indices[] = {
    0, 1, 2,
    0, 3, 4,
    0, 5, 6
};

И используйте это как индексы для glDrawElements.

Обновление 2

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

  • должность
  • нормальный
  • координаты текстуры
  • цвет вершины

До ядра OpenGL-3 атрибут position был обязательным. Поскольку ядро ​​OpenGL-3 сделало шейдеры обязательными, атрибуты вершин - это просто произвольные входные данные в шейдеры, и пока вершинному шейдеру удается доставить выходные данные * gl_Position *, OpenGL доволен.

Важно то, что две вершины идентичны только тогда, если все атрибуты совпадают. Если они отличаются только одним атрибутом, это не одна и та же вершина. Теперь возьмем наш предыдущий пример шестиугольника. Теперь мы делаем треугольники красным, зеленым и синим и собирались добавить два треугольника, чтобы удлинить красный и зеленый в форме ромба:

// x, y, red, green, blue
GLfloat vertices[5][] = {
    // red
    {0, 0, 1, 0, 0},         // 0
    {1, 0, 1, 0, 0},         // 1
    {0.5, 0.866, 1, 0, 0},   // 2
    {1, 1, 1, 0, 0},         // 3

    // green
    {0, 0, 0, 1, 0},         // 4
    {-0.5, 0.866, 0, 1, 0},  // 5
    {-1, 0, 0, 1, 0},        // 6
    {-1, 1, 0, 1, 0},        // 7

    // blue
    {0, 0, 0, 0, 1},         // 8
    {0.5, -0.866, 0, 0, 1},  // 9
    {-0.5, -0.866, 0, 0, 1}, // 10
};

Теперь мы хотим нарисовать треугольники.

GLushort indices[] = {
    // the two red triangles
    0, 1, 2,
    3, 2, 1,

    // the two green triangles
    4, 5, 6,
    5, 7, 6,

    // the blue triangle
    8, 9, 10
};

Теперь нам нужно сообщить OpenGL о структуре нашего массива вершин. Здесь параметр stride функций gl… Pointer входит в изображение. Если ненулевое значение, шаг сообщает OpenGL расстояние (в байтах) между началом каждой вершины в массиве. Передавая указатель data с правильным смещением, OpenGL получает доступ к нужным вещам. В нашем случае вершина состоит из

  • 2 позиционных элемента GLfloat со смещением 0
  • 3 цветных элемента GLfloat со смещением 2 * sizeof (GLfloat)

и каждая вершина находится на расстоянии sizeof(GLfloat)*5 байтов друг от друга.

Мы позволим компилятору C выполнить вычисления смещения за нас, просто разыменовав правильные элементы массива и взяв его адрес:

glVertexPointer(2, GL_FLOAT, sizeof(GLfloat)*5, &vertices[0][0]);
glColorPointer(3, GL_FLOAT, sizeof(GLfloat)*5, &vertices[0][2]);

Остальное просто glDrawElements(GL_TRIANGLES, 5, GL_UNSIGNED_SHORT, indices).

Обратите внимание, что на данный момент мы не используем VBO, а только массивы вершин на стороне клиента. VBO основаны на массивах вершин. Поэтому я настоятельно рекомендую вам сначала хорошо изучить массивы вершин, прежде чем переходить к VBO. На самом деле они довольно просты в использовании, но есть несколько концептуальных ловушек, таких как обман компилятора для передачи числа в качестве параметра указателя.

person datenwolf    schedule 30.11.2011
comment
Как мне создать VBO для этих прямоугольников? А для чего нужны эти знаки? Как мне потом изменить цвет прямоугольника, когда он будет в VBO? - person dontWatchMyProfile; 30.11.2011
comment
@dontWatchMyProfile: Как я уже писал, вы можете частично обновить содержимое VBO. Индексы используются, если вы не хотите использовать сам массив вершин как последовательный список вершин, которые нужно нарисовать, а массив вершин, которые рисуются в соответствии с массивом индексов. - person datenwolf; 30.11.2011
comment
Спасибо datenwolf! Итак, в моем случае, когда я просто хочу нарисовать сетку из прямоугольников 32 x 48 (то есть 32 столбца и 48 строк), а затем изменять их цвет независимо только в последующих итерациях цикла выполнения, мне понадобится 32 VBO (один для каждый столбец, поэтому я могу легко использовать GL_TRIANGLE_STRIP) без необходимости в индексах, а затем мог бы каким-то образом установить цвет для каждой вершины? Или мне в этом случае понадобятся индексы? - person dontWatchMyProfile; 01.12.2011
comment
Вам не нужны 32 VBO. Вы можете просто начать с ненулевого смещения, используя glDrawArrays. Цвета будут дополнительным атрибутом вершины (см. GlColorPointer). И не используйте GL_TRIANGLE_STRIP, если вы хотите нарисовать много отключенных квадратов, просто используйте обычный ручной GL_TRIANGLES. Полосы треугольника предназначены для длинных полос не менее 100 вершин, чтобы быть эффективными. Черт возьми, в большинстве случаев простые индексированные треугольники легко превосходят полосы треугольников. - person datenwolf; 01.12.2011
comment
GL_TRIANGLES - отличный совет. У меня уже есть проблемы с тем, как я бы раскрасил квадраты независимо, когда они сделаны GL_TRIANGLE_STRIP. Как мне создать простую сетку из четырех прямоугольников, каждый из которых имеет свой сплошной цвет, с помощью GL_TRIANGLES? Возможно, вы могли бы обновить свой ответ, чтобы отразить это, так как это сделает его более ясным. - person dontWatchMyProfile; 01.12.2011