Отрисовка нескольких моделей в OpenGL с помощью одного вызова отрисовки

Я построил 2D-графический движок и создал для него пакетную систему, поэтому, если у меня есть 1000 спрайтов с одинаковой текстурой, я могу нарисовать их одним вызовом openGl.

Это достигается помещением в один массив вершин vbo всех вершин всех спрайтов с одинаковой текстурой.

Вместо «распечатать эти вершины, распечатать эти вершины, распечатать эти вершины», я делаю «сложить все вершины вместе, напечатать», просто для большей ясности. Достаточно просто, но теперь я пытаюсь добиться того же в 3D, и у меня большая проблема.

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

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

Если бы я выполнял преобразование за пределами шейдера, он бы выполнялся процессором, что я не считаю хорошей идеей по очевидным причинам.

Но проблема в этом. Мне нужно передать матрицу шейдеру, но для каждой модели матрица разная.

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

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


person Ryno    schedule 14.06.2015    source источник


Ответы (3)


Существует функция, в точности похожая на то, что вы ищете, и она называется создание экземпляров. При создании экземпляров вы сохраняете n матрицы (или что-то еще, что вам нужно) в унифицированном буфере и вызываете glDrawElementsInstanced для рисования n копий. В шейдере вы получаете дополнительный ввод gl_InstanceID, с помощью которого вы индексируете в Uniform Buffer, чтобы получить матрицу, необходимую для этого конкретного экземпляра.

Вы можете узнать больше о создании экземпляров здесь: https://www.opengl.org/wiki/Vertex_Rendering#Instancing

person orost    schedule 14.06.2015
comment
Отличный ответ! Я сам пытался реализовать что-то похожее, но я действительно не знал, как, это именно то, что я искал - person Ryno; 15.06.2015

Ответ зависит от того, идентичны ли данные вершин для каждого элемента или нет. Если это так, вы можете использовать создание экземпляров, как в ответе @orost, используя glDrawElementsInstanced и gl_InstanceID в вершинном шейдере, и этот метод должен быть предпочтительным.

Однако, если для каждой 3D-модели требуются разные данные вершин (что часто бывает), вы все равно можете визуализировать их с помощью одного вызова отрисовки. Для этого вы должны добавить еще один поток в данные вершины с помощью glVertexAttribPointerglEnableVertexAttribArray). Этот дополнительный поток будет содержать индекс матрицы в унифицированном буфере, который вершина должна использовать при рендеринге, поэтому каждая сетка в VBO будет иметь идентичный индекс в дополнительном потоке. Единообразный буфер содержит те же данные, что и в настройке экземпляра.

Обратите внимание, что этот метод может потребовать дополнительной обработки ЦП, если вам нужно повторить пакетную обработку - например, объект в пакете больше не должен отображаться. Если этот процесс требуется часто, следует определить, действительно ли выгодно группирование товаров.

