
Introduccion
Una de las tareas más interesantes resueltas por medio de gráficos tridimensionales es la creación de "grandes mundos": escenas largas que contienen una gran cantidad de objetos con la posibilidad de un movimiento ilimitado por el escenario. La solución a este problema se basa en las limitaciones entendibles inherentes al hardware de la computadora.
Un ejemplo típico: el "gran mundo" cuando se visualiza el ferrocarril en el motor OSG. Todo lo que falta son los langoliers que devoran el mundo detrás del tren ...En este sentido, es necesario administrar los recursos de la aplicación, lo que se reduce a una solución obvia: cargar solo los recursos (modelos, texturas, etc.) que se necesitan para formar una escena en el momento actual con la posición actual del observador; reducción de niveles de detalle de objetos remotos; la descarga de objetos ya no es necesaria desde la memoria del sistema. En su mayor parte, los motores de gráficos y juegos proporcionan un cierto conjunto de herramientas para resolver tales problemas. Hoy miramos cuáles de ellos están disponibles en OpenSceneGraph.
1. Uso de niveles de detalle (LOD)
La técnica de usar niveles de detalle le permite mostrar el mismo objeto con más o menos detalle, dependiendo de la distancia desde el mismo hasta el observador. El uso de esta técnica se basa en la simple consideración de que los pequeños detalles de un modelo tridimensional son indistinguibles en una gran distancia, lo que significa que no hay necesidad de dibujarlos. Por un lado, esta técnica le permite reducir el número total de primitivas geométricas que salen al búfer de cuadros y, por otro lado, no perder el rango de visualización de los objetos de la escena, lo cual es muy útil cuando se crean grandes mundos abiertos.
OSG proporciona herramientas para implementar esta técnica a través de la clase osg :: LOD, heredada del mismo grupo osg ::. Esta clase le permite representar el mismo objeto en varios niveles de detalle. Cada nivel de detalle se caracteriza por una distancia mínima y máxima al observador, en cuya observación se cambia la visualización del objeto en este nivel de detalle.
osg :: LOD le permite especificar este rango inmediatamente al definir un nodo secundario, o posterior, utilizando los métodos setRange ()
osg::ref_ptr<osg::LOD> lodNode = new osg::LOD; lodNode->addChild(node2, 500.0f, FLT_MAX); lodNode->addChild(node1); . . . lodNode->setRange(1, 0.0f, 500.0f);
Continuamos atormentando a Cessna e ilustramos la técnica descrita con un ejemplo
Ejemplo de Lodmain.h #ifndef MAIN_H #define MAIN_H #include <osg/LOD> #include <osgDB/ReadFile> #include <osgUtil/Simplifier> #include <osgViewer/Viewer> #endif
main.h #include "main.h" int main(int argc, char *argv[]) { (void) argc; (void) argv; osg::ref_ptr<osg::Node> modelL3 = osgDB::readNodeFile("../data/cessna.osg"); osg::ref_ptr<osg::Node> modelL2 = dynamic_cast<osg::Node *>(modelL3->clone(osg::CopyOp::DEEP_COPY_ALL)); osg::ref_ptr<osg::Node> modelL1 = dynamic_cast<osg::Node *>(modelL3->clone(osg::CopyOp::DEEP_COPY_ALL)); osgUtil::Simplifier simplifer; simplifer.setSampleRatio(0.5f); modelL2->accept(simplifer); simplifer.setSampleRatio(0.1f); modelL1->accept(simplifer); osg::ref_ptr<osg::LOD> root = new osg::LOD; root->addChild(modelL1.get(), 200.0f, FLT_MAX); root->addChild(modelL2.get(), 50.0f, 200.0f); root->addChild(modelL3.get(), 0.0f, 50.0f); osgViewer::Viewer viewer; viewer.setSceneData(root.get()); return viewer.run(); }
Primero, cargue el modelo
osg::ref_ptr<osg::Node> modelL3 = osgDB::readNodeFile("../data/cessna.osg");
Ahora necesita generar varios modelos (nos limitaremos a dos ejemplos), con un menor nivel de detalle. Para hacer esto, copie el nodo cargado dos veces, utilizando la técnica de la llamada copia "profunda" de la clase, para el nodo implementado por el método clone ()
osg::ref_ptr<osg::Node> modelL2 = dynamic_cast<osg::Node *>(modelL3->clone(osg::CopyOp::DEEP_COPY_ALL)); osg::ref_ptr<osg::Node> modelL1 = dynamic_cast<osg::Node *>(modelL3->clone(osg::CopyOp::DEEP_COPY_ALL));
Ahora reducimos la geometría de estos modelos usando la clase osgUtil :: Simplifer. El grado de simplificación del modelo se establece mediante el método setSampleRatio () de esta clase: cuanto más pequeño sea el parámetro pasado, menos detallado será el modelo después de aplicar el procedimiento de reducción
osgUtil::Simplifier simplifer; simplifer.setSampleRatio(0.5f); modelL2->accept(simplifer); simplifer.setSampleRatio(0.1f); modelL1->accept(simplifer);
Cuando tenemos modelos de diferentes niveles de detalle, podemos cargarlos al nodo raíz, creado como un puntero inteligente para osg :: LOD. Para cada nivel de detalle, configure la distancia de visualización de este nivel
osg::ref_ptr<osg::LOD> root = new osg::LOD; root->addChild(modelL1.get(), 200.0f, FLT_MAX); root->addChild(modelL2.get(), 50.0f, 200.0f); root->addChild(modelL3.get(), 0.0f, 50.0f);
Por FLT_MAX se entiende de alguna manera una "gran distancia" infinita al observador. Después de iniciar el visor, obtenemos la siguiente imagen
Nivel de detalle 3

