Введение в OpenSceneGraph: Инкапсуляция машины состояний OpenGL
Как правило, работая с параметрами рендеринга, OpenGL действует как конечный автомат. Состояние рендеринга - это совокупность атрибутов состояния, таких как источники света, материалы, текстуры и режимы отображения, включаемые и выключаемые функциями glEnable() и glDisable(). При установке определенного состояния оно действует до тех пор, пока какая-либо дргуая функция не изменит его. Ковейер OpenGL поддерживает стек состояний для сохранения и восстановления состояний в любой момент времени. Машина состояний предоставляет разработчику полный контроль над текущими и сохраненными в стеке состояниями рендеринга.
Однако такой подход неудобен при работе с OSG. По этой причине машина состояний OpenGL инкапсулирована в классе osg::StateSet, который берет на себя операции по работе со стеком состояний, их установке в процессе обхода графа сцены.
Экземпляр класса osg::StateSet содержит подмножество различных состояний рендеринга и может применять их к узлам сцены osg::Node и отрисовываем геометрическим объектам osg::Drawable с помощью метода setStateSet()
osg::StateSet *stateset = new osg::StateSet;
node->setStateSet(stateset);
Более безопасным способом будет использование метода getOrCreateStateSet(), который гарантирует возврат корректного состояния и присоединение его к узлу или отрисовываемому объекту
osg::StateSet *stateset = node->getOrCreateStateSet();
Классы osg::Node и osg::Drawable управляют переменной-членом osg::StateSet через умный указатель osg::ref_ptr<>. Это означает, что набор состояний может разделятся между несколькими объектами сцены и будет уничтожен только при уничтожении всех этих объектов.
Атрибуты и режимы
В OSG определен класс osg::StateAttribute для хранения атрибутов рендеринга. Это виртуальный базовый класс, который наследуется различными атрибутами рендеринга, такимим как свет, материал и туман.
Режимы рендеринга работаю как переключатели, которые можно включать и выключать. Кроме того, с ними связаны перечислители, которые используют для указания типа режима OpenGL. Иногда режим рендеринга ассоциируется с атрибутом, например режим GL_LIGHTING включает переменные для источников света, отправляемые в конвейер OpenGL когда включен, и выключает освещение в противном случае.
Класс osg::StateSet делит атрибуты и режимы на две групы: текстурные и не текстурные. Он имеет несколько публичных методов для добавления не текстурных атрибутов и режимов в набор состояний:
- setAttribute() - добавляет объект типа osg::StateAttribute к набору состояний. Атрибуты одного типа не могут сосуществовать в одном наборе состояний. Предыдущее заданное значение будет перезаписано новым.
- setMode() - прикрепляет перечислитель режима к набору состояний и устанавливает его зачение в osg::StateAttribute::ON или в osg::StateAttribute::OFF, что обозначает включение или отключение режима.
- setAttributeAndModes() - прикрепляет отрибут рендеринга и ассоциированный с ним режим и задает зачение переключателя (по-умолчанию ON). Слудет иметь в виду, что не каждый атрибут имеет соответствующий режим, но вы можете использовать этот метод в любом случае.
Для установки атрибута и ассоциированного с ним режима можно использовать такой код
stateset->setAttributeAndModes(attr, osg::StateAttribute::ON);
Для установки текстурных аттрибутов необходимо передавать дополнительный параметр, чтобы указать текстуру, к которой он должен быть применен. Для этго osg::StateSet предоставляет несколько других публичных методов, таких как setTextureAttribute(), setTextureMode() и setTextureAttributeAndModes()
stateset->setTextureAttributeAndModes(0, textattr, osg::StateAttribute::ON);
применяет атрибут textattr к теустуре с идентификатором 0.
Задание режима отображения полигонов для узлов сцены
Проилюстрируем вышеописанную теорию практическим примером - изменением режима растеризации полигонов OpenGL, используя класс osg::PolygonMode, наследующий от osg::StateAttribute. Этот класс инкапсулирует функцию glPolygonMode() и предоставляет интерфейс для установки режима отображения полигонов для конкретного узла сцены.
main.h
#ifndef MAIN_H
#define MAIN_H
#include <osg/PolygonMode>
#include <osg/MatrixTransform>
#include <osgDB/ReadFile>
#include <osgViewer/Viewer>
#endif
main.cpp
#include "main.h"
int main(int argc, char *argv[])
{
(void) argc; (void) argv;
osg::ref_ptr<osg::Node> model = osgDB::readNodeFile("../data/cessna.osg");
osg::ref_ptr<osg::MatrixTransform> transform1 = new osg::MatrixTransform;
transform1->setMatrix(osg::Matrix::translate(-25.0f, 0.0f, 0.0f));
transform1->addChild(model.get());
osg::ref_ptr<osg::MatrixTransform> transform2 = new osg::MatrixTransform;
transform2->setMatrix(osg::Matrix::translate(25.0f, 0.0f, 0.0f));
transform2->addChild(model.get());
osg::ref_ptr<osg::PolygonMode> pm = new osg::PolygonMode;
pm->setMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE);
transform1->getOrCreateStateSet()->setAttribute(pm.get());
osg::ref_ptr<osg::Group> root = new osg::Group;
root->addChild(transform1.get());
root->addChild(transform2.get());
osgViewer::Viewer viewer;
viewer.setSceneData(root.get());
return viewer.run();
}
Здесь мы загрзуим модель нашей любимой цессны и применяя к ней трансформации получим два экземпляра модели. К одному из них, тому что слева, применим аттрибут, задающий режим каркасного отображения полигонов
osg::ref_ptr<osg::PolygonMode> pm = new osg::PolygonMode;
pm->setMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE);
transform1->getOrCreateStateSet()->setAttribute(pm.get());
Если обратится к спецификации OpenGL, то можно легко представить себе какие параметры отображения полигонов будут доступны нам при использовании setMode() в данном конкретном случае. Первый параметр может принимать значения osg::PolygonMode::FRONT, BACK и FRONT_AND_BACK, соотвествующие перечислителям OpenGL GL_FRONT, GL_BACK, GL_FRONT_AND_BACK. Второй параметр модет принимать значения osg::PolygonMode::POINT, LINE и FILL, которые соответствуют GL_POINT, GL_LINE и GL_FILL. Никаких других трюков, как это часто бывает при разработке на чистом OpenGL тут применять не нужно - OSG берет на себя большую часть работы. Режим отображения полигонов не имеет связанного режима и не требует вызова пары glEnable()/glDisable(). Метод setAttributeAndModes() будет прекрасно работать и в данном случае, но значение его третьего параметра будет при этом бесполезным.