1 просмотров
Рейтинг статьи
1 звезда2 звезды3 звезды4 звезды5 звезд
Загрузка...

OpenGL 2.0: программируемый, масштабируемый и расширяемый

OpenGL 2.0: программируемый, масштабируемый и расширяемый

Два графических API, OpenGL и DirectX, наглядно демонстрируют раскол, творящийся в компьютерном мире. Иногда такая ситуация тормозит прогресс, иногда – ускоряет. Появляются новые разработки, и кажется, что разрыв между двумя API сокращается.

Однажды Microsoft и SGI, разработчик OpenGL, предприняли попытку добавить OpenGL в DirectX. Проект был назван Fahrenheit, и он кончился полным фиаско. Надежды индустрии создать универсальный API, работающий как в системах Microsoft, так и в других, пошли прахом. Единый API создал бы цельную платформу для разработчиков графических приложений. Задача похожа на невозможную, и на самом деле, она такой и оказалось. Microsoft обвинили в слабохарактерности, а SGI – в поддержке порочного принципа «что не мое – чужое». В любом случае, человеческие ресурсы была потрачены, Fahrenheit остался только в памяти, а Microsoft бросила все силы на дальнейшее совершенствование DirectX API для его использования как в играх, так и в high-end графике.

В отличие от OpenGL, являющегося открытым стандартом и обладающим всеми соответствующими атрибутами, DirectX предназначен для облегчения разработки приложений под Windows.

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

Расширения, к сожалению, не вызвали значительного энтузиазма у большинства разработчиков программ, нуждавшихся в цельной платформе. Вместо этого были созданы многочисленные расширения, причем некоторые из них предназначались лишь для специальных приложений. Комитет разработчиков OpenGL (ARB) не смог сделать больше по нескольким причинам, среди которых стоит упомянуть и разногласия в интеллектуальной собственности. Некоторые компании пытались войти в ARB и реализовать свою технологию, но в то же время они старались защитить свою интеллектуальную собственность. Так что много сил у ARB ушло на примирение разных интересов. Комитет разработчиков OpenGL, по сути, неплохо поработал все эти годы, однако индустрия ушла вперед огромными темпами, осуществляя весомое давление на ARB.

Не отягощенный бюрократией OpenGL, DirectX быстро впитал в себя ряд возможностей, которые когда-нибудь появятся и в OpenGL. Главное нововведение заключается в добавлении программируемости в API, такая возможность еще далека до широкого применения разработчиками программ, однако продавцам ПО и «железа» она пришлась по душе. Благодаря программируемости API талантливый разработчик может сделать графику такой, какой он ее желает видеть. Причем все эффекты и функции будут доступны на типичном «железе». Конечно же, при этом будет достигаться и желанная цель «уменьшения нагрузки на центральный процессор». Члены комитета ARB, а по большей части это компании, также разрабатывают продукты и под DirectX. Они ясно понимают необходимость дальнейшего совершенствования OpenGL для распространения своих продуктов под различные аппаратные платформы.

Но и сам DirectX отнюдь не является беспроблемным: с одной стороны, у нас есть nVidia DirectX 8 с программируемыми шейдерами версии 1.2, с другой стороны существует подход ATi, который Microsoft обозначила как DirectX 8.1 с программируемыми шейдерамии версии 1.4. Каждый подход реализует доступ к программированию чипа, однако у каждого используется своя архитектура регистров. Все это отнюдь не играет на руку разработчикам. В теории, DirectX 9 должен решить данную проблему, поскольку он предлагает полностью аппаратно-независимый подход. Но имейте в виду, что и на пути Microsoft не так все гладко, о чем говорит переименование DirectX 10 в DirectX 9.1.

К тому же, с DirectX Microsoft пытается сделать акцент на нуждах объемного потребительского рынка, отодвинув профессиональный рынок на второе место, а именно там прекрасно работал OpenGL. Учитывая консервативную природу большинства разработчиков профессиональных графических пакетов, дальнейшее развитие OpenGL весьма желанно на профессиональном сегменте, даже под Windows.

На прошлогодней выставке Siggraph кое-кто высказал мнение, что nVidia сделала прекрасный ход по искажению идеи программируемости путем убеждения Microsoft реализовать подход nVidia в DirectX 8 первым. nVidia преуспела и в обучении разработчиков использованию этих новых возможностей. Далее на сцену вышла ATi со своим подходом, и проблема стала очевидной. Разработчикам также пришлось вмешаться, поскольку они бояться опасности остаться наедине с единственным разработчиком «железа» в такой критичной области, как программирование графики. Да и сошелся ли свет клином на Windows?

Спор перешел сейчас на OpenGL, где до сих пор все попытки внести программируемость в API реализовывались через дополнения. Было создано огромное число дополнений, причем здесь возникли сходные аппаратные проблемы, к тому же усугубленные различными подводными камнями с интеллектуальной собственностью. (Всего было определено около 230 расширений OpenGL. Документация по расширениям nVidia превышает 500 страниц, в то время как сама спецификация OpenGL 1.3 умещается на 284 страницах.) Поэтому у OpenGL сейчас существует множество нерешенных проблем.

Хотя сегодня OpenGL может напрямую поддерживаться чипом, не так давно комитет разработчиков начал откат назад. Разговор ведется о том, какие функции существующих чипов следует стандартизировать, и вряд ли все это ускорит появление будущего индустриального стандарта. OpenGL не предусматривает аппаратно-независимого подхода к новым программируемым графическим процессорам, предлагая использовать различные аппаратные архитектуры через соответствующие расширения. При этом проблемы с интеллектуальной собственностью сдерживают широкое распространение и дальнейшее развитие расширений. Возникает вопрос: проблемы с интеллектуальной собственностью действительно тормозят прогресс, или все намного глубже?