Nivel de detalle 2

Nivel de detalle 1

Se puede ver que cuando la cámara se aleja del objeto, el detalle de la geometría visualizada disminuye. Con esta técnica, puede lograr un alto realismo de la escena con un bajo consumo de recursos.
2. Técnica de carga de fondo para nodos de escena
El motor OSG proporciona las clases osg :: ProxyNode y osg :: PagedLOD, diseñadas para equilibrar la carga al representar la escena. Ambas clases heredan de osg :: Group.
Un nodo del tipo osg :: ProxyNode reduce el tiempo de inicio de la aplicación antes de renderizar, si la escena tiene una gran cantidad de modelos cargados desde el disco y mostrados. Funciona como una interfaz para archivos externos, lo que permite la carga diferida de modelos. Para agregar nodos secundarios, use el método setFileName () (en lugar de addChild) para establecer el nombre del archivo del modelo en el disco y cargarlo dinámicamente.
El nodo osg :: PagedNode hereda los métodos osg :: LOD y carga y descarga los niveles de detalle de manera que se evite sobrecargar la canalización de OpenGL y garantizar una representación fluida de la escena.
3. Carga dinámica (tiempo de ejecución) del modelo
Veamos cómo ocurre el proceso de cargar el modelo usando osg :: ProxyNode.
Ejemplo de proxynodomain.h #ifndef MAIN_H #define MAIN_H #include <osg/ProxyNode> #include <osgViewer/Viewer> #endif
main.cpp #include "main.h" int main(int argc, char *argv[]) { (void) argc; (void) argv; osg::ref_ptr<osg::ProxyNode> root = new osg::ProxyNode; root->setFileName(0, "../data/cow.osg"); osgViewer::Viewer viewer; viewer.setSceneData(root.get()); return viewer.run(); }
El proceso de descarga aquí es un poco diferente
osg::ref_ptr<osg::ProxyNode> root = new osg::ProxyNode; root->setFileName(0, "../data/cow.osg");
En lugar de cargar explícitamente el modelo de vaca, le indicamos al nodo raíz el nombre del archivo donde se encuentran el modelo y el índice del nodo secundario, donde este modelo debe colocarse después de cargarlo. Al ejecutar el programa, obtenemos este resultado

