Урок 9. Индексация VBO


Принцип индексации

До текущего момента, когда мы строили наш VBO массив, мы повторяли каждую вершину по несколько раз, чтобы задать треугольники.
В данном уроке мы рассмотрим использование индексов при задании меша. Это позволяет использовать однажды заданную вершину для нескольких полигонов одновременно.

Совместно vs Раздельно

Давайте рассмотрим пример. На рисунке ниже художник, который сделал эти два треугольника, скорее всего, хотел, чтобы они представляли собой гладкую поверхность.

А вот тут, скорее всего, художник ожидал, что он получит острый угол. Но если мы объединим две нормали, то в результате шейдер интерполирует их, чтобы получить визуально гладкую поверхность:

В данном случае желательно иметь две отдельные нормали, по одной для каждой вершины. Единственный способ сделать это в OpenGL – полностью дуплицировать вершину со всем её набором атрибутов.

Индексированный VBO в OpenGL

Использовать индексацию очень просто. Для этого нам сначала создать дополнительный буфер, который мы заполним индексами вершин. Код остается тем же самым, но мы меняем ARRAY_BUFFER на ELEMENT_ARRAY_BUFFER.

std::vector<unsigned int> indices;
// заполняем индексы
// Создаем буфер для индексов
 GLuint elementbuffer;
 glGenBuffers(1, &elementbuffer);
 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementbuffer);
 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), &indices[0], GL_STATIC_DRAW);
А теперь при отрисовке меша нужно заменить glDrawArrays вот этим:
// Индексный буфер
 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementbuffer);
 // рисуем треугольники!
 glDrawElements(
     GL_TRIANGLES,      // режим
     indices.size(),    // количество
     GL_UNSIGNED_INT,   // тип
     (void*)0           // смещение в элементном буфере
 );

Заполняем индексный буфер

Теперь у нас есть проблема. Как я уже говорил раньше, OpenGL умеет использовать один индексный буфер, а наш загрузчик OBJ(и некоторые другие популярные форматы, например, Collada)используют отдельный индексный буфер для каждого атрибута. Так что нам нужно как-то преобразовывать из N индексных буферов в один индексный буфер.
Алгоритм для этого:
Для каждой входной вершины
    Пробуем найти такую же ( = такую же по всем атрибутам ) вершину среди уже обработанных нам
    Если нашли:
        Раз нашли, то значит, эта вершина уже есть в VBO, и можно использовать её!
    Если не нашли:
        Раз не нашли, то нужно её добавить в VBO.

Сам код программы можно найти в папке common\vboindexer.cpp. Он хорошо прокомментирован, так что если вы поняли алгоритм который я рассказал выше, то проблем быть не должно.
Критерий похожести – позиция вершины, UV координаты и нормали. Если у вас есть еще какие-то атрибуты, то можете просто добавить их в код.
Но в этом коде поиск похожих вершин выполняется простым линейным поиском по массиву, что медленно. В реальном приложении лучше использовать std::map.

Дополнительно: Счетчик FPS

Хотя это не относится прямо к индексации, сейчас очень хороший момент познакомиться со счетчиком FPS – количество кадров в секунду, так как с его помощью можно наглядно увидеть прирост скорости при любых оптимизациях.


Комментариев нет:

Отправить комментарий