Ответ на поверхности, причем все не так плохо, как могло бы быть

На собрании комитета разработчиков OpenGL в сентябре 2001 года 3DLabs раскрыла свое видение OpenGL 2.0. В прошлом де-факто лидером ARB являлась SGI, следовавшая пути дальнейшего развития расширений. Но в последнее время SGI занята целиком своей реорганизацией, так что 3DLabs стала более агрессивно вести себя в комитете, заложив фундамент OpenGL 2.0 и проведя предварительную подготовку перед встречей в сентябре. Также на Siggraph представители компании показали соответствующую презентацию. Поэтому к моменту собрания ARB, члены комитета были уже подготовлены к обсуждению предложения 3DLabs.

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

Комитет поддержал предложение 3DLabs по разработке архитектуры OpenGL 2.0, в результате чего прояснился новый курс. Как уже говорилось выше, главной целью доработки OpenGL является поддержка программируемости графических чипов. Однако не следует забывать и другие аспекты. OpenGL был разработан в 1992 году, когда графические чипы были медленными, и вопрос об управлении памятью не поднимался. Сейчас ситуация изменилась, и для поддержки расширенной обработки пикселей в OpenGL необходимо включить управление памятью. Также OpenGL следует догнать (или обогнать) DirectX в отношении компрессии текстур, или, как называет технологию ARB, сжатия пикселей (pixel packing). Далее OpenGL должен учитывать встроенные решения. Здесь ситуация несколько отличается от обычных компьютеров, поскольку встроенная графика по своему определению фиксирована, приложения имеют свои специфические требования, да и требования эти сложными не назовешь (по крайней мере, на первой поре). Скажем, одно из направлений развития встроенных систем заключается в интеграции всех возможностей Playstation 1 в один чип.

И, наконец, группа Khronos, разработчики и покровители OpenML, надеются добавить свой стандарт в OpenGL, что позволит разрабатывать продукты, сочетающие богатую видео-функциональность и расширенные графические возможности. К тому же группа OpenML, в которую входят 3DLabs, Sun, Intel, Discreet, Evans & Sutherland, Pinnacle, RealViz и SGI, имеет свои планы на встроенные системы. Они надеются, что, развивая мультимедиа-стандарты, скажем, на Интернет-приставках, можно пойди дальше переноса игр на PDA и добиться богатых графических и видео возможностей вместе с интерактивностью.

Естественно, при создании нового расширенного OpenGL стандарта следует обеспечить обратную совместимость с OpenGL 1.3, при этом сохранив паритет с DirectX, включив такие возможности, как обработку вершин и пикселей и управление памятью.

В первой фазе OpenGL будет поддерживать полную обратную совместимость с OpenGL 1.3. Для этого OpenGL 2.0 будет состоять из существующих функций OpenGL 1.3, с включенными новыми функциями в режиме полной совместимости, как показано на рисунке выше. Преимущество такого процесса заключается и в том, что он сможет распутать клубок расширений, выросших вокруг OpenGL. Внедрение программируемости предоставит намного более удобный путь для интеграции существующих расширений.

Следующим шагом станет синтез «чистого OpenGL 2.0», который обеспечит обтекаемое API для разработчиков. Некоторые OpenGL функции будут определены как «наследственные», а разработчикам будет рекомендовано использовать более гибкие программируемые функции взамен фиксированных «наследственных». Этот шаг определен на следующей иллюстрации.

3DLabs хорошо снабдила OpenGL 2.0 документацией. Как утверждает компания, они разработали достаточно революционный план по изменению самой природы OpenGL, поскольку нынешняя реализация не является гибкой. Новая реализация будет программируемой, что позволит проложить быстрый путь внесения наболевших совершенствований. При этом будут учитываться уже существующие расширения, а приоритет будет отдан тем областям, где требуется сделать наибольшее количество изменений.

В соответствии с планом, OpenGL сохранит фиксированную функциональность там, где не требуется гибкость и где аппаратная реализация дешевле и эффективнее. К примеру, такие функции, как область видимости (frustum clipping), отбрасывание нелицевых граней (backface culling), отображение (viewport mapping) и т.д., останутся фиксированными. Также операции с кадровым буфером, включая операции чтения/модификации/записи, продолжат быть прерогативой железа. Не забывайте, что главная цель OpenGL заключается в расширении программируемости под всеми операционными системами и аппаратными платформами.

Программируемость

Программируемость – ключевое слово в OpenGL 2.0, оно означает ориентацию API на более широкое взаимодействие с приложениями. Как и подобает подобному стандарту, программируемость графики реализуется через высокоуровневый язык программирования. Язык обладает богатой функциональностью, независим от аппаратных решений (как и подобает стандарту) и разработан специально для OpenGL. Выделим ключевые моменты языка.

  • Программируемая обработка вершин – про эту функцию вы еще много услышите. Она заменит трансформацию координат, наложение материала (material application), освещение и позволит выполнять произвольные операции по вершинам.
  • Программируемая обработка фрагментов является еще одной ключевой особенностью. Она заменит доступ к текстурам, наложение текстур и туман и позволит выполнять произвольные операции по фрагментам. Именно этого так долго ждали разработчики.
  • Программируемый формат изображений заменит фиксированное сжатие изображений, позволив произвольно сочетать тип и формат при отсылке/получении пиксельных данных через OpenGL.

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

Возможности

Преимущества нового API заключаются в стандартизации возможностей и улучшенной производительности, помимо стандартизации существующей функциональности. Большая часть опциональной области OpenGL 1.3 по обработке изображений (imaging subset) перешла в разряд стандарта OpenGL 2.0. Многочисленные расширения будут введены в стандартный состав OpenGL 2.0, что позволит выжать максимальную производительность из «железа». В результате мы выиграем в производительности при работе с графической подсистемой и добьемся большего параллелизма между процессором и графическим чипом. Схематично вид нового API можно представить следующей схемой:

