
Introduccion
Como regla general, cuando se trabaja con parámetros de representación, OpenGL actúa como una máquina de estados. Un estado de representación es una colección de atributos de estado, como fuentes de luz, materiales, texturas y modos de visualización, activados y desactivados por las funciones glEnable () y glDisable (). Cuando se establece un cierto estado, permanece vigente hasta que alguna otra función lo cambie. La canalización de OpenGL admite una pila de estados para guardar y restaurar estados en cualquier momento dado. La máquina de estados le da al desarrollador un control completo sobre los estados de representación actuales y guardados en la pila.
Sin embargo, este enfoque es inconveniente cuando se trabaja con OSG. Por esta razón, la máquina de estado OpenGL está encapsulada por la clase osg :: StateSet, que se encarga de trabajar con la pila de estados y configurarlos en el proceso de atravesar el gráfico de escena.
Una instancia de la clase osg :: StateSet contiene un subconjunto de los distintos estados de representación y puede aplicarlos a los nodos de escena osg :: Node y osg :: Objetos geométricos dibujables utilizando el método setStateSet ()
osg::StateSet *stateset = new osg::StateSet; node->setStateSet(stateset);
Una forma más segura sería utilizar el método getOrCreateStateSet (), que garantiza que se devuelva el estado correcto y se adjunte al nodo o al objeto dibujable
osg::StateSet *stateset = node->getOrCreateStateSet();
Las clases osg :: Node y osg :: Drawable controlan la variable miembro osg :: StateSet a través del puntero inteligente osg :: ref_ptr <>. Esto significa que un conjunto de estados se puede dividir entre varios objetos en la escena y se destruirá solo cuando se destruyan todos estos objetos.
1. Atributos y modos
OSG define la clase osg :: StateAttribute para almacenar atributos de representación. Esta es una clase base virtual que es heredada por varios atributos de representación, como la luz, el material y la niebla.
Los modos de representación funcionan como interruptores que se pueden encender y apagar. Además, están asociados con enumeradores, que se utilizan para indicar el tipo de modo OpenGL. A veces, el modo de representación está asociado con un atributo, por ejemplo, el modo GL_LIGHTING incluye variables para las fuentes de luz que se envían a la tubería de OpenGL cuando se enciende, y apaga la iluminación de lo contrario.
La clase osg :: StateSet divide los atributos y modos en dos grupos: textura y no textura. Tiene varios métodos públicos para agregar atributos y modos sin textura a un conjunto de estados:
- setAttribute () - Agrega un objeto de tipo osg :: StateAttribute al conjunto de estados. Los atributos del mismo tipo no pueden coexistir en el mismo conjunto de estados. El punto de ajuste anterior se sobrescribirá con el nuevo.
- setMode (): adjunta un enumerador de modo a un conjunto de estados y establece su valor en osg :: StateAttribute :: ON u osg :: StateAttribute :: OFF, lo que significa habilitar o deshabilitar el modo.
- setAttributeAndModes (): adjunta el atributo de representación y su modo asociado y establece el valor del conmutador (el valor predeterminado es ON). Debe tenerse en cuenta que no todos los atributos tienen un modo correspondiente, pero puede usar este método en cualquier caso.
Para establecer el atributo y su modo asociado, puede usar este código
stateset->setAttributeAndModes(attr, osg::StateAttribute::ON);
Para establecer atributos de textura, se debe pasar un parámetro adicional para indicar la textura a la que se debe aplicar. Osg :: StateSet proporciona varios otros métodos públicos para esto, como setTextureAttribute (), setTextureMode () y setTextureAttributeAndModes ()
stateset->setTextureAttributeAndModes(0, textattr, osg::StateAttribute::ON);
aplica el atributo textattr a la textura con el identificador 0.
2. Configuración del modo de visualización de polígono para nodos de escena
Ilustramos la teoría anterior con un ejemplo práctico: cambiar el modo de rasterización de los polígonos OpenGL usando la clase osg :: PolygonMode, que hereda de osg :: StateAttribute. Esta clase encapsula la función glPolygonMode () y proporciona una interfaz para configurar el modo de visualización de polígonos para un nodo de escena en particular.
Ejemplo de modo poligonalmain.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(); }
Aquí cargamos el modelo de nuestra querida cessna y, al aplicarle transformaciones, obtenemos dos instancias del modelo. A uno de ellos, a la izquierda, aplicamos un atributo que establece el modo de visualización de trama de polígonos
osg::ref_ptr<osg::PolygonMode> pm = new osg::PolygonMode; pm->setMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE); transform1->getOrCreateStateSet()->setAttribute(pm.get());

