Это введение предназначено для людей, которые хорошо умеют программировать на C++, у которых есть опыт использования OpenGL, DirectX или игровых движков, и которые понимают базовые вещи в компьютерной графике.
Здесь будут описаны основные классы, связи между ними и кратко примеры использования.
Начнем с полезных утилит, которые входят в состав OSG.
i. osgviewer - просмотр 3д-моделей. Пример использования из командной строки:
osgviewer cow.osg
Можно настроить в Windows так, чтобы по клику на файле с 3д-моделью запускался osgviewer. osgviewer поддерживает кучу
аргументов командной строки.
ii. osgconv - конвертация между разными форматами 3д-моделей, которые поддерживает OSG. Пример использования из командной строки:
osgconv inputfile outputfile
Есть полезный аргумент -compressed, который при конвертации сжимает текстуры. Рекомендуется использовать всегда:
osgconv -compressed inputfile outputfile
Аргументы командной строки.
Переходим к служебным классам, которые формируют каркас приложения.
1. OpenSceneGraph поддерживает работу с несколькими окнами, несколькими мониторами и даже с несколькими видеокартами одновременно. Поэтому главной структурной единицей приложения является вид. Каждый вид включает в себя отдельную трехмерную сцену, свою независимую виртуальную камеру (или несколько) и графический контекст (или несколько). Рендеринг в разные контексты может выполняться в разных потоках.
Вид реализуется в 2х классах. Класс osg::View содержит ссылку на виртуальную камеру и возможно на камеры-слейвы (о них позже). От него наследуется класс osgViewer::View, который дополнительно содержит ссылки на граф сцены, на объект-манипулятор (для перемещения по сцене) и имеет методы для быстрого создания графического контекста, взаимодействия с ним и несколько вспомогательных методов.
Метод setSceneData устанавливает ссылку на граф сцены.
Методы setUpViewAcrossAllScreens, setUpViewInWindow, setUpViewOnSingleScreen предназначены для быстрого создания графического контекста.
2. Вьюер - главный организационный объект. Должен быть только один в приложении (это не синглтон, но все равно создавать несколько вьюеров не желательно). Запускает цикл рендеринга и контролирует многопоточность.
В OSG имеется 2 класса-вьюера, производные от базового osgViewer::ViewerBase.
osgViewer::Viewer - для простых приложений, где имеется только один вид. Поэтому для удобства использования osgViewer::Viewer дополнительно наследуется от класса osgViewer::View.
osgViewer::CompositeViewer - для приложений, в которых несколько видов.
Метод realize нужен для автоматического создания графического контекста (выбирает один из 3х методов osgViewer::View).
Метод frame рендерит один кадр.
Метод run запускает цикл рендеринга (вызов метода frame в цикле).
Диаграмма классов выглядит так:
3. Класс osg::ArgumentParser позволяет удобно разбирать аргументы командной строки. Есть куча примеров использования.
Итого простейшая программа на OpenSceneGraph будет выглядеть так:
int main(int argc, char** argv)
{
osg::ArgumentParser arguments(&argc, argv);
osgViewer::Viewer viewer(arguments);
osg::Node* root = …;
viewer.setSceneData(root);
viewer.realize();
return viewer.run();
}
4. Класс 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). Об этом позже.
5. Графический контекст - экземпляр одного из классов, производных от osg::GraphicsContext. Отвечает за взаимодействие с операционной системой: создание графического окна, контроль размеров окна, выделение памяти под буферы кадра, получение от операционной системы входных данных от клавиатуры и мыши. В разных ОС это осуществляется по разному, поэтому для каждой ОС есть своя реализация и свой производный класс от osg::GraphicsContext. Есть также производный класс для встраивания в интерфейс Qt.
Графический контекст может быть автоматически создан в методе вьюераrealizeлибо с помощью методов класса osgViewer::View. Но иногда его необходимо настраивать вручную. Пример:
osg::ref_ptr traits = newosg::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 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автоматически выбирает реализацию контекста в зависимости от операционной системы.
После создания контекста вся работа с ним происходит “под капотом”.
6. Класс osg::DisplaySettings инкапсулирует некоторые настройки приложения. Похож на синглтон в том смысле, что есть один выделенный экземпляр, доступный из любой точки приложения с помощью статического метода instance. Но при этом можно создавать новые экземпляры этого класса для изменения поведения отдельных видов.
Кратко список настроек:
- Настройки буферов кадра
- Настройки параметров стерео
- Настройки количества потоков для подгрузки данных
osg::DisplaySettings может инициализировать параметры стереорежима на основе переменных окружения или аргументов командной строки. После этого параметры доступны на чтение. Можно также их задавать вручную.
Пример включения сглаживания и буфера трафарета. Эти строки нужно поместить в начало функции main:
osg::DisplaySettings::instance()->setNumMultiSamples(4);
osg::DisplaySettings::instance()->setMinimumNumStencilBits(8);