Читать еще:  Обзор зеркальной фотокамеры Nikon D7500: шаг вперед или компромисс

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

Урок №1. Введение в OpenGL

Обновл. 30 Мар 2020 |

Перед началом нашего путешествия мы должны определиться с тем, что такое вообще OpenGL.

Что такое OpenGL?

OpenGL (англ. «Open Graphics Library») в основном рассматривается как API (англ. «Application Programming Interface» = «Интерфейс прикладного программирования»), который предоставляет большой набор функций, которые мы можем использовать для управления графикой и изображениями. Если конкретнее, то OpenGL является спецификацией, разработанной и поддерживаемой Khronos Group.

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

Люди, разрабатывающие библиотеки OpenGL, обычно являются производителями видеокарт. Каждая приобретаемая вами видеокарта поддерживает определённые версии OpenGL, разработанные специально под эту линейку видеокарт. При использовании программного обеспечения от Apple библиотека OpenGL поддерживается, собственно, разработчиками Apple, а в Linux существует целый набор версий графических поставщиков и адаптации от опенсорс сообщества этих библиотек. Это также означает, что всякий раз, когда OpenGL демонстрирует странное поведение, которого не должно было бы быть, то это, скорее всего, вина производителей видеокарт (или тех, кто разрабатывал/поддерживает эту библиотеку).

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

Khronos публично размещает все спецификации документов для всех версий OpenGL. Заинтересованный читатель может найти здесь спецификацию OpenGL версии 3.3 (которую мы и будем использовать), где он сможет углубиться в детали OpenGL (обратите внимание, что в этой спецификации в основном просто описываются результаты, а не реализации). Эта спецификация также предоставляют отличную справочную информацию для понимания того, какой результат выполнения функций должен быть.

Core-profile vs. Непосредственный режим

В старые времена использование OpenGL означало разработку в непосредственном режиме (ещё называемом «конвейером фиксированных функций»), который был простым в использовании методом для рисования графики. Большая часть функционала OpenGL была скрыта внутри библиотеки, и разработчики не имели контроля над тем, как OpenGL выполняет свои вычисления. Поскольку разработчики жаждали большей гибкости, то со временем спецификации стали более гибкими; разработчики получили больше контроля над своей графикой. Непосредственный режим действительно прост в использовании и понимании, но он также крайне неэффективен. По этой причине, начиная со спецификации версии 3.2, функционал непосредственного режима начали считать устаревшим, мотивируя тем самым разработчиков перейти на разработку в режиме core-profile, который является разделом спецификации OpenGL, удаливший весь устаревший функционал.

Используя core-profile, OpenGL заставляет нас использовать современные техники. Всякий раз, когда мы пытаемся использовать одну из устаревших функций OpenGL в режиме core-profile, OpenGL выбросит ошибку и остановит рисование. Преимуществом изучения современного подхода является его гибкость и эффективность. Тем не менее, учить его уже несколько сложнее. Непосредственный режим довольно сильно абстрагировался от реальных операций, выполняемых OpenGL, и, хотя это было легко освоить, трудно было понять, как на самом деле работает OpenGL. Современный подход требует от разработчика понимания работы OpenGL и графического программирования, и, хотя это немного сложно, это обеспечивает гораздо большую гибкость и эффективность и, что самое важное, гораздо лучшее понимание графического программирования.

Это также одна из причин, по которой этот туториал более ориентирован на core-profile в OpenGL версии 3.3. Хотя это сложнее, но оно того стоит.

На сегодняшний день доступны более новые версии OpenGL (на момент написания — 4.6), поэтому следует логичный вопрос: «Почему я должен изучать OpenGL 3.3, когда уже есть OpenGL 4.6?». Дело в том, что все последующие версии OpenGL, начиная с 3.3, добавляют дополнительные полезные фичи в OpenGL без изменения фундаментального ядра/базиса, используемого в OpenGL; более новые версии просто предоставляют несколько более эффективных или полезных способов решения одних и тех же задач. В результате все концепции и техники остаются неизменными при выходе новых версий OpenGL, поэтому изучение OpenGL 3.3 является совершенно справедливым.

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

Расширения в OpenGL

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

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

OpenGL 2.0: программируемый, масштабируемый и расширяемый

glMap2f(GL_MAP2_TEXTURE_COORD_2, 0, 1, 2, 2, 0, 1, 4, 2, st) ; Для получения результирующего цвета при наложении текстуры OpenGL учитывает цвет, материал вершины и применяет ряд фильтров. Когда одному пикселю изображения после растеризации соответствует дробное число текселей, используются фильтры увеличения (magnification) или уменьшения (minification) текстуры.

Рис. 5. Фильтры увеличения и уменьшения текстуры В OpenGL существует понятие уровня детализации текстуры. Каждый уровень задается отдельным изображением. В зависимости от расстояния до наблюдателя OpenGL выбирает нужный уровень детализации, не прибегая к лишним операциям масштабирования. Данный механизм называется мипмэпингом (mipmap-ing). Изображения, используемые в мипмэпинге, задаются непосредственно или создаются OpenGL автоматически.

Очевидно, что текстура является довольно ресурсоемким объектом. Вызовом функции glGetlntegerv(GL_MAX_TEXTURE_SlZE. ) можно определить максимально возможный размер квадратной текстуры. Но иногда этого недостаточно, так как при этом, например, не учитывается внутренний формат данных и уровень детализации. С помощью текстуры подмены (proxy texture) можно более точно определить, хватает ли памяти для указанной текстуры. Для этого функцией gITexImage нужно определить текстуру подмены (типы текстур с префиксом GL_PROXY_), а затем получить о ней информацию функцией gIGetTexLevelParameter. Если памяти недостаточно, то функция для любых запрашиваемых параметров устанавливает значение 0. Текстуру подмены нельзя использовать в качестве обычной текстуры, так как с ней не связано никакого изображения.