person MuertoExcobito    schedule 15.06.2015
comment
Лучший ответ, который у меня был до сих пор, он действительно помог мне понять, как к нему подойти ... Может быть, я попытаюсь написать систему, которая автоматически выбирает инстансинг или этот метод ... Я могу сохранить вместе с сетками количество ее вершин . Затем, каждый раз, когда я добавляю сетку в систему, если номер такой же, как у сетки ранее, я сохраняю bool isInstance = true, но как только я получаю другое количество вершин, я устанавливаю его на false. В конце кадра я визуализирую тем или иным способом, в зависимости от значения bool. - person Ryno; 16.06.2015
comment
Вам кажется разумным? - person Ryno; 16.06.2015
comment
@ user3578051 важно не только количество вершин, но и фактическое содержимое. По сути, создание экземпляров просто рисует одну и ту же сетку несколько раз. Предположительно, вы бы изменили единообразные данные между экземплярами. В этом методе содержимое данных буфера вершин может полностью различаться между экземплярами. Система, которая может обрабатывать и то, и другое (и предпочитает создание экземпляров), вероятно, будет правильным подходом, за исключением случаев, когда, скажем, ваши партии меняют каждый кадр. - person MuertoExcobito; 16.06.2015
comment
ммм .... нет, погоди, я скажу тебе то, что понял, а ты скажешь мне, в чем я ошибся. Идея состоит в том, чтобы поместить все вершины в один vbo (каждая вершина содержит позицию, цвет, нормаль и все остальное). Допустим, я добавляю куб, а затем огромную сетку (всего 1000 вершин). У моего vbo будет 36 + 1000 вершин. Затем у меня есть унифицированный буфер, содержащий матрицы MVP, которых в данном случае две. Я понял, что должен иметь буфер длиной 1036, где первые 36 - указатели на первую матрицу, а другие указатели - на вторую. Это правильно? - person Ryno; 16.06.2015
comment
Если это верно, моя идея была бы следующей: 1) Я дам каждой сетке идентификатор (потому что вы сказали, что одного количества вершин недостаточно) 2) Каждый раз, когда я добавляю в пакет, я помещаю модель в пакет с собственной текстурой 3) Если идентификатор другой, я переключусь на режим без создания экземпляров 4) в конце кадра я визуализирую двумя способами в зависимости от метода, который мне нужно использовать. Это нормально? - person Ryno; 16.06.2015
comment
Я думаю, это звучит так, как будто это сработает. Я просто хотел прояснить, что у вас могут быть две разные сетки, но с одинаковым количеством вершин. - person MuertoExcobito; 17.06.2015
comment
Хорошо, отлично, да, я знал этот факт, потому что я подумал, что при создании экземпляров вы также должны передать vbo со всеми вершинами для всех матриц, а затем указать, как вырезать его в графический процессор. Например, для десяти кубов передайте 360 вершин vbo и скажите графическому процессору брать 36 вершин за раз. В этом случае я подумал, что даже сетки с одинаковыми номерами будут работать, но, вероятно, для создания экземпляров требуется просто одна сетка в качестве параметра и номер N для выполнения работы, я еще не пробовал, поэтому я не знал. Спасибо за ответы, теперь мои идеи стали более ясными - person Ryno; 17.06.2015
comment
Новости: Я реализовал пакетную систему и инстансинг. Я хотел бы добавить к нему ваш метод для обработки разных моделей, поэтому я сделал дополнительный поток, передал индексы, но как я могу использовать их в шейдере? Я получаю матрицу из потока экземпляров и индекс из вашего ... но я не могу использовать этот индекс для получения матрицы из vbo, потому что в вершинном шейдере у меня есть только одна матрица ... надеюсь, у меня есть было ясно ... - person Ryno; 18.06.2015
comment
Ваши матрицы должны быть установлены в шейдере как униформы, а не как VBO. - person MuertoExcobito; 18.06.2015

Помимо создания экземпляра и добавления еще одного атрибута вершины в качестве идентификатора объекта, я хотел бы также упомянуть еще одну стратегию (которая требует современного OpenGL):

Расширение ARB_multi_draw_indirect (в ядре, начиная с GL 4.3) добавляет косвенный команды рисования. Эти команды действительно получают свои параметры (количество вершин, начальный индекс и т. Д.) Непосредственно из другого буферного объекта. С помощью этих функций можно нарисовать множество различных объектов с помощью одного вызова отрисовки.

Однако, поскольку вам по-прежнему требуется некоторое состояние для каждого объекта, такое как матрицы преобразования, этой функции недостаточно. Но в сочетании с ARB_shader_draw_parameters (еще не в ядре GL) вы получаете параметр gl_DrawID, который будет увеличиваться на единицу для каждого отдельного объекта в одном косвенном вызове mult draw. Таким образом, вы можете индексировать в некоторый UBO, или TBO, или SSBO (или что-то еще), где вы храните данные для каждого объекта.

person derhass    schedule 15.06.2015