ИНТЕГРАЦИЯ ГРАФИЧЕСКОГО ИНСТРУМЕНТАРИЯ OPENSCENEGRAPH С ФРЕЙМВОРКОМ QT

INTEGRATION OF THE OPENSCENEGRAPH GRAPHICAL TOOLKIT WITH THE QT FRAMEWORK

1 Введение

На сегодняшний день Qt является одним из наиболее успешных кросс-платформенных приложений и UI-фреймворков, которые могут использоваться под Linux, Windows, MacOS X и даже мобильными системами. Что делает ее более удобной, чем интеграция OSX с оконными ручками X11/Windows. Qt включает в себя классы, которые необходимы при создании прикладного программного обеспечения, начиная с элементов графического интерфейса и заканчивая классами для работы с сетевыми интерфейсамибазами данных и XML. Является полностью объектно-ориентированным, расширяемым и поддерживающим технику компонентного программирования.

2 Интеграция OSG с QT

Qt имеет класс QGLWidget, который может отображать элементы OpenGL непосредственно на поверхности окна, в более старых версиях исходного кода OSG можно найти некоторые примеры использования этого класса. Но для версии 3.0 и более новых версий OSG имеет библиотеку osgQt для реализации различных функций, связанных с Qt.

В качестве примера интеграции OSG и Qt воспользуемся классом osgQt:: GraphicsWindowQt. Прежде чем приступать к работе, имеется необходимость обзавестись и скомпилировать библиотеку Qt 4.x.

Распределим работу на несколько структурированных шагов:

1. Включаем необходимые заголовки. При включении обращаем внимание, что мы можем добавить определенные заголовки классов Qt вместо <QtCore> и <QtGui> (рис. 1):

Рис. 1: Подключение библиотек

2. Функция createCamera() будет использовать класс Traits для определения базового окна параметра, а затем применить его к новому экземпляру osgQt::GraphicsWindowQt. Этот osgQt::GraphicsWindowQt объект на самом деле включает в себя виджет Qt, связанный с графическим контекстом OSG для операций рендеринга. После того, как установится сцены камеры, виджет будет автоматически отображать сцену (рис. 2).

Рис. 2: Настройка камеры

3. Определяем класс ViewerWidget, производный от QWidget, как контейнер виджета визуализации. Он также имеет таймер, который может инициировать событие обновления, чтобы часто выполнять метод frame() для OSG (рис. 3).

Рис. 3: класс ViewerWidget

4. В конструкторе класса ViewerWidget можно настроить OsgViewer, включая настройку основной камеры, данных сцены и манипулятора камеры (рис. 4).

Рис. 4: Настройка OsgViewer

5. Придется снова получить объект osgQt::GraphicsWindowQt и добавить его внутренний виджет в родительское окно Qt с помощью метода getGLWidget() (рис. 5).

Рис. 5: Объединение с родительским окном

6. Последний шаг в конструкторе  ­­­- это запустить QTimer и заставить его начать с постоянного интервала ожидания. Когда таймер истечет, появится сигнал timeout() и будет вызван метод update(), который перекрашивает окно и, таким образом, выполняется _viewer.frame() в переопределенном paintEvent() (рис. 6).

Рис. 6: Запуск таймера

7. В основной части кода мы создадим объект камеры и виджета один за другим и запустим цикл событий Qt с помощью метода exec(). Моделирование OSG цикл уже выполнен в методе paintEvent() (рис. 7).

Рис. 7: Моделирование OSG

8. Теперь вы увидите сцену с моделью коровы, встроенной в окно Qt (рис. 8).

Рис. 8: Результат

Нажав клавишу S, мы увидим, что частота кадров составляет около 25 кадров в секунду (рис. 8). Это просто потому, что метод paintEvent() будет вызываться, когда таймер истекает каждые 40 мс, и, таким образом, вызывает метод frame() средства просмотра, который вызывается 25 раз в секунду. Мы также можем найти в конструкторе виджета Viewer, что используется однопоточная модель, здесь (рис. 9):

Рис. 9 Однопоточный режим