Иногда возникает необходимость наложить одну и ту же текстуру на многоугольник несколько раз. Тогда приходится выводить многоугольник несколько раз, изменяя текстурные координаты. В последних версиях OpenGL или через такие расширения, как ARB_multitexture и SGIS_multitexture, эта задача существенно облегчается возможностью перед выводом сразу указать несколько наборов текстурных координат функцией gIMultiTexCoord.

Список функций для наложения текстур: gIBindTexture — выбирает активную текстуру; glCopyTexImage — сохраняет изображение в цветовом буфере как текстуру;

glCopyTexSublmage — заменяет часть изображения текстуры данными из цветового буфера;

gIGenTextures — создание массива имен текстур; gITexCoord — установка текстурных координат; glTexEnv — параметры наложения текстур; glTexGen — генерация текстурных координат; gITexImage — определение изображения текстуры; glTexParameter — установка параметров текстуры; gITexSublmage — изменение части изображения текстуры.

Демонстрация наложения текстур.

Для работы примера необходимы два файла texl.bmp tex2.bmp с 24-битными изображениями размера 25 6×25 6. */

GLobj cam; // камера

Models m; // объект моделей

uint tex[2]; // массив имен текстур

// коэффициенты для отображения // координат вершины в текстурные координаты float sp[4]=<0,О,0.05,0>; float tp[4]= <0,0.05,0,0>;

Оптимизация OpenGL приложений (Optimizing OpenGL applications)

OpenGL – библиотека (API — Application Programming Interface) для обработки графической информации и прямого доступа к железу («software interface to graphics hardware», как обозначаются они в спецификации). Библиотека содержит набор уже однажды написанных функций, от самых простых (вывод точки на экран) до довольно сложных (построение готовых примитивов, например, трехмерной пирамидки), которые применяются практически в каждой программе. Базовые функции реализованы аппаратно, в виде части GPU, более сложные функции представляют собой программные модули, построенные на базовых командах. Видеокарта не всегда аппаратно поддерживает нужные для работы приложения функции, и в этом случае библиотека использует программные модули, эмулирующие требующиеся возможности. Сама же библиотека реализована в виде информации о структуре, определяющей, как объекты будут нарисованы во framebuffer, причем некоторые из структур доступны пользователю, который может создавать различные вызовы процедур для изменения параметров.

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

Display list – группа OpenGL команд, которые сохраняются на стороне сервера для последующего выполнения. Улучшается производительность, поскольку для запуска списка команд всего лишь одна команда glCallList передается по медленному каналу между клиентом и сервером.

Vertex Operation. Каждая вершина преобразуются матрицей GL_MODELVIEW (от СК объекта в СК наблюдателя). Кроме того, если включено освещение, то вместе с преобразованием вершин преобразуются параметры нормальных векторов.

Primitive Assembly. Примитивы (точки, линии и полигоны) преобразуются матрицей GL_PROJECTION и обрезаются объемом просмотра, который определяется в СК наблюдателя. После этого, определяется перспектива делением на коэффициент W и создается 2D сцена в СК окна.

Pixel Transfer Operation. После того, как пиксели прочитаны из памяти клиента, выполняется масштабирование, смещение и отображение. Обработанные данные сохраняются в текстурной памяти или непосредственно подготавливаются для фрагментных операций.

Texture Memory. Текстура изображения загружается в текстурную память для нанесения на геометрических объектов.

Raterization. Растеризация — преобразование геометрических и пиксельных данных в фрагмент. Фрагмент – прямоугольный массив, содержащий цвет, глубину, ширину линии, размер точки и данные для расчетов сглаживания (GL_POINT_SMOOTH, GL_LINE_SMOOTH, GL_POLYGON_SMOOTH). Если включен режим заполнения GL_FILL, то пространство полигона будет заполнено. Каждый фрагмент содержит информацию о пикселах в буфере кадра.

Fragment Operation. Это последний процесс преобразования фрагментов в пиксели на буфере кадра. Первый процесс на этой стадии – генерация текселей; элемент текстуры генерируется из памяти текстур и применяется к каждому фрагменту. Затем вычисления тумана применяются. После этого, следует смешивание, сглаживание, логические операции и битовая маска выполняются. Фактические данные пикселя сохраняются в буфере кадра.

FrameBuffer — область памяти для временного хранения информации о точках, составляющих один кадр изображения на мониторе. Открыть окно во FB – это значит распределить видеопамять под изображение.

Feedback. OpenGL может возвратить большинство из текущих состояний и данных через команды glGet() and glIsEnabled(). Более того, вы можете прочитать в прямоугольную область пиксельных данных из буфера кадра использованием glReadPixels () и получить полностью преобразованные данные про вершины используя glRenderMode (GL_FEEDBACK). glCopyPixels () не возвращает пиксельные данные пикселя, в системную память, но их можно скопировать обратно в другой буфер кадра, например, от переднего (front) буфера в резервный (back) буфер.

Читать еще:  Очаровательный платформер Yoshi для Switch отложен на 2019 год

Модель клиент-сервер

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

