Самый эффективный способ нарисовать вершину с OpenGL


8

Я пишу 3D-игру OpenGL. Там будет множество треугольников для местности и объектов в использовании.

Я учусь на официальном руководстве OpenGL, и первым представленным методом является вызов функции glVertexпосле glBeginкаждой вершины, которую вы хотите нарисовать.

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

Подходя к моему вопросу: я хотел бы получить четкое объяснение наиболее современных и эффективных функций и методов OpenGL, используемых для отправки информации о вершинах на аппаратное обеспечение и предоставления инструкций по визуализации.

Каковы наилучшие способы и инструменты OpenGL для отправки заказа на визуализацию данных вершин, которые редко изменяются во время фреймов (я думаю о рельефе и объектах), и каков наилучший способ отправки данных вершин, которые сильно меняются во время фреймов?


Вы правы, glBegin () и glEnd () являются частью рендеринга непосредственного режима и довольно старомодны. В настоящее время можно использовать, например, VBO. Как именно и что вы используете, зависит от вашего сценария!
thalador

Я также студент, пытающийся изучать OpenGL. Я знаю, что вам нужно будет использовать объекты буфера вершин (VBO), чтобы быстрее рисовать вершины. Хотя я просто не знаю достаточно, чтобы рассказать вам, как это сделать, я могу дать вам ссылку на большую (бесплатную онлайн) книгу, которая делает. Я снял его и сделал маленький треугольник, но это действительно сложно. Arcsynthesis Modern OpenGL Tutorial Я сейчас изучаю уроки LazyFoo. Они начинаются с конвейера фиксированных функций glBegin (), glEnd (), а затем переходят в более современный OpenGL, используя конвейер фиксированных функций в качестве основы. Я буду
Бенбот

Ответы:


19

В общем, ответ «это зависит». Способ отправки обновлений частиц несколько отличается от способа отправки серии моделей с графическим интерфейсом.

Vertex / Index Buffers

Однако в общем смысле все в наши дни делается с помощью VBO ( объект буфера вершин ). Старый API непосредственного режима ( glBegin/ glEnd), вероятно, просто реализован как толстая оболочка вокруг внутренней системы буферов вершин драйвера.

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

Для динамических объектов сделайте то же самое, хотя и с динамическим набором буферов.

Расширенные заметки

Для больших кусков, таких как ландшафт, вы, вероятно, не будете разбивать сетку на несколько частей. Заставить графический процессор визуализировать сто миллионов треугольников, когда видны только двести тысяч из них, - огромная трата, особенно если они не отсортированы и существует много вызовов overdraw и потраченных фрагментов шейдеров. Разбейте сетку на большие куски, а затем визуализируйте только те, которые находятся в пределах видимости. Существуют также различные более продвинутые методы отбраковки, которые вы можете использовать, чтобы отсеять куски, которые могут находиться в зоне досок, но полностью находятся за холмом или зданием или чем-то еще. Удерживать обратный отсчет вызовов отрисовки - это хорошо, но есть баланс (который вы должны найти для своего конкретного приложения / оборудования) между минимизацией вызовов отрисовки и минимизацией отрисовки скрытой геометрии.

Одна из ключевых вещей, которую следует иметь в виду при использовании буфера GPU, заключается в том, что вы не можете записывать в него, пока GPU читает из него. Вы должны сообщить водителю, что можно удалить старую копию буфера (когда это будет сделано) и дать вам новую (если старая занята). Там, конечно, долгое время не было функции OpenGL для этого (теперь есть InvalidateBufferData для GL 4.3 и некоторые более старые реализации в качестве расширения). Скорее, существует нестандартное, но распространенное поведение, которое реализует большинство драйверов. Сделайте это, чтобы сбросить буфер перед обновлением:

glBindBuffer(GL_ARRAY_BUFFER, my_vbo);
glBufferData(GL_ARRAY_BUFFER, 0, NULL, GL_DYNAMIC_DRAW);

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

Обратите внимание, что он может быть быстрее в использовании glBufferDataили glBufferSubDataбыстрее glMapBuffer. Это действительно зависит от драйвера и оборудования. Аппаратные средства ПК нынешнего поколения, вероятно, будут самыми быстрыми, glBufferDataно проверят, чтобы быть уверенными.

Другой метод - использовать инстансинг . Instancing позволяет сделать один вызов отрисовки, который рисует несколько копий данных в буфере вершин / индексов. Если бы у вас было, скажем, 100 одинаковых камней, вы бы хотели нарисовать их все за один раз, а не делать 100 независимых розыгрышей.

При создании экземпляра вам необходимо поместить данные для каждого экземпляра в другой буфер (например, положение объекта каждого отдельного человека). Это может быть либо обычный буфер ( постоянный буфер в терминологии D3D), либо текстурный буфер, либо атрибут вершины для каждого экземпляра. Опять же, что быстрее зависит. Атрибуты для каждого экземпляра, вероятно, быстрее и определенно намного проще в использовании, но многие распространенные реализации GL по-прежнему не поддерживают, glBindingAttribDivisorпоэтому вам нужно будет посмотреть, доступен ли он для использования и действительно ли он быстрее (некоторые старые драйверы эмулировали экземпляры путем добавления буферы, и в итоге было медленнее использовать на них инстансинг, чем делать независимые вызовы отрисовки, и нет стандартного способа выяснить ... радости использования OpenGL).

Существуют также алгоритмы оптимизации вершинного кэша , которые представляют собой порядок упорядочения вершин / индексов в ваших буферах для воспроизведения с вершинным кэшем на современных графических процессорах. Графический процессор будет запускать шейдер только для вершины, а затем кэшировать его в кеше вершин, но, возможно, его придется выселить слишком рано, чтобы освободить место для других вершин. (Скажем, два треугольника имеют общую вершину, но между ними нарисовано еще 100 треугольников; общая вершина, вероятно, будет в итоге расточительно оценена вершинным шейдером дважды.)

Некоторые из этих функций требуют достаточно новой версии GL или GLES. Например, GLES2 не поддерживает создание экземпляров.

Всегда профиль

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

Когда дело касается производительности, всегда будь скептиком .

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.