Si recurrimos a la especificación OpenGL, podemos imaginar fácilmente qué opciones de visualización de polígonos estarán disponibles para nosotros cuando usemos setMode () en este caso particular. El primer parámetro puede tomar los valores osg :: PolygonMode :: FRONT, BACK y FRONT_AND_BACK, correspondientes a los enumeradores de OpenGL GL_FRONT, GL_BACK, GL_FRONT_AND_BACK. El segundo parámetro puede tomar los valores osg :: PolygonMode :: POINT, LINE y FILL, que corresponden a GL_POINT, GL_LINE y GL_FILL. No se necesitan otros trucos, como suele ser el caso cuando se desarrolla en un OpenGL puro, OSG se encarga de la mayor parte del trabajo. El modo de visualización de polígono no tiene modo asociado y no requiere llamar al par glEnable () / glDisable (). El método setAttributeAndModes () funcionará bien en este caso también, pero el valor de su tercer parámetro será inútil.
3. Herencia de los estados de representación. Aplicación de atributos y modos
El conjunto de estados de nodo afecta al nodo actual y a todos sus hijos. Por ejemplo, el atributo osg :: PolygonMode establecido para transform1 del ejemplo anterior se aplicará a todos los elementos secundarios de este nodo. Sin embargo, el nodo secundario puede anular los atributos primarios, es decir, el estado de representación se heredará del nodo primario si el nodo secundario no cambia el comportamiento.
A veces necesita redefinir el comportamiento del nodo en términos del uso de atributos. Por ejemplo, en la mayoría de los editores 3D, el usuario puede cargar varios modelos y cambiar el modo de visualización de todos los modelos cargados al mismo tiempo, independientemente de cómo se mostraron anteriormente. En otras palabras, todos los modelos en el editor deben heredar un solo atributo, independientemente de cómo se establecieron previamente para cada uno de los modelos. En OSG, esto se puede implementar usando el indicador osg :: StateAttribute :: OVERRIDE, por ejemplo
stateset->StateAttribute(attr, osg::StateAttribute::OVERRIDE);
Al configurar modos y modos con atributos, se utiliza el operador OR bit a bit
stateset->StateAttributeAndModes(attr, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
Además, el atributo también puede protegerse de la anulación; para esto, debe marcarse con el indicador osg :: StateAttribute :: PROTECTED.
Hay un tercer indicador, osg :: StateAttribute :: INHERIT, que se utiliza para indicar que este atributo debe heredarse del conjunto de estados del nodo primario.
Aquí hay un breve ejemplo usando las banderas OVERRIDE y PROTECTED. El nodo raíz se establecerá en OVERRIDE para forzar a todos los nodos secundarios a heredar sus atributos y modos. En este caso, los nodos secundarios intentarán cambiar su estado con o sin la ayuda de la bandera PROTEGIDA, lo que conducirá a resultados diferentes.
Heredar texto de ejemplomain.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/glider.osg"); osg::ref_ptr<osg::MatrixTransform> transform1 = new osg::MatrixTransform; transform1->setMatrix(osg::Matrix::translate(-0.5f, 0.0f, 0.0f)); transform1->addChild(model.get()); osg::ref_ptr<osg::MatrixTransform> transform2 = new osg::MatrixTransform; transform2->setMatrix(osg::Matrix::translate(0.5f, 0.0f, 0.0f)); transform2->addChild(model.get()); osg::ref_ptr<osg::Group> root = new osg::Group; root->addChild(transform1.get()); root->addChild(transform2.get()); transform1->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF); transform2->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED); root->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); osgViewer::Viewer viewer; viewer.setSceneData(root.get()); return viewer.run(); }

Para comprender lo que está sucediendo, debe ver cómo se ve un ala delta normalmente iluminada descargando su osgviewer OSG a tiempo completo
$ osgviewer glider.osg
En el ejemplo, estamos tratando de cambiar el modo de iluminación para los nodos transform1 y transform2 apagando completamente la iluminación.
transform1->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF); transform2->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED);
En este caso, activamos el modo de iluminación para el nodo raíz y utilizamos el indicador OVERRIDE para todos sus nodos secundarios, de modo que hereden el estado del nodo raíz. Sin embargo, trnsform2 usa el indicador PROTEGIDO para evitar que la configuración del nodo raíz se vea afectada.
transform2->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED);
Como resultado, a pesar de que apagamos la iluminación en el nodo transform1, el ala delta izquierda todavía está iluminada porque la configuración de la raíz de la escena bloqueó nuestro intento de apagar la iluminación. El ala delta derecha se muestra sin iluminación (se ve más brillante solo porque está inundada de un color simple sin iluminación), ya que transform2 está protegido de heredar los atributos del nodo raíz.
4. Lista de atributos de OpenGL admitidos en OpenSceneGraph
OSG admite casi todos los atributos y modos de representación compatibles con OpenGL a través de clases derivadas de osg :: StateAttribute. La tabla muestra todos los parámetros de la máquina de estado OpenGL disponibles desde el motor.
ID de tipo de atributo | Nombre de la clase | Modo asociado | Función OpenGL equivalente |
---|
ALFEFUNC | osg :: AlphaFunc | GL_ALPHA_TEST | glAlphaFunc () |
BLENDFUNC | osg :: BlendFunc | GL_BLEND | glBlendFunc () y glBlendFuncSeparate () |
CLIPPLANE | osg :: ClipPlane | GL_CLIP_PLANEi (i de 1 a 5) | glClipPlane () |
Máscara de color | osg :: ColorMask | - | glColorMask () |
CULLFACE | osg :: CullFace | GL_CULLFACE | glCullFace () |
Profundidad | osg :: Profundidad | GL_DEPTH_TEST | glDepthFunc (), glDepthRange () y glDepthMask () |
Niebla | osg :: Niebla | GL_FOG | glFog () |
FRENTE | osg :: FrontFace | - | glFrontFace () |
Luz | osg :: Light | GL_LIGHTi (i de 1 a 7) | glLight () |
MODELO LIGERO | osg :: LightModel | - | glLightModel () |
TRIPLE DE LÍNEA | osg :: LineStripple | GL_LINE_STRIPPLE | glLineStripple () |
ANCHO DE LÍNEA | osg :: LineWidth | - | glLineWidht () |
LOGICOP | osg :: LogicOp | GL_COLOR_LOGIC_OP | glLogicOp () |
Material | osg :: Material | - | glMaterial () y glColorMaterial () |
Punto | osg :: Point | GL_POINT_SMOOTH | glPointParameter () |
POINTSPRITE | osg :: PointSprite | GL_POINT_SPRITE_ARB | Funciones para trabajar con sprites OpenGL |
MODO POLIGONO | osg :: PolygonMode | - | glPolygonMode () |
POLYGONOFFSET | osg :: PolygonOffset | GL_POLYGON_OFFSET_POINT | glPolygonOffset () |
TRIPLE DE POLÍGONOS | osg :: PolygonStripple | GL_POLYGON_STRIPPLE | glPolygonStripple () |
TIJERA | osg :: Tijera | GL_SCISSOR_TEST | glScissor () |
SHADEMODEL | osg :: ShadeModel | - | glShadeModel () |
PLANTILLA | osg :: Stencil | GL_STENCIL_TEST | glStencilFunc (), glStencilOp () y glStencilMask () |
Texenv | osg :: TexEnv | - | glTexEnv () |
Texgen | osg :: TexGen | GL_TEXTURE_GEN_S | glTexGen () |
La columna de ID de tipo de atributo indica el identificador OSG específico que identifica este atributo en los enumeradores de la clase osg :: StateAttribute. Se puede usar en el método getAttribute para obtener el valor de un atributo específico.
osg::PolygonMode *pm = dynamic_cast<osg::PolygonMode *>(stateset->getAttribute(osg::StateAttribute::POLYGONMODE));
Un puntero válido indica que el atributo se ha establecido previamente. De lo contrario, el método será NULL. También puede obtener el valor del modo actual usando la llamada
osg::StateAttribute::GLModeValue value = stateset->getMode(GL_LIGHTING);
Aquí el enumerador GL_LIGHTING se usa para habilitar / deshabilitar la iluminación en toda la escena.
5. Aplicar niebla a la modelo en la escena.
Citemos el efecto de niebla como una forma ideal de mostrar cómo trabajar con varios atributos y modos de renderizado. OpenGL usa una ecuación lineal y dos exponenciales que describen el modelo de niebla, soportado por la clase osg :: Fog.
Texto de ejemplo nieblamain.h #ifndef MAIN_H #define MAIN_H #include <osg/Fog> #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::Fog> fog = new osg::Fog; fog->setMode(osg::Fog::LINEAR); fog->setStart(500.0f); fog->setEnd(2500.0f); fog->setColor(osg::Vec4(1.0f, 1.0f, 0.0f, 1.0f)); osg::ref_ptr<osg::Node> model = osgDB::readNodeFile("../data/lz.osg"); model->getOrCreateStateSet()->setAttributeAndModes(fog.get()); osgViewer::Viewer viewer; viewer.setSceneData(model.get()); return viewer.run(); }
Primero, cree el atributo de niebla. Utilizamos un modelo lineal, ajustamos el rango de visualización de niebla por la distancia al modelo
osg::ref_ptr<osg::Fog> fog = new osg::Fog; fog->setMode(osg::Fog::LINEAR); fog->setStart(500.0f); fog->setEnd(2500.0f); fog->setColor(osg::Vec4(1.0f, 1.0f, 0.0f, 1.0f));
Cargamos la muestra horizontal lz.osg y le aplicamos este atributo
osg::ref_ptr<osg::Node> model = osgDB::readNodeFile("../data/lz.osg"); model->getOrCreateStateSet()->setAttributeAndModes(fog.get());
En la ventana del visor, vemos un paisaje borroso, y podemos ver cómo cambia la densidad de la niebla dependiendo de la distancia al modelo.



