Рендеринг экземпляров произвольного размера из одного VBO в OpenGL

Рассмотрим один VBO (или, возможно, несколько VBO), заполненный несколькими объектами, каждый из которых имеет произвольное количество вершин. Для наглядности предположим, что в VBO хранится яблоко с 500 вершинами и апельсин с 650 вершинами.

Обычно, если я хотел нарисовать апельсин, я просто вызывал glDrawRangeElements и указывал, что хочу нарисовать элементы 500-1150. Если бы я хотел, чтобы апельсин был нарисован в определенном месте в пространстве, я бы использовал преобразование модели.

Однако что, если я хочу нарисовать потенциально тысячи объектов? Кроме того, что, если VBO содержит сотни, если не тысячи объектов в качестве своего рода «библиотеки объектов»? Я бы предпочел не передавать то, что я хочу отрисовать, на мой графический процессор, используя немедленный режим (то есть вызывая glDrawRangeElements вручную для каждого объекта). Это добавит много дорогостоящих накладных расходов на связь с графическим процессором.

То, что я ищу, - это какой-то способ загрузить какой-то массив с OpenGL за один раз. Этот массив будет содержать какой-то идентификатор для каждого объекта, который я хочу нарисовать, и, возможно, преобразование для каждого (положение, поворот). Таким образом, если бы я хотел нарисовать море разных фруктов в произвольных местах, я бы просто один раз вычислил индекс и загрузил его. Массив можно чередовать. Например, массив может выглядеть так: [идентификатор объекта 1], [позиция 1], [поворот 1] ... [ идентификатор объекта n].


person NickB    schedule 21.06.2012    source источник
comment
используя немедленный режим (то есть вызов glDrawRangeElements вручную для каждого объекта). Это не немедленный режим. Немедленный режим glBegin/glEnd.   -  person Nicol Bolas    schedule 21.06.2012
comment
А, спасибо. Я все еще ржавый на терминологии.   -  person NickB    schedule 21.06.2012


Ответы (2)


В конечном счете, то, что вы пытаетесь сделать (нарисовать множество совершенно разных объектов), не будет работать ни с одним из «экземпляров».

Создание экземпляров предназначено для объектов, которые одинаковы. То есть повторяющиеся объекты, которые нарисованы в разных местах. То, что вы делаете, это просто рисование множества разных объектов. И для этого вам придется выполнить несколько вызовов отрисовки. Этого не избежать.

Лучшее, что вы можете сделать, это свести к минимуму изменения состояния. Используйте тот же шейдер для визуализации объектов. Используйте атлас текстур, возможно, в массиве текстур, чтобы вам не приходилось менять текстуры между объектами. Не используйте glVertexAttribPointer (больше, чем начальная настройка) и не изменяйте буфер элементов.

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

В конечном счете, вам нужно только изменить положение объекта в пространстве модели между объектами. У вас есть несколько вариантов решения этой проблемы.

Проще всего просто использовать glUniform для изменения матрицы модели-камеры для каждого объекта, как обычно. Вы уже должны знать, как это сделать.

Немного сложнее использовать универсальный буфер для хранения всех ваших матриц связи модели с камерой для все объекты, которые вы хотите нарисовать. Между объектами вы используете glBindBufferRange для привязки конкретной матрицы для этого объекта. Нет информации о том, будет ли это быстрее или медленнее, чем предыдущий метод. Обратите внимание, что реализации имеют зависимое от реализации выравнивание для диапазонов юниформ-буферов.

Теперь, технически, аппаратное обеспечение GL 4.x позволяет вам создавать некоторые данные в буферном объекте, который представляет команду рендеринга, а затем рендерить с ним. Это ядро ​​версии 4.0, которое называется косвенным рендерингом. Общая идея для этого заключалась в том, что OpenCL или какой-либо специализированный шейдерный процесс GL будет записывать данные в буферный объект, а клиентский код должен будет только сказать «рендерить то, что он сказал», вместо того, чтобы считывать данные и вручную сделать glDrawElements вызов.

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

person Nicol Bolas    schedule 21.06.2012
comment
Николай, я ценю исчерпывающий ответ и дополнительные советы. У меня было подозрение, что то, что я хотел сделать, невозможно. Тем не менее, я собираюсь подробнее изучить косвенный рендеринг, о котором вы упомянули. Большое спасибо! - person NickB; 21.06.2012

Похоже, вам нужно создание экземпляров.

Загляните в ARB_instanced_arrays/ARB_draw_instanced.

Вот сравнение различных методов.

Если вы используете конвейер с фиксированной функцией, вы SOL, хотя :(

person genpfault    schedule 21.06.2012
comment
Спасибо за быстрый ответ! Я собираюсь поискать их прямо сейчас. - person NickB; 21.06.2012
comment
Я прочитал все три приведенные вами ссылки. Третий был отличным и очень полезным. Однако у меня сложилось впечатление, что создание экземпляров позволяет указать только диапазон экземпляров; Я хотел бы иметь возможность выбирать их произвольно. Например, если это мой набор {груша, яблоко, апельсин, пицца}, и я просто хочу яблоко и пиццу, есть ли способ передать индекс, чтобы указать только их и пропустить два других? Спасибо. - person NickB; 21.06.2012