Se puede ver que el punto de vista no se eligió de la mejor manera: la cámara descansa directamente en el lado del espejo de la vaca. Esto sucedió porque el modelo se cargó después de iniciar el renderizado e inicializar la cámara, cuando el nodo 0 aún no era visible. El espectador simplemente no pudo calcular los parámetros necesarios de la cámara. Sin embargo, el modelo se ha cargado y podemos configurar el modo de visualización manipulando el mouse

¿Qué sucede en el ejemplo anterior? osg :: ProxyNode y osg :: PagedLOD funcionan en este caso como contenedores. El administrador de datos interno de OSG enviará solicitudes y cargará datos en el gráfico de escena a medida que surja la necesidad de archivos de modelo y niveles de detalle.
Este mecanismo funciona en varias secuencias en segundo plano y controla la carga de datos estáticos ubicados en archivos en el disco y datos dinámicos generados y agregados durante la ejecución del programa.
El motor procesa automáticamente los nodos que no se muestran en la ventana gráfica actual y los elimina del gráfico de escena cuando el renderizado está sobrecargado. Sin embargo, este comportamiento no afecta a los nodos osg :: ProxyNode.
Al igual que el nodo proxy, la clase osg :: PagedLOD también tiene un método setFileName () para especificar la ruta al modelo cargado, sin embargo, debe establecer el rango de visibilidad para él, como para el nodo osg :: LOD. Siempre que tengamos un archivo cessna.osg y un modelo low-poly de nivel L1, podemos organizar el nodo paginado de la siguiente manera
osg::ref_ptr<osg::PagedLOD> pagedLOD = new osg::PagedLOD; pagedLOD->addChild(modelL1, 200.0f, FLT_MAX ); pagedLOD->setFileName( 1, "cessna.osg" ); pagedLOD->setRange( 1, 0.0f, 200.0f );
Debe comprender que el nodo modelL1 no se puede descargar de la memoria, ya que este es un nodo secundario no proxy normal.
Al renderizar, la diferencia entre osg :: LOD y osg :: PagedLOD no es visible cuando se usa solo un nivel de detalle del modelo. Una idea interesante sería organizar un gran grupo de modelos Cessna utilizando la clase osg :: MatrixTransform. Para esto, puede usar, por ejemplo, dicha función
osg::Node* createLODNode( const osg::Vec3& pos ) { osg::ref_ptr<osg::PagedLOD> pagedLOD = new osg::PagedLOD; … osg::ref_ptr<osg::MatrixTransform> mt = new osg::MatrixTransform; mt->setMatrix( osg::Matrix::translate(pos) ); mt->addChild( pagedLOD.get() ); return mt.release(); }
Un programa de ejemplo que implementa la carga en segundo plano de 10.000 aviones.
main.h #ifndef MAIN_H #define MAIN_H #include <osg/PagedLOD> #include <osg/MatrixTransform> #include <osgViewer/Viewer> #endif
main.cpp #include "main.h"
Se supone que el avión se ubicará en un avión con un intervalo de 50 unidades de coordenadas. Al cargar, veremos que solo se cargan los cessna que entran en el marco. Esos planos que desaparecen del marco desaparecen del árbol de la escena.

Conclusión
Esta lección de la serie OpenSceneGraph será la última en el formato Cómo. En el transcurso de doce artículos, fue posible ajustar los principios básicos de trabajo y uso de OpenSceneGraph en la práctica. Realmente espero que este motor se haya vuelto más claro para el desarrollador de habla rusa.
Esto no significa que esté cerrando el tema de OpenSceneGraph en el recurso, por el contrario, está previsto dedicar futuros artículos a técnicas y métodos más avanzados de uso de OSG en el desarrollo de aplicaciones gráficas. Pero para esto necesita acumular buen material y procesar muchas fuentes de habla inglesa, y esto lleva tiempo.
Pero no me despido, gracias por su atención y hasta
pronto!