Архитектура Qt Quick 3D
Qt Quick 3D расширяет Qt Quick, обеспечивая поддержку отрисовки 3D-контента. Он добавляет обширную функциональность, включая несколько новых публичных импортов QML, а также новую внутреннюю сцену графика и рендерер. Данный документ описывает архитектуру Qt Quick 3D от публичного API до подробностей работы конвейера рендеринга.
Обзор модуля
Qt Quick 3D состоит из нескольких модулей и плагинов, которые предоставляют дополнительные 3D API, а также инструменты для обработки и импорта существующих 3D-активов.
Импорты QML
- QtQuick3D - Основной импорт, содержащий все основные компоненты Qt Quick 3D
- QtQuick3D.Effects - Библиотека встроенных компонентов эффектов пост-обработки
- QtQuick3D.AssetUtils - Библиотека для импорта 3D-активов во время выполнения
- QtQuick3D.Helpers - Библиотека дополнительных компонентов, которые могут быть использованы для проектирования и отладки 3D-сцен.
Библиотеки C++
-
QtQuick3D - Единственный публичный модуль C++. Содержит определения всех типов, экспонированных в импорте QML QtQuick3D, а также несколько C++ API
- QQuick3DGeometry - Подкласс для создания процедурных данных мешей
- QQuick3DTextureData - Подкласс для создания процедурных данных текстур
- QQuick3D::idealSurfaceFormat - используется для получения идеального формата поверхности
-
QtQuick3DAssetImport- Внутренняя и закрытая библиотека для помощи в импорте активов и преобразовании активов в QML. -
QtQuick3DRuntimeRender- Внутренняя и закрытая библиотека, содержащая узлы графа пространственной сцены и рендерер. -
QtQuick3DUtils- Внутренняя и закрытая библиотека, используемая в качестве общей библиотеки утилит всеми другими модулями C++.
Плагины AssetImporters
Инструменты импорта активов реализованы с использованием архитектуры плагинов. Плагины, поставляемые с Qt Quick 3D, расширяют функциональность библиотеки и инструмента импорта активов, Balsam.
- Assimp - Этот плагин использует стороннюю библиотеку libAssimp для преобразования 3D-активов в форматах обмена 3D в компоненты QML Qt Quick 3D.
- UIP - Этот плагин позволяет преобразовывать проекты, созданные с помощью Qt 3D Studio, в компоненты QML Qt Quick 3D.
Как Qt Quick 3D вписывается в стек графики Qt
На приведенной выше диаграмме показано, как Qt Quick 3D вписывается в более широкий стек графики Qt. Qt Quick 3D работает как расширение 2D API Qt Quick, и при использовании элементов 3D-сцены в сочетании с View3D сцена будет отрисована с помощью интерфейса рендеринга аппаратного обеспечения Qt (RHI). RHI будет переводить вызовы API в соответствующие вызовы API рендеринга аппаратного обеспечения для конкретной платформы. На диаграмме выше показаны доступные варианты для каждой платформы. Если явным образом не определен никакой родной бэкэнд, Qt Quick по умолчанию будет использовать разумный родной бэкэнд для рендеринга для каждой платформы.
Интеграция компонентов Qt Quick 3D стека и стека Qt Quick описаны ниже в следующих разделах.
Интеграция 3D в 2D
Отображение 3D-контента в 2D является основной целью API Qt Quick 3D. Основной интерфейс для интеграции 3D-контента в 2D - компонент View3D.
Компонент View3D работает как любой другой производный от QQuickItem класс, с содержимым и реализует виртуальную функцию QQuickItem::updatePaintNode. Qt Quick вызывает updatePaintNode для всех «грязных» элементов в графе сцены Qt Quick во время фазы синхронизации. Это включает элементы 3D, управляемые View3D, которые также проходят свою фазу синхронизации в результате вызова updatePaintNode.
Метод updatePaintNode компонента View3D выполняет следующие действия:
- Настройка рендерера и целевого объекта рендеринга, если они еще не существуют
- Синхронизация элементов в 3D-сцене через SceneManager
- Обновление любых «динамических» текстур, которые были отрисованы Qt Quick (Путь текстуры 2D в 3D ниже)
Однако отрисовка 3D-сцены не происходит в методе updatePaintNode компонента View3D. Вместо этого updatePaintNode возвращает подкласс QSGNode, содержащий рендерер для Qt Quick 3D, который будет отрисовывать 3D-сцену во время фазы предварительной обработки процесса отрисовки Qt Quick.
Прокладка, по которой Qt Quick 3D будет отрисовывать, зависит от используемого View3D::renderMode:
Оффскрин
По умолчанию для View3D используется режим Оффскрин. При использовании режима оффскрин View3D становится поставщиком текстур, создавая оффскрин-поверхность и отрисовывая на ней. Эта поверхность может быть отображена как текстура в Qt Quick и отрисована с помощью QSGSimpleTextureNode.
Этот шаблон очень близок к тому, как работают QSGLayerNodes в Qt Quick.
Подложка
При использовании режима Подложка 3D-сцена непосредственно отрисовывается на QQuickWindow, содержащем View3D. Отрисовка происходит в результате сигнала QQuickWindow::beforeRenderPassRecording(), что означает, что всё остальное в Qt Quick будет отрисовано поверх 3D-контента.
Перекрытие
При использовании режима Перекрытие 3D-сцена непосредственно отрисовывается на QQuickWindow, содержащем View3D. Отрисовка происходит в результате сигнала QQuickWindow::afterRenderPassRecording(), что означает, что 3D-контент будет отрисован поверх всего остального контента Qt Quick.
Встроенный
Режим рендеринга Встроенный использует QSGRenderNode, что позволяет напрямую отрисовывать на целевой объект рендеринга Qt Quick без использования оффскрин-поверхности. Это делается путём вставки команд рендеринга непосредственно во время 2D-рендеринга сцены Qt Quick.
Этот режим может быть проблематичным, так как использует тот же буфер глубины, что и рендерер Qt Quick, а значения z имеют совершенно разный смысл в Qt Quick и Qt Quick 3D.
Интеграция 2D в 3D
При отрисовке 3D-сцены в многочисленных случаях требуется встраивание 2D-элементов в 3D. Существует два различных способа интеграции 2D-контента в 3D-сцены, каждый из которых имеет свой собственный путь к экрану.
Прямой путь
Прямой путь используется для отрисовки 2D-контента Qt Quick так, как если бы он существовал как плоский элемент в 3D-сцене. Например, рассмотрим следующее определение сцены:
Node {
Text {
text: "Hello world!"
}
} Что происходит здесь: когда дочерний компонент устанавливается на пространственный узел типа QQuickItem, он сначала оборачивается классом QQuick3DItem2D, который является просто контейнером, добавляющим 3D-координаты к 2D-элементу. Это задаёт базовое 3D-преобразование для отрисовки всех последующих 2D-потомков, чтобы они отображались правильно в 3D-сцене.
Когда приходит время отрисовать сцену, узлы QSG этих 2D-элементов передаются рендереру Qt Quick для генерации соответствующих команд отрисовки. Поскольку команды выполняются непосредственно и учитывают текущее 3D-преобразование, они отрисовываются точно так же, как и в 2D-рендерере, но отображаются так, как будто они были отрисованы в 3D.
Недостатком этого подхода является то, что никакая информация о освещении 3D-сцены не может быть использована для затенения 2D-контента, поскольку 2D-рендерер Qt Quick не имеет понятия об освещении.
Путь текстуры
Путь текстуры использует 2D-сцену Qt Quick для создания динамического контента текстуры. Рассмотрим следующее определение текстуры:
Texture {
sourceItem: Item {
width: 256
height: 256
Text {
anchors.centerIn: parent
text: "Hello World!"
}
}
} Этот подход работает так же, как и элементы Layer в Qt Quick, в том смысле, что всё отрисовывается на оффскрин-поверхность размером с родительский элемент, а эта оффскрин-поверхность затем может быть использована как текстура, которая может быть повторно использована в другом месте.
Затем эта текстура может быть использована материалами в сцене для отрисовки контента Qt Quick на элементах.
Синхронизация сцены
Менеджер сцены
Менеджер сцены в Qt Quick 3D отвечает за отслеживание пространственных элементов в 3D-сцене и за обеспечение обновления соответствующих узлов графа сцены во время фазы синхронизации. В Qt Quick эту роль выполняет QQuickWindow в 2D случае. Менеджер сцены является основным интерфейсом между узлами переднего плана и объектами бэкенд-графа сцены.
Каждый элемент View3D будет иметь по крайней мере один менеджер сцены, так как один создаётся и связывается с корнем встроенной сцены при создании. Когда пространственные узлы добавляются в качестве потомков View3D, они регистрируются в менеджере сцены View3D. При использовании импортированной сцены создаётся второй SceneManager (или используется существующий), для управления узлами, которые не являются непосредственными потомками View3D. Это необходимо, потому что, в отличие от View3D, импортированная сцена не существует на QQuickWindow до тех пор, пока на неё не будет сделана ссылка. Дополнительный SceneManager гарантирует, что активы, принадлежащие импортированной сцене, создаются по крайней мере один раз на QQuickWindow, к которому они относятся.
Хотя менеджер сцены — это внутренний API, важно знать, что менеджер сцены отвечает за вызов updateSpatialNode для всех объектов, которые были помечены как грязные путём вызова метода update().
Синхронизация Frontend/Backend
Цель синхронизации — обеспечить соответствие состояний, установленных на переднем плане (Qt Quick), состоянию на заднем плане (рендерер графа пространственной сцены Qt Quick). По умолчанию передний и задний план находятся в разных потоках: передний план в главном потоке Qt, а задний план в потоке рендеринга Qt Quick. Фаза синхронизации — это место, где главный поток и поток рендеринга могут безопасно обмениваться данными. Во время этой фазы менеджер сцены вызовет updateSpatialNode для каждого грязного узла в сцене. Это либо создаст новый узел бэкенда, либо обновит существующий для использования рендерером.
Граф пространственной сцены Qt Quick
Qt Quick 3D разработан для использования той же схемы разделения фронтенда/бэкенда, что и Qt Quick: объекты фронтенда управляются движком Qt Quick, в то время как объекты бэкенда содержат данные состояния для отрисовки сцены. Объекты фронтенда наследуются от QObject и доступны для движка Qt Quick. Элементы в файлах исходного кода QML напрямую соответствуют объектам фронтенда.
По мере обновления свойств этих объектов фронтенда создается один или несколько бэкенд-узлов и размещаются в графическом дереве сцены. Поскольку отрисовка 3D-сцен включает в себя гораздо больше состояний, чем отрисовка 2D, существует отдельный набор специализированных узлов графического дерева сцены для представления состояния объектов 3D-сцены. Это графическое дерево сцены известно как Qt Quick Spatial Scene Graph.
И объекты фронтенда, и бэкенд-узлы можно разделить на два класса. Первый — пространственные, в том смысле, что они существуют где-то в трехмерном пространстве. На практике это означает, что каждый из этих типов содержит матрицу преобразования. Для пространственных элементов отношения родитель-потомок имеют значение, поскольку каждый дочерний элемент наследует преобразование своих родителей.
Другой класс элементов — ресурсы. Элементы ресурсов не имеют положения в 3D-пространстве, а представляют собой данные состояния, используемые другими элементами. Между этими элементами может существовать отношение родитель-потомок, но оно не имеет другого значения, кроме владения.
В отличие от 2D-графического дерева сцены в Qt Quick, пространственное графическое дерево сцены предоставляет пользователю узлы ресурсов напрямую. Например, в Qt Quick, хотя QSGTexture — это общедоступный API, нет QQuickItem, который напрямую предоставляет доступ к этому объекту. Вместо этого пользователь должен либо использовать элемент Image, который описывает, откуда происходит текстура, а также как ее отобразить, либо написать код на C++, чтобы работать с QSGTexture непосредственно. В Qt Quick 3D эти ресурсы напрямую доступны в QML-API. Это необходимо, поскольку ресурсы являются важной частью состояния сцены. К этим ресурсам могут обращаться многие объекты в сцене: например, многие материалы могут использовать одну и ту же текстуру. Также можно задать свойства текстуры во время выполнения, что напрямую повлияет на способ выборки текстуры, например.
Пространственные объекты
Все пространственные объекты являются подклассами компонента Node, который содержит свойства, определяющие положение, вращение и масштаб в 3D-пространстве.
Объекты ресурсов
Объекты ресурсов являются подклассами компонента Object3D. Object3D — это просто подкласс QObject с некоторыми специальными помощниками для использования с менеджером сцены. У объектов ресурсов есть ассоциации родитель/потомок, но они в основном полезны для управления ресурсами.
View3D и слои отрисовки
Что касается разделения фронтенда/бэкенда, View3D является точкой разделения с точки зрения пользователя, потому что View3D определяет, какой контент сцены следует отрисовывать. В Qt Quick Spatial Scene Graph корневым узлом сцены, которая будет отрисовываться, является узел Layer. Узлы Layer создаются View3D с использованием комбинации свойств View3D и свойств SceneEnvironment. При отрисовке сцены для View3D именно этот узел Layer передается в рендерер для отрисовки сцены.
Отрисовка сцены
Настройка целевого изображения для отрисовки
Первый шаг в процессе отрисовки — определение и настройка целевого изображения для отрисовки сцены. В зависимости от свойств, установленных в SceneEnvironment, фактическое целевое изображение будет отличаться. Первое решение — отрисовывать ли содержимое напрямую на поверхность окна или на внеэкранную текстуру. По умолчанию View3D будет отрисовывать на внеэкранную текстуру. При использовании эффектов пост-обработки отрисовка на внеэкранную текстуру является обязательной.
После определения целевого изображения для отрисовки сцены устанавливаются некоторые глобальные состояния.
- размер окна — если отрисовка на окно
- viewport — размер области сцены, которая будет отрисовываться
- прямоугольник scissor — подмножество окна, к которому должен быть применён обрезка viewport
- цвет очистки — цвет, которым необходимо очистить целевое изображение, если необходимо.
Подготовка к отрисовке
Следующий этап отрисовки — подготовка, где рендерер выполняет текущую работу, чтобы определить, что нужно отрисовать в данный кадр, и что все необходимые ресурсы доступны и актуальны.
Этап подготовки сам по себе имеет две фазы: высокоуровневую подготовку по определению того, что должно быть отрисовано и какие ресурсы необходимы; и низкоуровневую подготовку, которая использует RHI для фактической настройки рендеринговых конвейеров и буферов, а также настройки зависимостей отрисовки основного прохода сцены.
Высокоуровневая подготовка к отрисовке
Цель этой фазы — извлечь состояние пространственного графического дерева сцены в нечто, что можно использовать для создания команд отрисовки. Обзор заключается в том, что рендерер создает списки комбинаций геометрии и материалов для отрисовки с точки зрения одной камеры со множеством состояний освещения.
Сначала определяется общее глобальное состояние для всего содержимого. Если SceneEnvironment определяет lightProbe, то проверяется, загружена ли карта среды, связанная с текстурой light probe, и если нет, то загружается или генерируется новая карта среды. Генерация карты среды сама по себе будет набором проходов для свертки исходной текстуры в кубовую карту. Эта кубовая карта будет содержать как информацию о отражении, так и о освещении, которая используется для затенения материала.
Далее рендерер должен определить, какую камеру использовать в сцене. Если активная камера не определена явно View3D, используется первая доступная камера в сцене. Если в сцене нет камер, то ничего не рендерится, и рендерер завершает работу.
После определения камеры можно рассчитать матрицу проекции для этого кадра. Вычисление выполняется на этом этапе, потому что каждый рендерируемый элемент должен знать, как его спроецировать. Это также означает, что теперь можно рассчитать, какие рендерируемые элементы должны быть отрисованы. Начиная со списка всех рендерируемых элементов, удаляются все элементы, которые не видны, потому что они либо отключены, либо полностью прозрачны. Затем, если отсечение по конусу обзора включено для активной камеры, проверяется каждый рендерируемый элемент, чтобы определить, находится ли он полностью за пределами видимой области конуса обзора камеры, и если да, то он удаляется из списка рендерируемых элементов.
Помимо проекции камеры, также рассчитывается направление камеры, поскольку это необходимо для вычислений освещения в коде затенения.
Если в сцене есть узлы освещения, они собираются в список, длина которого равна максимальному доступному числу источников света. Если в сцене есть больше узлов освещения, чем поддерживает рендерер, любые дополнительные узлы освещения, превышающие этот предел, игнорируются и не влияют на освещение сцены. Можно указать область действия узлов освещения, но обратите внимание, что даже при установке области действия состояние освещения каждого источника света все равно отправляется каждому материалу, который имеет освещение, но для источников света, не входящих в область действия, яркость устанавливается в 0, поэтому на практике эти источники света не влияют на освещение этих материалов.
Теперь, с, надеюсь, более коротким списком рендерируемых элементов, каждый из этих элементов должен быть обновлен, чтобы отразить текущее состояние сцены. Для каждого рендерируемого элемента проверяется, загружен ли соответствующий материал, и если нет, создается новый. Материал — это сочетание шейдеров и рендерингового конвейера, и он необходим для создания вызова отрисовки. Кроме того, рендерер проверяет, загружены ли все необходимые для отрисовки рендерируемого элемента ресурсы, например, геометрия и текстуры, установленные на модели. Ресурсы, которые еще не загружены, загружаются здесь.
Список рендерируемых элементов затем сортируется в 3 списка.
- Непрозрачные элементы: они сортируются от переднего к заднему, или другими словами, от элементов, которые ближе к камере, к элементам, которые дальше от камеры. Это делается для использования аппаратного отсечения или раннего определения z в шейдере фрагмента.
- 2D-элементы: это элементы QtQuick, которые отрисовываются рендерером Qt Quick.
- Прозрачные элементы: они сортируются от заднего к переднему, или другими словами, от элементов, которые дальше от камеры, к элементам, которые ближе к камере. Это делается, потому что прозрачные элементы должны быть смешаны со всеми элементами, которые находятся позади них.
Низкоуровневая подготовка к отрисовке
Теперь, когда для этого кадра определено всё необходимое, можно заняться подготовкой и зависимостями основного прохода отрисовки. В первую очередь на этом этапе выполняются отрисовки дополнительных проходов, необходимые для основного прохода.
- Render DepthPass - Некоторые функции, такие как Screen Space Ambient Occlusion и Shadowing, требуют предварительного прохода по глубине. Этот проход включает в себя рендеринг всех непрозрачных элементов в текстуру глубины.
- Render SSAOPass - Цель прохода Screen Space Ambient Occlusion заключается в генерации текстуры затенения окружающей среды. Эта текстура используется позже материалами для затемнения определённых областей при затенении.
- Render ShadowPasses - Каждый свет в сцене, у которого включено затенение, вносит вклад в дополнительный проход затенения. Рендерер использует две различные техники затенения, поэтому в зависимости от типа света будут разные проходы. При рендеринге теней от направленного света сцена рендерится в 2D-текстуру непрозрачности, исходя из комбинации направления направленного света и размера камеры. При рендеринге теней от точечного или прожекторного света текстура непрозрачности света представляет собой куб, отображающий вклад непрозрачности относительно каждого направления грани света.
- Render ScreenTexture - Этот проход будет выполняться только при использовании CustomMaterial, которому требуется текстура экрана, которая может использоваться для таких методов рендеринга, как преломление. Этот проход работает как проход по глубине, но вместо этого рендерит все непрозрачные элементы в цветную текстуру.
После завершения рендеринга зависимостей остальные проходы подготавливаются, но не рендерятся. Эта подготовка включает в себя преобразование состояния, собранного на этапе подготовки высокого уровня, в графические примитивы, такие как создание/обновление значений унифицированных буферов, связывание сэмплеров с зависимыми текстурами, настройку для связывания ресурсов шейдера и всё остальное, необходимое для создания состояния конвейера, необходимого для выполнения вызова отрисовки.
Отрисовка сцены
Теперь, когда подготовка завершена, выполняется отрисовка элементов, входящих в основной контент сцены.
- Clear Pass - Это не совсем проход, но в зависимости от того, какой backgroundMode установлен для SceneEnvironment, могут происходить разные действия. Если режим фона прозрачный или цветной, то буфер цвета очищается либо прозрачностью, либо указанным цветом. Если же режим фона установлен на SkyBox, то будет выполнен проход, который рендерит SkyBox с точки зрения камеры, который также заполнит буфер начальными данными.
- Opaque Pass - Далее все непрозрачные элементы будут отрисованы. Это просто включает настройку состояния конвейера и выполнение команды отрисовки для каждого элемента в порядке списка, так как они уже отсортированы на этом этапе.
- 2D Pass - Если в сцене есть какие-либо 2D-элементы, то вызывается рендерер Qt Quick для генерации команд рендеринга, необходимых для рендеринга этих элементов.
- Transparent Pass - И наконец, прозрачные элементы сцены рендерятся по одному так же, как и непрозрачные элементы.
Это завершает рендеринг сцены.
Последовательная обработка
Если включена какая-либо функциональность пост-обработки, можно предположить, что результатом рендеринга сцены является текстура, которая является входным параметром для этапа пост-обработки. Все методы пост-обработки являются дополнительными проходами, которые работают с этой входной текстурой сцены.
Все шаги этапа пост-обработки являются необязательными, и если их нет, то выход рендеринга сцены используется в качестве конечного целевого объекта.
Эффекты пост-обработки
Этот первый шаг пост-обработки заключается в выполнении всех пользовательских эффектов пост-обработки. Они определяются в упорядоченном списке свойством SceneEnvironment::effects. Способ работы заключается в том, что каждый эффект пост-обработки является частью цепочки, при этом выход предыдущего эффекта является входом для следующего. Первый эффект в этой цепочке получает входные данные непосредственно от выходных данных шага рендеринга сцены. Также эффекты могут получать доступ к текстуре глубины, выводимой шагом рендеринга сцены.
Каждый эффект в этом процессе может состоять из нескольких подпроходов, что означает, что можно рендерить контент в промежуточные буферы. Ожидается, что конечный проход многопроходного эффекта выведет одну текстуру, содержащую данные цвета, которые будут использоваться следующими шагами этапа пост-обработки.
Временная и прогрессивная антиалиасинг
Шаги временного и прогрессивного антиалиасинга можно выборочно включить, установив свойства в SceneEnvironment. Хотя это не строго часть этапа пост-обработки, фактические результаты временного и прогрессивного антиалиасинга реализуются на этапе пост-обработки.
Временная антиалиасинг выполняется, когда сцена активно обновляется. При включении активная камера делает очень небольшие корректировки направления камеры для каждого кадра при отрисовке сцены. Текущий кадр затем смешивается с ранее отрисованным кадром для сглаживания отрисованного изображения.
Прогрессивная антиалиасинг выполняется только когда сцена не обновляется. При включении выполняется обновление, и текущее состояние сцены рендерится с очень небольшими корректировками направления активной камеры. До 8 кадров накапливаются и смешиваются с заранее определёнными весами. Это позволяет сгладить неанимированную сцену, но влечёт за собой затраты производительности, потому что для каждого обновления будет отрисовано несколько дополнительных кадров.
Антиалиасинг сверхдискретизации (SSAA)
Антиалиасинг сверхдискретизации — это грубый способ сглаживания сцены. Он работает путём рендеринга в текстуру, которая является кратным значением запрошенного размера сцены, а затем последующим её сжатием до целевого размера. Например, если запрошен 2X SSAA, то сцена будет рендериться в текстуру, которая в 2 раза больше предполагаемого размера, а затем сжиматься в этом этапе. Это может сильно повлиять на производительность и использование ресурсов, поэтому следует избегать этого метода, если это возможно. Также размер View3D может быть слишком большим для использования этого метода, поскольку текстура, необходимая для этого метода, может быть больше, чем поддерживается графическим оборудованием.
© The Qt Company Ltd
Licensed under the GNU Free Documentation License, Version 1.3.
https://doc.qt.io/qt-6.2/qtquick3d-architecture.html