Быстрое введение в OpenSceneGraph (часть 1)

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

Полезные утилиты

Начнем с полезных утилит, которые входят в состав OSG.

osgviewer – просмотр 3д-моделей. Пример использования из командной строки:

osgviewer cow.osg

Можно настроить в Windows так, чтобы по клику на файле с 3д-моделью запускался osgviewer. Он поддерживает множество аргументов командной строки.

osgconv – конвертация между разными форматами 3д-моделей, которые поддерживает OSG. Пример использования из командной строки:

osgconv inputfile outputfile

Есть полезный аргумент -–compressed, который при конвертации сжимает текстуры:

osgconv –-compressed inputfile outputfile

Аргументы командной строки

Вид

Переходим к служебным классам, которые формируют каркас приложения.

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

Вид реализуется в 2х классах. Класс osg::View содержит ссылку на виртуальную камеру и возможно на камеры-слейвы (о них позже). От него наследуется класс osgViewer::View, который дополнительно содержит ссылки на граф сцены, на объект-манипулятор (для перемещения по сцене) и имеет методы для быстрого создания графического контекста, взаимодействия с ним и несколько вспомогательных методов.

Основные методы:

  • setSceneData устанавливает ссылку на граф сцены.

  • setUpViewAcrossAllScreens, setUpViewInWindow, setUpViewOnSingleScreen предназначены для быстрого создания графического контекста.

Вьюер

Вьюер – главный организационный объект. Должен быть только один в приложении (это не синглтон, но все равно создавать несколько вьюеров не желательно). Запускает цикл рендеринга и контролирует многопоточность.

В OSG имеется 2 класса-вьюера, производные от базового osgViewer::ViewerBase.

  • osgViewer::Viewer – для простых приложений, где имеется только один вид. Поэтому для удобства использования он дополнительно наследуется от класса osgViewer::View.

  • osgViewer::CompositeViewer – для приложений, в которых несколько видов.

Основные методы:

  • realize нужен для автоматического создания графического контекста (выбирает один из 3х методов osgViewer::View).

  • frame рендерит один кадр.

  • run запускает цикл рендеринга (вызов метода frame в цикле).

Диаграмма классов выглядит так:

Диаграмма классов

Разбор аргументов командной строки

Класс osg::ArgumentParser позволяет удобно разбирать аргументы командной строки. Есть куча примеров использования.

Пример

Итого простейшая программа на OpenSceneGraph будет выглядеть так:

int main(int argc, char** argv)
{
    osg::ArgumentParser arguments(&argc, argv);

    osgViewer::Viewer viewer(arguments);

    osg::Node* root = ; //Формируем граф сцены, загружаем 3д-модели

    viewer.setSceneData(root);
    viewer.realize();   
    return viewer.run();
}

Камера

Класс osg::Camera, как видно из названия, соответствует виртуальной камере и содержит множество настроек камеры: матрицы вида, проекции, вьюпорт, параметры очистки буферов.

Матрицу вида можно задавать/получать как через osg::Matrix, так и с помощью 3х векторов eye, center, up.

Матрицу проекции можно задавать/получать 4 способами: напрямую через osg::Matrix, а также через asOrtho, asFrustum, asPerspective.

Метод setGraphicsContext устанавливает графический контекст.

Можно рендерить в текстуру, если задать RenderTargetImplementation и прикрепить к камере нужные текстуры с помощью метода attach.

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

Нужно отметить, что среди базовых классов osg::Camera есть класс osg::Node. В целом, в приложении экземпляры класса osg::Camera могут встречаться в 3х разных местах:

  • Главная камера вида. Камера прикреплена к osg::View. Об этом говорилось раньше.
  • Подчиненная камера (slave). Также прикреплена к osg::View, но через метод addSlave. Об этом позже.
  • Один из узлов графа сцены (благодаря наследованию от osg::Node). Об этом позже.

Графический контекст

Графический контекст – экземпляр одного из классов, производных от osg::GraphicsContext. Отвечает за взаимодействие с операционной системой: создание графического окна, контроль размеров окна, выделение памяти под буферы кадра, получение от операционной системы входных данных от клавиатуры и мыши. В разных ОС это осуществляется по разному, поэтому для каждой ОС есть своя реализация и свой производный класс от osg::GraphicsContext. Есть также производный класс для встраивания в интерфейс Qt.

Графический контекст может быть автоматически создан в методе вьюера realize либо с помощью методов класса osgViewer::View. Но иногда его необходимо настраивать вручную. Пример:

osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits(osg::DisplaySettings::instance().get());
traits->screenNum = 0;
traits->x = 0;
traits->y = 0;
traits->width = 1920;
traits->height = 1080;
traits->windowDecoration = false;
traits->doubleBuffer = true;
traits->sharedContext = 0;

osg::ref_ptr<osg::GraphicsContext> gc = osg::GraphicsContext::createGraphicsContext(traits.get());

camera->setGraphicsContext(gc.get());
camera->setViewport(new osg::Viewport(0, 0, traits->width, traits->height));

camera->setDrawBuffer(GL_BACK);
camera->setReadBuffer(GL_BACK);

Здесь через класс osg::GraphicsContext::Traits устанавливаются свойства контекста: номер экрана, размеры окна в пикселях и т.д. Статический метод createGraphicsContext автоматически выбирает реализацию контекста в зависимости от операционной системы.

После создания контекста вся работа с ним происходит «под капотом».

Настройки

Класс osg::DisplaySettings инкапсулирует некоторые настройки приложения. Похож на синглтон в том смысле, что есть один выделенный экземпляр, доступный из любой точки приложения с помощью статического метода instance. Но при этом можно создавать новые экземпляры этого класса для изменения поведения отдельных видов.

Кратко список настроек:

  • Настройки буферов кадра
  • Настройки параметров стерео
  • Настройки количества потоков для подгрузки данных

osg::DisplaySettings может инициализировать параметры стереорежима на основе переменных окружения или аргументов командной строки. После этого параметры доступны на чтение. Можно также их задавать вручную.

Пример включения сглаживания и буфера трафарета. Эти строки нужно поместить в начало функции main:

osg::DisplaySettings::instance()->setNumMultiSamples(4);
osg::DisplaySettings::instance()->setMinimumNumStencilBits(8);