Это означает, что операции обновления, отбраковки и рендеринга выполняются в одном потоке/процессе, без использования многопоточной стратегии оптимизации OSG и, таким образом, ограничены интервалом таймера. В Windows мы можем удалить эту строку напрямую, чтобы улучшить производительность рендеринга. Но в некоторых дистрибутивах Linux могут возникнуть проблемы при включении многопоточной визуализации с помощью виджета Qt, и приложение может аварийно завершить работу.

3 Запуск цикла рендеринга в отдельных потоках

Что бы избежать подобных ошибок, необходимо запустить циклы рендеринга в отдельных потоках. Мы не можем быть в восторге от скорости рендеринга 25 кадров в секунду. Это означает, что OSG может использовать не более 1/25 одной секунды для работы рендеринга. И когда есть больше объектов для визуализации или больше задач для завершения в обратных вызовах, частота кадров будет продолжать падать и делает весь процесс неэффективным.

Одним из решений является использование независимого потока для обхода OSG и команд рисования. Конечно, библиотека OpenThreads, которая входит в основной исходный код OSG, является лучшим выбором для реализации такой работы. Но поскольку мы также работаем с Qt, мы попробуем класс Qt threading. Код будет основан на интеграции OSG с Qt и будет иметь только несколько модификаций.

Распределим работу на несколько структурированных шагов:

1. Функция createCamera() не нуждается в изменении. Мы будем наследовать от класса QThread для разработки многопоточного решения визуализации. Он фактически переопределяет только метод run() для выполнения цикла моделирования зрителя. И когда экземпляр будет уничтожен, мы должны немедленно установить существующий флаг зритель и ждать, пока цикл будет завершен. Это крошечный, но стандартный стиль для написания многопоточных программ (рис. 10).

Рис. 10 Многопоточность

2. В конструкторе класса виджета Viewer мы больше не будем запускать события обновления с помощью таймера, а вместо этого запустим поток (рис. 11).

Рис. 11 Запуск потока

3. Больше никаких изменений в главной записи.

4. Результат точно такой же, как в предыдущем примере интеграции Qt. Но когда мы нажмем клавишу S для просмотра сцены и статистики рендеринга. Мы обнаружим, что частота кадров на этот раз будет стабильной на уровне 60 кадров в секунду (в прошлом это было около 25 кадров в секунду). Это все потому, что работа рендеринга в отдельном потоке будет выполняться одновременно с основным процессом вместо ожидания вызова в событии таймера (рис. 12).

Рис. 12 Многопоточный результат

Класс QThread легко понять, поскольку он только повторно реализует следующий метод (рис. 13):

Рис. 13 Многопоточный метод

4 Заключение

Предпочтительный способ заставить приложения на основе OpenGL извлекать выгоду из нескольких потоков - это отделить пользовательский поток от чертежного потока. То есть для обработки пользовательской логики и работы по обновлению, а также операций отбраковки сцен в одном или нескольких потоках, которые никогда не могут быть изменены во время рендеринга. Данные, подлежащие обновлению или отбраковке, должны быть отложены, если они должны использоваться потоком визуализации одновременно. Это также известно как структура «app-cull-draw» в современных фреймворках рендеринга.

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

 

Литература

1.       Верескун В.Д., Притыкин Д.Е. Многомассовая модель подвижной единицы для исследования продольной динамики грузового поезда // Вестник РГУПС. – 2014. – №2. – С. 16 – 27.

2.       Верескун В.Д., Притыкин Д.Е. Математическое моделирование тормозных процессов в грузовом поезде // Механика и трибология транспортных систем: сборник докладов международной научной конференции, Ростов-на-Дону, 8 – 10 ноября 2016 г.: в 2 т. – Ростов н/Д: ФГБОУ ВО РГУПС. – 2016. – Т. – С. 3 – 11.

3.       Официальный сайт компании Qt [электронный ресурс] точка доступа: https://www.qt.io/ (дата обращения: 10.12.2019).

4.       Официальный сайт компании OSG [электронный ресурс] точка доступа: https://ru.osgeurope.com/ (дата обращения: 13.12.2019).