Т.о., одним из самых «узких» мест в работе с современным GPU является передача ему данных – текстур, вершин, нормалей и т.п. Очевидный путь оптимизации OpenGL приложения – минимизация передачи информации между клиентом и сервером. Для повышения быстродействия следует уменьшить количество запросов на передачу данных и передавать их как можно большими частями. Если с текстурами все достаточно просто — они один раз загружаются в память GPU и больше не изменяются, то с геометрическими данными (координаты вершин, нормали, текстурные координаты) дело обстоит значительно хуже. Стандартный способ передачи данных через команды glVertex, glNormal и т.п. является крайне неэффективным, поскольку передача данных осуществляется очень маленькими частями и через очень большое количество вызовов. Большие объемы (больше нескольких десятков тысяч примитивов) лучше рисовать с помощью массивов вершин.

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

Пошаговый и пакетный режимы

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

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

Несмотря на свою гибкость, режим непосредственного вывода может оказаться неэффективным, если требуется переопределить неизменяемые интерактивно объекты или параметры. В этом случае используется пакетный режим вывода через дисплейный список (display list), содержащий последовательность команд OpenGL. Смысл ясен – инкапсуляция функциональности (в данном случае – построения объектов для последующего многократного использования).

Дисплейный список – это удобный и эффективный путь именования и организации набора команд OpenGL. Данный механизм удобен в случае необходимости многократного выполнения группы команд, заключенных между скобками glBeginList/glEndList. Поименованный буфер памяти с дисплейным списком хранится на сервере, а его содержимое выводится на экран при поступлении команды glCallList, содержащей приказ от программы клиента. Предположим, например, что вы хотите нарисовать торус и смотреть на него под разными углами. Наиболее эффективный способ сделать это заключается в том, чтобы сохранить торус в дисплейном списке. После этого, если вы хотите изменить угол обзора, все что вам нужно сделать, это изменить видовую матрицу и вызвать список отображения к исполнению.

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

Недостаток дисплейных списков проявляется при частом внесении в него изменений – при этом, понятно, ни один метод оптимизации не будет работать. Для работы с такими «изменчивыми» объектами и сценами в OpenGL предпочтительнее режим прямого отображения. В практике оба метода нашли широкое применение.

Массивы вершин

Можно заметно повысить эффективность передачи данных используя так называемые вершинные массивы (vertex arrays). Рассмотрим особенности их использования на примере прорисовки куба

Куб состоит из 6 граней и 8 вершин. Предположим, что OpenGL умеет работать только с треугольниками (например, OpenGL ES не поддерживает примитив GL_QUADS). Таким образом, нам нужно преобразовать каждую грань в 2 треугольники (используется примитив GL_TRIANGLES). Треугольники в OpenGL представляют собой комбинацию из 3х вершин. Таким образом, что бы “объяснить” OpenGL, что мы хотим изобразить грань куба, нам потребуется нарисовать 2 треугольника со следующими вершинами: , . Здесь видим проблему – это избыточность обработки смежных вершин. Так, например, вершина v0 вызывается в программе 6 раз

С точки зрения OpenGL-оптимизации – это бездарный код. Из общих принципов программирования напрашивается 8 вершин положить в массив и сделать один вызов функции. Это намного быстрее – передать все сразу, чем по одной вершине.

Для оптимального построения куба в OpenGL необходимо создать массив с информацией о 8 вершинах и массив индексов вида , где каждая комбинация из 3х элементов (0,1,22,3,00,3,4…) представляет собой конкретный треугольник. Эта возможность позволяет один раз (без дублирования) записать информацию о вершинах и многократно использовать ее, связав с массивом индексов. Ниже показано, как это может быть реализовано программно.

Этот вариант программы выглядит более профессиональным, однако количество вызовов glVertex() не уменьшилось (12*3=36). Можно заменить 36 вызовов glVertex() единственным вызовом функции glDrawArrays().

Однако, в этом случае необходимо дублировать смежные вершины в массиве, поэтому число вершин будет 36 вместо 8. Функция glDrawElements() позволяет уменьшить число вершин в массиве за счет использования массива индексов. При этом, поскольку массив вершин содержит 8 вершин, то тип данных GLubyte массива индексов может быть достаточным.

Достоинства. При использовании массива вершин передача осуществляется сразу большими блоками, количество обращений к GPU заметно сокращается. Это приводит к гораздо более эффективному использованию GPU и общему повышению быстродействия программы. Указание OpenGL на массивы происходит отдельными функциями, сам вывод тоже происходит отдельной функцией. С массивами вершин вместо тысяч вызовов glVertex3f() мы можем сделать только ОДИН вызов, что-нибудь вроде glDrawArrays(…). Использование массивов вершин ускоряет работу программы в 10, а то и в 20 раз, в зависимости от того, сколько вершин вы отрисовываете. Конечно, в этом примере мы просто отрисовали несколько треугольников, так что большой разницы вы не заметите, но, если добавить ещё 5000 треугольников, то результат будет ощутимым. Если вы посмотрите исходники любой большой игры, например quake, вы увидите, что все они используют массивы вершин. Играм необходимо каждое минимальное увеличение производительности, так как игровые данные очень объемны, а использование массивов вершин даёт отнюдь не маленький выигрыш.

Недостатки. Этот способ требует постоянной передачи массивов данных от CPU к GPU – каждый вызов, в котором используется указатель на массив приводит к передаче его графическому ускорителю и именно эта постоянная необходимость передачи большого объема данных через шину PCI Express ограничивает эффективность приложения. Использование vertex array может уменьшить число вызовов функций. Тем не менее, недостаток vertex array в том, что функции работы с массивами вершин находятся на стороне клиента и массивы должны быть повторно отправлены на сервер каждый раз, когда к ним обращаются.

Vertex Buffer Objects