6. Trabajar con fuentes de luz e iluminación.
Al igual que con OpenGL, OSG admite hasta ocho fuentes de luz para encontrarse directamente con objetos de escena. Al igual que OpenGL, OSG no calcula automáticamente las sombras. Los rayos de luz provienen de fuentes en líneas rectas, se reflejan de los objetos y se dispersan por ellos, después de lo cual son percibidos por los ojos del espectador. Para el procesamiento de alta calidad de la iluminación, es necesario establecer las propiedades del material, la geometría normal de los objetos, etc.
La clase osg :: Light proporciona métodos para controlar las fuentes de luz, incluidos: setLightNum () y getLightNum (): para trabajar con la cantidad de fuentes; setAmbient () y getAmbient () para controlar el componente circundante; setDiffuse () y getDiffuse (): para trabajar con un componente disperso, etc.
OSG también describe la clase osg :: LightSource para agregar fuentes de luz a la escena. Proporciona el método setLight () y es el nodo hoja del gráfico de escena con un solo atributo. Todos los demás nodos del gráfico de escena se ven afectados por la fuente de luz si se establece el modo correspondiente para GL_LIGHTi. Por ejemplo:
Otra solución más conveniente es el método setStateSetModes (), con el cual la fuente de luz con el número deseado se conecta automáticamente al nodo raíz
root->addChild( lightSource.get() ); lightSource->setStateSetModes( root->getOrCreateStateSet(), osg::StateAttribute::ON );
Puede agregar nodos secundarios a la fuente de luz, pero esto no significa en absoluto, iluminará el subgráfico asociado de alguna manera especial. Se procesará como geometría, representada por la forma física de la fuente de luz.
El nodo osg :: LightSource se puede conectar al nodo de transformación y, por ejemplo, una fuente de luz puntual se puede mover en el espacio. Esto puede deshabilitarse configurando el sistema de coordenadas absoluto para la fuente de luz.
lightSource->setReferenceFrame( osg::LightSource::ABSOLUTE_RF );
7. Crear fuentes de luz en la escena.
De manera predeterminada, OSG establece automáticamente la fuente de luz en el número 0, que emite luz direccional uniforme a la escena. Sin embargo, en cualquier momento puede agregar varias fuentes de luz adicionales e incluso controlarlas utilizando los nodos de transformación de coordenadas. Solo se pueden mover las fuentes posicionales (fuentes puntuales). La luz direccional solo tiene dirección (una corriente de rayos paralelos provenientes del infinito) y no está vinculada a una posición específica en el escenario. OpenGL y OSG usan el cuarto componente del parámetro de posición para especificar el tipo de fuente de luz. Si es 0, entonces la luz se considera como dirigida; con un valor de 1 - posicional.
Considere un pequeño ejemplo de trabajo con iluminación.
Encabezado de spoilermain.h #ifndef MAIN_H #define MAIN_H #include <osg/MatrixTransform> #include <osg/LightSource> #include <osgDB/ReadFile> #include <osgViewer/Viewer> #endif
main.cpp #include "main.h"
Para crear una fuente de luz, tenemos una función separada
osg::Node *createLightSource(int num, const osg::Vec3 &trans, const osg::Vec4 &color) { osg::ref_ptr<osg::Light> light = new osg::Light; light->setLightNum(num); light->setDiffuse(color); light->setPosition(osg::Vec4(0.0f, 0.0f, 0.0f, 1.0f)); osg::ref_ptr<osg::LightSource> lightSource = new osg::LightSource; lightSource->setLight(light); osg::ref_ptr<osg::MatrixTransform> sourceTrans = new osg::MatrixTransform; sourceTrans->setMatrix(osg::Matrix::translate(trans)); sourceTrans->addChild(lightSource.get()); return sourceTrans.release(); }
En esta función, primero determinamos los parámetros de iluminación dados por la fuente, creando así el atributo GL_LIGHTi
osg::ref_ptr<osg::Light> light = new osg::Light;
Después de eso, se crea una fuente de luz a la que se asigna este atributo.
osg::ref_ptr<osg::LightSource> lightSource = new osg::LightSource; lightSource->setLight(light);
Creamos y configuramos el nodo de transformación, pasándole nuestra fuente de luz como un nodo hijo
osg::ref_ptr<osg::MatrixTransform> sourceTrans = new osg::MatrixTransform; sourceTrans->setMatrix(osg::Matrix::translate(trans)); sourceTrans->addChild(lightSource.get());
Devuelve un puntero al nodo de transformación
return sourceTrans.release();
En el cuerpo del programa principal, cargamos un modelo tridimensional (de nuevo, nuestro cessna favorito)
osg::ref_ptr<osg::Node> model = osgDB::readNodeFile("../data/cessna.osg"); osg::ref_ptr<osg::Group> root = new osg::Group; root->addChild(model.get());
Creamos dos fuentes de luz con los números 0 y 1. La primera brillará en amarillo, la segunda - azul-verde
osg::Node *light0 = createLightSource(0, osg::Vec3(-20.0f, 0.0f, 0.0f), osg::Vec4(1.0f, 1.0f, 0.0f, 1.0f)); osg::Node *light1 = createLightSource(1, osg::Vec3(0.0f, -20.0f, 0.0f), osg::Vec4(0.0f, 1.0f, 1.0f, 1.0f));
Informamos a la máquina de estado de OpenGL que es necesario encender las fuentes de luz 0 y 1 y agregar las fuentes que creamos a la escena.
root->getOrCreateStateSet()->setMode(GL_LIGHT0, osg::StateAttribute::ON); root->getOrCreateStateSet()->setMode(GL_LIGHT1, osg::StateAttribute::ON); root->addChild(light0); root->addChild(light1);
Después de inicializar e iniciar el visor, obtenemos una imagen

Conclusión
Muy conmovido por la atención de las personas interesadas a este ciclo. Esta acción no comenzó mucho, pero creo que la comunidad necesita artículos. Gracias por todo tipo de comentarios positivos.
Hoy traté de considerar, nuevamente, las cosas bastante básicas del motor OSG. No estoy seguro de lo que salió genial. Pero hasta ahora estoy exponiendo cosas precisamente primitivas, en la línea en que yo mismo las entendí. Revisé todos los ejemplos personalmente, mi repositorio está disponible
aquí . Gracias, queridos colegas, intentaré que esta historia
continúe ...