Самый эффективный способ рисовать большое количество одинаковых объектов, но с разными преобразованиями

8

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

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

Конечно, это ужасно медленно, так как (я предполагаю) слишком много вызовов отрисовки для драйвера и моего ПК, чтобы эффективно обрабатывать.

Что я могу сделать, чтобы сделать это быстрее?

TravisG
источник
4
Я не могу дать вам полный ответ, но высокоуровневый ответ заключается в использовании геометрии .
Сет Бэттин
Поищите в Google «Геометрия», может быть, вы найдете что-то, что вам в этом поможет
Луис В.

Ответы:

11

Решением этого является создание экземпляров. Этот урок объясняет несколько методов создания экземпляров. Если у вас есть ARB_draw_instancedи ARB_instanced_arraysиспользуйте их.

Общая идея состоит в том, чтобы хранить все преобразования ваших сеток в отдельном объекте буфера и связать его с массивом атрибутов, который использует одну «вершину» на экземпляр через glVertexAttribDivisor.

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

Роберт Роухани
источник
1
+1; со многими тысячами объектов вы также можете стать привязанными к процессору при преобразованиях (в этом может помочь отправка позиции и вращения в буфере для каждого экземпляра и вычисление матрицы для объекта в графическом процессоре), и не забудьте посмотреть, как Вы обновляете этот динамический буфер вершин!
Максимус Минимус
@ Роберт Роухани: Спасибо, я исследовал и быстро реализовал это. Теперь я могу нарисовать 5000 ячеек с 20 трис в каждой при 250 FPS. Кстати, предложенный вами способ выглядит несколько устаревшим. Поскольку создание экземпляров геометрии OpenSL 3.something является официальной частью базовой спецификации OpenGL, ARB_xxx больше не требуется. Я использую единый буфер для хранения матриц и использую glDrawElementsInstanced. Внутри вершинного шейдера есть макет (std140) однородных матриц {mat4 array [5000]; } где я могу получить доступ к соответствующему элементу через массив [gl_InstanceID]
TravisG
1
@TravisG Я стараюсь поддерживать старые версии. Я понимаю, что оба эти расширения некоторое время были переведены в ядро, однако строка расширения все равно будет присутствовать :). Кроме того, использование glVertexAttribDivisor(которое не считается устаревшим или каким-либо образом устаревшим) имеет то преимущество, что не имеет верхнего предела количества экземпляров. Производительность исторически была лучше, чем единообразные буферы, но, вероятно, сейчас такая же или очень похожая, и оба способа справляются с работой.
Роберт Роухани
@ Роберт Роухани: Ммм ... Я думаю, это правда. Кто-то с устаревшими драйверами может поддерживать ARB_xxx, но не официальные основные функции OpenGl 3.x. Я посмотрю на glVertexAttribDivisor, спасибо.
TravisG
@ Роберт Рухани: Я реализовал обе версии (используя унифицированные буферы, и альтернативно вашу предложенную версию, где я использую атрибут вершины, который изменяется только один раз за экземпляр), и версия унифицированного буфера на самом деле примерно в два раза быстрее (4000 кадров в секунду в режиме выпуска, по сравнению с ~ 2000 с glVertexAttribDivisor, число 250 кадров в секунду было только для стороны процессора).
TravisG