Vertex Buffer Objects (VBO) – технология, позволяющая хранить координаты вершин совместно с их атрибутами в видеопамяти. При использовании VBO все геометрия загружается в видеопамять только один раз, на этапе инициализации, после чего мы просто ссылаемся на эти данные. Это, во-первых, позволят существенно разгрузить шину для более важных задач, во-вторых – приводит к существенному повышению производительности, так как GPU может незамедлительно приступать к рендерингу, не дожидаясь пока будут получены данные от CPU.

Для создания VBO требуется выполнить 3 шага:

  1. Генерировать новый буферный объект (функция glGenBuffersARB()).
  2. Связать буферный объект (функция glBindBufferARB()).
  3. Копировать данные вершин в буферный объект (функция glBufferDataARB()).

Следующий код является примером создания VBO для прорисовки куба:

Рендеринг с помощью VBO реализуется почти так же, как при использовании vertex array. Разница только в том, что указатель на массив вершин, в данном случае, связан с VBO.

Ниже приводится код, где модифицируется VBO

VBO предназначен для повышения производительности OpenGL, предоставляя преимущества vertex array и display list , избегая при этом недостатков их реализации. VBO создает «буфер объектов» для атрибутов вершин в высокопроизводительной памяти на стороне сервера и обеспечивает те же функции доступа к массивам, которые используются в Vertex arrays. В отличие от display list, данные в VBO могут быть прочитаны и обновлены в памяти клиента. Другое важное преимущество VBO –деление буфера объектов между большим количеством клиентов наподобие, как и display list и текстуры. Так как VBO на стороне сервера, несколько клиентов будут иметь доступ к тому же буферу с соответствующим идентификатором. Использовать VBO рекомендуется практически всегда, проще назвать причины, когда не следует его использовать: это либо очень маленькие массивы данных (неэффективно), либо очень большие (когда они не помещаются в свободную видеопамять).

Pixel_buffer object (PBO) тесно связан с VBO. Он расширяет возможности VBO, позволяя сохранить не только данные о вершинах в буфере, но и данные о пикселах.

Вращение, движение, масштабирование и проекции в OpenGL под Lazarus

В OpenGL используются основные три системы координат: левосторонняя, правосторонняя и оконная. Первые две системы являются трехмерными и отличаются друг от друга направлением оси z: в правосторонней она направлена на наблюдателя, в левосторонней – в глубину экрана. Ось x направлена вправо относительно наблюдателя, ось y – вверх.

Системы координат в OpenGL

Строго говоря, OpenGL позволяет путем манипуляций с матрицами моделировать как правую, так и левую систему координат. Но на данном этапе лучше пойти простым путем и запомнить: основной системой координат OpenGL является правосторонняя система.

РАБОТА С МАТРИЦАМИ

Для задания различных преобразований объектов сцены в OpenGL используются операции над матрицами, при этом различают три типа матриц: модельно-видовая, матрица проекций и матрица текстуры. Все они имеют размер 4×4. Видовая матрица определяет преобразования объекта в мировых координатах, такие как параллельный перенос, изменение масштаба и поворот. Матрица проекций определяет, как будут проецироваться трехмерные объекты на плоскость экрана (в оконные координаты), а матрица текстуры определяет наложение текстуры на объект.

Умножение координат на матрицы происходит в момент вызова соответствующей команды OpenGL, определяющей координату (как правило, это команда glVertex*)

Для того чтобы выбрать, какую матрицу надо изменить, используется команда:

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

Для определения элементов матрицы текущего типа вызывается команда

где m указывает на массив из 16 элементов типа float или double в соответствии с названием команды, при этом сначала в нем должен быть записан первый столбец матрицы, затем второй, третий и четвертый. Еще раз обратим внимание: в массиве m матрица записана по столбцам.

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

МОДЕЛЬНО-ВИДОВЫЕ ПРЕОБРАЗОВАНИЯ

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

Сама матрица может быть создана с помощью следующих команд:

glTranlsate*() производит перенос объекта, прибавляя к координатам его вершин значения своих параметров.

glRotate*() производит поворот объекта против часовой стрелки на угол angle (измеряется в градусах) вокруг вектора (x,y,z).

glScale*() производит масштабирование объекта (сжатие или растяжение) вдоль вектора (x,y,z), умножая соответствующие координаты его вершин на значения своих параметров.

Все эти преобразования изменяют текущую матрицу, а поэтому применяются к примитивам, которые определяются позже. В случае, если надо, например, повернуть один объект сцены, а другой оставить неподвижным, удобно сначала сохранить текущую видовую матрицу в стеке командой glPushMatrix(), затем вызвать glRotate() с нужными параметрами, описать примитивы, из которых состоит этот объект, а затем восстановить текущую матрицу командой glPopMatrix().

Чтобы изображенная фигура выглядела пространственной, систему координат разворачивают вокруг оси X и вокруг оси Y.

ПРОЕКЦИИ

В OpenGL существуют стандартные команды для задания ортографической (параллельной) и перспективной проекций. Первый тип проекции может быть задан командами

Место команд в программе.
Для того чтобы при каждой последующей перерисовке экрана не происходило изменение размеров сцены, связанное с переносом и проецированием, следует использовать видовые команды по определенным правилам.
Первый способ – использование команды glLoadIdentity:

Второй способ – использование команд glPushMatrix и glPopMatrix:

ПРОГРАММА — ПРИМЕР ВРАЩЕНИЯ КУБА В LAZARUS НА OPENGL

Разместите на форме три компонента

Примечание. Обратите внимание для установки компонента TOpenGLControl смотрите урок «Подключение и работа с OpenGL в Lazarus под Windows».

Пропишите на события создания формы Form1: OnFormCreate, нажатия кнопки Form1: OnButton1Click и работы таймера Form1: OnTimer1Timer следующий код:

Комментарии

Timer1 — выдает ошибку. Использовал IdleTimer1, работает!

unit Unit1;

