Пакетная обработка данных произвольных вершин в OpenGL

Я делаю 2D рендерер в OpenGL, вдохновленный интерфейсом XNA / MonoGame, но у меня возникла небольшая проблема с дизайном, и я ищу какой-то вклад. В настоящее время вы можете отправлять данные вершин четырьмя основными способами:

void Render(const Sprite& sprite);
void Render(const Shape& shape);
void Render(const Vertex* vertices, unsigned int length);
void Render(const Vertex* vertices, unsigned int length, const Texture* texture);

Спрайт содержит четыре вершины, координаты цвета и текстуры, а остальные три могут содержать произвольное число (спрайт и форма имеют уникальные преобразования). Все может быть текстурированным или нетекстурированным. Я хочу пакетировать все, чтобы уменьшить количество изменений состояния и вызовов OpenGL Draw. Я считаю разумным предположить, что большинство представлений будут иметь общие вершины, поэтому я могу использовать glDrawElements вместо glDrawArrays, но у меня возникают проблемы с выяснением того, как правильно пакетировать вещи, учитывая то, что я описал выше.

Дозаторы спрайтов XNA / MonoGame работают, потому что они работают исключительно с текстурированными квадратами / треугольниками, а не произвольными формами. В качестве альтернативы, я мог бы сделать как рендерер SFML и выполнить вызов отрисовки для каждого рисуемого объекта, но это противоречит цели пакетного рендеринга.

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

Я спрашиваю: как я могу изменить дизайн моего рендерера? Могу ли я вести отдельные списки пакетов для разных представлений? Могу ли я как-нибудь модулировать мой рендерер? Должен ли я просто разрешить только текстурированные объекты, как в XNA / MonoGame?

4

Решение

Хорошо, поэтому нам нужно минимизировать количество изменений состояния и проводить звонки. Я предполагаю, что вы используете современный OpenGL, в том числе Объекты буфера вершин и шейдеры.

Одним из подходов является обеспечение того, чтобы все данные вершин имели одинаковый формат. Например, каждая вершина имеет координаты положения, цвета и текстуры (xyz, rgba, uv). Если мы чередуем наши данные вершин в VBO, нам нужен только один вызов glVertexAttribPointer а также glEnableVertexAttribArrayдо рендеринга.

Это означает некоторые избыточные данные для нетекстурированных объектов, но мы можем объединить все в один пакет, что приятно.

Чтобы обрабатывать нетекстурированные объекты, вы можете либо связать чистую белую текстуру и рассматривать ее как текстурированный объект. Или, вы можете иметь равномерную переменную (с плавающей точкой от 0 до 1) в вашем фрагментном шейдере и смешивать цвет текстуры с цветом вершины, используя mix функция.

Для пакетной обработки спрайтов и форм мы должны сначала обработать преобразования на процессоре, чтобы мы всегда загружали «мировые» координаты в графический процессор. Это избавляет нас от необходимости устанавливать униформу преобразования для каждого спрайта, для каждого из которых потребуются отдельные вызовы отрисовки.

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

Наш подход в основном сводится к следующему:

  • Поддерживать единый объект буфера вершин и индексов для хранения данных
  • Храните все данные вершин в одном формате и чередуйте данные в VBO
  • Сортировать по текстуре
  • Сбрасывайте данные (рисуйте элементы / массивы) в буферах всякий раз, когда мы меняем текстуру (или устанавливаем форму смешивания текстуры, если мы пойдем с этой опцией)

Получение данных из CPU в память GPU может быть выполнено различными способами. Например, сначала выделив достаточно большой, пустой буфер памяти на GPU, и используя glBufferSubData загружать подмножество данных вершин / индексов всякий раз, когда вы делаете один из ваших вызовов Render.

При выполнении такого рода работы очень важно выполнять профилирование. Например, для сравнения производительности между пакетными и отдельными вызовами отрисовки или glDrawArrays против glDrawElements. Я рекомендую использовать gDebugger, это бесплатный и очень хороший OpenGL профилировщик.

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

6

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]