uses
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
ExtCtrls, OpenGLContext, GL, GLU;

TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
IdleTimer1: TIdleTimer;
Panel1: TPanel;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure IdleTimer1Timer(Sender: TObject);
private
< private declarations >
public
< public declarations >
OpenGLControl1: TOpenGLControl; // Контекст воспроизведения OpenGL
cube_rotation: GLFloat;
Speed: Double;
end;

var
Form1: TForm1;

procedure TForm1.Button1Click(Sender: TObject);
begin

glClearColor(1.0, 1.0, 1.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0, double(width) / height, 0.1, 100.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

glTranslatef(0.0, 0.0,-6.0);
glRotatef(cube_rotation, 1.0, 1.0, 1.0);

glBegin(GL_QUADS);
glColor3f(0.0,1.0,0.0); // Set The Color To Green
glVertex3f( 1.0, 1.0,-1.0); // Top Right Of The Quad (Top)
glVertex3f(-1.0, 1.0,-1.0); // Top Left Of The Quad (Top)
glVertex3f(-1.0, 1.0, 1.0); // Bottom Left Of The Quad (Top)
glVertex3f( 1.0, 1.0, 1.0); // Bottom Right Of The Quad (Top)
glEnd();
glBegin(GL_QUADS);
glColor3f(1.0,0.5,0.0); // Set The Color To Orange
glVertex3f( 1.0,-1.0, 1.0); // Top Right Of The Quad (Bottom)
glVertex3f(-1.0,-1.0, 1.0); // Top Left Of The Quad (Bottom)
glVertex3f(-1.0,-1.0,-1.0); // Bottom Left Of The Quad (Bottom)
glVertex3f( 1.0,-1.0,-1.0); // Bottom Right Of The Quad (Bottom)
glEnd();
glBegin(GL_QUADS);
glColor3f(1.0,0.0,0.0); // Set The Color To Red
glVertex3f( 1.0, 1.0, 1.0); // Top Right Of The Quad (Front)
glVertex3f(-1.0, 1.0, 1.0); // Top Left Of The Quad (Front)
glVertex3f(-1.0,-1.0, 1.0); // Bottom Left Of The Quad (Front)
glVertex3f( 1.0,-1.0, 1.0); // Bottom Right Of The Quad (Front)
glEnd();
glBegin(GL_QUADS);
glColor3f(1.0,1.0,0.0); // Set The Color To Yellow
glVertex3f( 1.0,-1.0,-1.0); // Bottom Left Of The Quad (Back)
glVertex3f(-1.0,-1.0,-1.0); // Bottom Right Of The Quad (Back)
glVertex3f(-1.0, 1.0,-1.0); // Top Right Of The Quad (Back)
glVertex3f( 1.0, 1.0,-1.0); // Top Left Of The Quad (Back)
glEnd();
glBegin(GL_QUADS);
glColor3f(0.0,0.0,1.0); // Set The Color To Blue
glVertex3f(-1.0, 1.0, 1.0); // Top Right Of The Quad (Left)
glVertex3f(-1.0, 1.0,-1.0); // Top Left Of The Quad (Left)
glVertex3f(-1.0,-1.0,-1.0); // Bottom Left Of The Quad (Left)
glVertex3f(-1.0,-1.0, 1.0); // Bottom Right Of The Quad (Left)
glEnd();
glBegin(GL_QUADS);
glColor3f(1.0,0.0,1.0); // Set The Color To Violet
glVertex3f( 1.0, 1.0,-1.0); // Top Right Of The Quad (Right)
glVertex3f( 1.0, 1.0, 1.0); // Top Left Of The Quad (Right)
glVertex3f( 1.0,-1.0, 1.0); // Bottom Left Of The Quad (Right)
glVertex3f( 1.0,-1.0,-1.0); // Bottom Right Of The Quad (Right)
glEnd();

cube_rotation += 5.15 * Speed;

procedure TForm1.Button2Click(Sender: TObject);
begin
if IdleTimer1.Enabled = true then
begin
IdleTimer1.Enabled := false;
Button2.Caption:=’Вращать автоматически’;
end
else
begin
IdleTimer1.Enabled := true;
Button2.Caption:=’Остановить вращение’;
end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
// Создание контекста воспроизведения OpenGL и привязка его к панели на форме
OpenGLControl1:=TOpenGLControl.Create(Self);
with OpenGLControl1 do begin
Name:=’OpenGLControl1′;
Align:=alClient;
Parent:=Panel1;
end;

IdleTimer1.Enabled := false;
IdleTimer1.Interval := 100;
Speed := 1;

Button2.Caption:=’Вращать автоматически’;
Button1.Caption:=’Повернуть’;
end;

procedure TForm1.IdleTimer1Timer(Sender: TObject);
begin
Button1Click(Sender);
end;

Стоимость OpenGL команд. (2 стр)

Инстансинг придуман для быстрого рендера одинаковой геометрии с разными параметрами. Каждому объекту соответствует свой индекс, по которому можно выбрать соответствующие параметры из буфера, варьировать какие то переменные и т.д. Главное преимущества от использования инстансинга — можно сильно сократить количество дипов.

Можно сложить все параметры объектов в 1 буфер, переслать на GPU и выполнить один дип. Хранение данных в буферах само по себе является неплохой оптимизацией — экономим на том, что не надо постоянно менять параметры шейдера. К тому же, если данные инстансов не меняются (например, мы точно знаем, что эта геометрия статическая), то можно не пересылать их на гпу постоянно. В целом, для оптимального рендеринга стоит сперва упаковать все инстанс данные в один буфер и передать на GPU одной командой. Для каждого дипа передавать только смещение по которому находятся его данные. Используя индекс инстанса (gl_InstanceID) можно добраться до данных конкретного объекта.

Вариантов хранения данных в OpenGL достаточно много: vertex buffer (VBO), uniform buffer (UBO), texture buffer (TBO), shader storage buffer (SSBO), textures. Все зависит в каком буфере хранятся данные. Есть различные особенности, которые и рассмотрим.

Текстурный инстансинг

Все данные хранятся в текстуре. Для эффективного обновления текстур лучше использовать специальные структуры Pixel Buffer Object (PBO), которые позволяют асинхронно передавать данные на GPU. CPU не ждет пока данные передадутся и продолжает работу.

Код рендера через текстурный инстансинг:

Вершинный шейдер для доступа к данным:

Инстансинг через вершинный буфер

Идея в том, чтобы можно держать данные инстансов в вершинном буфере и передавать их как вершинные атрибуты.

Код создания самого буфера опустим, он выглядит тривиально. Нашей задачей является модифицирование информации о вершине для шейдера (vertex declaration, vdecl).

Вершинный шейдер для доступа к данным:

Uniform buffer instancing, Texture buffer instancing, SSBO buffer instancing

3 метода в целом очень похожи друг на друга, различаются только типом создаваемого буфера.
— Uniform buffer (UBO) отличается небольшим размером, но теоретически должен быть быстрее остальных.
— Texture buffer (TBO) имеет очень большой размер. В нем можно уместить данные всех объектов сцены, данные скелетной трансформации.
— Shader Storage Buffer (SSBO) теоретически обладает обоими свойствами — быстрый с большим размером. К тому же в него можно писать данные. Единственное, это новое расширение и старым железом не поддерживается.

Uniform buffer

Uniforms instancing

Достаточно простой. Имеем возможность передать через glUniform* некоторое количество векторов с данными. Максимальное количество зависит от видеокарты. Получить максимальное количесто можно при помощи вызова glGetIntegerv с параметром GL_MAX_VERTEX_UNIFORM_VECTORS. Для R9 380 вернет 4096. Минимальное значение 256.

Multi draw indirect

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

По сути, данная команда выполняет несколько glDrawElementsInstancedIndirect за один вызов. Есть, правда, неприятная особенность в поведении. Что каждый такой glDrawElementsInstanced будет иметь независимый gl_InstanceID, то есть каждый раз сбрасываться в 0 при новом Draw*. Что усложняет доступ к данным соответствующего инстанса. Обходится эта проблема модифицированием vdecl каждого типа объектов, посылаемых на рендер. Можно почитать Surviving without gl_DrawID.

Стоит заметить, что glMultiDrawElementsIndirect выполнил сразу огромное количество дипов (для наглядности), одной командой. Для этого и предназначен. Не стоит сравнивать его скорость с остальными типами инстансинга.

Сравнение типов инстансинга о скорости

Таблица 8. Стоимость типов инстансинга. Количество итераций = 100. Сверху — количество инстансов. Время потраченное CPU (время потраченное GPU) в ms.

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

UBO, VBO, SSBO, TEXTURE типы инстансинга примерно одинаковы по скорости и имеют ‘хороший’ тайминг.

TBO можно хранить большие объемы информации, но он проигрывает по скорости большинству других типов. Если есть такая возможность, то стоит использовать SSBO для хранения данных, т.к. он и быстрый и обладает большим размером.

Текстурный инстансинг тоже хорошая альтернатива UBO. Поддерживается «старым железом», можно хранить огромное количество информации. Немного неудобно обновлять.
Передавать данные каждый раз через шейдерные переменные, очевидно, оказалось самым медленным.

glMultiDrawElementsIndirect в тестах выполнил 5к, 10к и 20к дипов! Правда мы тестировали просто повторения теста. Такое количество дипов можно было бы сделать и одной командой. Единственное, стоит заметить, что при таком количестве дипов сам масив с их описанием будет достаточно большим. Так что лучше использовать GPU для генерации информации о дипах.

Рекомендации по оптимизации и выводы

В данной статье провели анализ стоимости API вызовов, оценили по скорости различные типы инстансинга геометрии. В целом, чем меньше переключений стейтов тем лучше. Следует использовать новые фичи последних версий API по максимуму: текстурные массивы, SSBO, Draw Indirect, мапинг буферов с флагами GL_MAP_PERSISTENT_BIT и GL_MAP_COHERENT_BIT для быстрой передачи данных и др фичи.

Рекомендации:
— Чем меньше переключений стейтов, тем лучше. Стоит группировать объекты по материалам.
— Можно сделать обертку на смену состояний (текстур, буферов, шейдеров, других стейтов). Проверять, действительно ли менялся ресурс прежде чем вызывать его смену на стороне gl, API вызов которого гораздо дороже чем проверка индекса.
— Объединять геометрию в один буфер.
— Использовать текстурные массивы.
— Хранить данные в буферах и текстурах.
— Использовать как можно меньше шейдеров. Но слишком сложный, универсальный шейдер с большим количеством ветвлений очевидно станет проблемой. Тем более на старых видео картах, где ветвления обходятся дороже.
— Использовать инстансинг.
— Если возможно, использовать Draw Indirect и формировать информацию о дипах на стороне GPU.

Несколько общих советов:
— Нужно вычислять узкие места и в первую очередь оптимизировать их.
— Нужно знать во что упирается производительность – CPU или GPU.
— Не делать работу дважды, переиспользывать результаты работы алгоритма с предыдущего кадра.
— Сложные вычисления можно предрассчитывать.
— Лучшая оптимизация производительности — вообще не делать работу.
— Использовать параллельные вычисления: разбивать работу на части и выполнять в параллельных потоках.

Ссылка на основную публикацию
Статьи c упоминанием слов:
Adblock
detector