OpenSceneGraph: Conceptos básicos de textura

imagen

Introduccion


Ya hemos examinado un ejemplo en el que pintaron un cuadrado con todos los colores del arcoíris. Sin embargo, existe otra tecnología, a saber, la aplicación a la geometría tridimensional del llamado mapa de textura o simplemente textura: una imagen ráster bidimensional. En este caso, el efecto no está en los vértices de la geometría, sino que se modifican los datos de todos los píxeles obtenidos cuando se rasteriza la escena. Esta técnica puede aumentar significativamente el realismo y los detalles de la imagen final.

OSG admite varios atributos de textura y modos de textura. Pero, antes de hablar sobre texturas, hablemos sobre cómo OSG maneja las imágenes de mapa de bits. Para trabajar con imágenes ráster, se proporciona una clase especial: osg :: Image, que almacena los datos de la imagen en su interior, destinados, en última instancia, a texturizar el objeto.

1. Presentación de datos de imagen ráster. Clase Osg :: Imagen


La mejor manera de cargar una imagen desde el disco es usar la llamada osgDB :: readImageFile (). Es muy similar a la llamada osg :: readNodeFile () que ya nos ha aburrido. Si tenemos un mapa de bits llamado picture.bmp, entonces cargarlo se verá así

osg::ref_ptr<osg::Image> image = osgDB::readImageFile("picture.bmp"); 

Si la imagen se carga correctamente, el puntero será válido; de lo contrario, la función devolverá NULL. Después de la descarga, podemos obtener información de la imagen utilizando los siguientes métodos públicos

  1. t (), s () y r (): devuelve el ancho, la altura y la profundidad de la imagen.
  2. data (): devuelve un puntero de tipo unsigned char * a los datos de la imagen "en bruto". A través de este puntero, el desarrollador puede actuar directamente sobre los datos de la imagen. Puede hacerse una idea del formato de datos de imagen utilizando los métodos getPixalFormat () y getDataType (). Los valores devueltos por ellos son equivalentes a los parámetros del formato y tipo de las funciones de OpenGL glTexImage * (). Por ejemplo, si la imagen tiene el formato de píxel GL_RGB y el tipo es GL_UNSIGNED_BYTE, se utilizan tres elementos independientes (bytes sin signo) para representar el componente de color RGB



Puede crear un nuevo objeto de imagen y asignarle memoria.

 osg::ref_ptr<osg::Image> image = new osg::Image; image->allocateImage(s, t, r, GL_RGB, GL_UNSIGNED_BYTE); unsigned char *ptr = image->data(); //         

Aquí s, t, r son tamaños de imagen; GL_RGB establece el formato de píxel y GL_UNSIGNED_BYTE establece el tipo de datos para describir un solo componente de color. Se asigna un búfer de datos interno del tamaño requerido en la memoria y se destruye automáticamente si no hay un solo enlace a esta imagen.

El sistema de complemento OSG admite la descarga de casi todos los formatos de imagen populares: * .jpg, * .bmp, * .png, * .tif, etc. Esta lista es fácil de expandir escribiendo su propio complemento, pero este es un tema para otra discusión.

2. Los fundamentos de la textura


Para aplicar textura a un modelo tridimensional, debe realizar una serie de pasos:

  1. Defina las coordenadas de textura de los vértices para el objeto geométrico (en el entorno de los diseñadores 3D, esto se llama escaneo UV).
  2. Cree un objeto de atributo de textura para 1D, 2D, 3D o textura cúbica.
  3. Establezca una o más imágenes para un atributo de textura.
  4. Adjunte un atributo y modo de textura al conjunto de estados aplicados al objeto que se está dibujando.

OSG define la clase osg :: Texture, que encapsula todo tipo de texturas. Las subclases osg :: Texture1D, osg :: Texture2D, osg :: Texture3D y osg :: TextureCubeMap se heredan de él, lo que representa varias técnicas de texturas adoptadas en OpenGL.

El método más común de la clase osg :: Texture es setImage (), que establece la imagen utilizada en la textura, por ejemplo

 osg::ref_ptr<osg::Image> image = osgDB::readImageFile("picture.bmp"); osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D; texture->setImage(image.get()); 

o puede pasar el objeto de imagen directamente al constructor de la clase de textura

 osg::ref_ptr<osg::Image> image = osgDB::readImageFile("picture.bmp"); osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D(image.get()); 

La imagen se puede recuperar del objeto de textura llamando al método getImage ().

Otro punto importante es establecer las coordenadas de textura para cada vértice en el objeto osg :: Geometry. La transferencia de estas coordenadas ocurre a través de la matriz osg :: Vec2Array y osg :: Vec3Array llamando al método setTexCoordArray ().

Después de establecer las coordenadas de textura, necesitamos establecer el número de ranura de textura (unidad), ya que OSG admite la superposición de múltiples texturas en la misma geometría. Cuando se usa una textura única, el número de unidad es siempre 0. Por ejemplo, el siguiente código ilustra la configuración de las coordenadas de textura para la unidad 0 de la geometría

 osf::ref_ptr<osg::Vec2Array> texcoord = new osg::Vec2Array; texcoord->push_back( osg::Vec2(...) ); ... geom->setTexCoordArray(0, texcoord.get()); 

Después de eso, podemos agregar el atributo de textura al conjunto de estados, activando automáticamente el modo de textura correspondiente (en nuestro ejemplo GL_TEXTURE_2D) y aplicar el atributo a la geometría o nodo que contiene esta geometría

 geom->getOrCreateStateSet()->setTextureAttributeAndModes(texture.get()); 

Tenga en cuenta que OpenGL gestiona los datos de la imagen en la memoria gráfica de la tarjeta de video, pero el objeto osg :: Image junto con los mismos datos se encuentra en la memoria del sistema. Como resultado, encontraremos el hecho de que hemos almacenado dos copias de los mismos datos, ocupando la memoria del proceso. Si esta imagen no es compartida por varios atributos de textura, se puede eliminar de la memoria del sistema inmediatamente después de que OpenGL la transfiera a la memoria del adaptador de video. La clase osg :: Texture proporciona el método apropiado para habilitar esta función.

 texture->setUnRefImageDataAfterApply( true ); 

3. Cargue y aplique una textura 2D


La técnica más utilizada es el texturizado 2D: superposición de una imagen (o imágenes) bidimensional en el borde de una superficie tridimensional. Considere el ejemplo más simple de aplicar una textura única a un polígono cuadrangular

Ejemplo de textura
main.h

 #ifndef MAIN_H #define MAIN_H #include <osg/Texture2D> #include <osg/Geometry> #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::Vec3Array> vertices = new osg::Vec3Array; vertices->push_back( osg::Vec3(-0.5f, 0.0f, -0.5f) ); vertices->push_back( osg::Vec3( 0.5f, 0.0f, -0.5f) ); vertices->push_back( osg::Vec3( 0.5f, 0.0f, 0.5f) ); vertices->push_back( osg::Vec3(-0.5f, 0.0f, 0.5f) ); osg::ref_ptr<osg::Vec3Array> normals = new osg::Vec3Array; normals->push_back( osg::Vec3(0.0f, -1.0f, 0.0f) ); osg::ref_ptr<osg::Vec2Array> texcoords = new osg::Vec2Array; texcoords->push_back( osg::Vec2(0.0f, 0.0f) ); texcoords->push_back( osg::Vec2(0.0f, 1.0f) ); texcoords->push_back( osg::Vec2(1.0f, 1.0f) ); texcoords->push_back( osg::Vec2(1.0f, 0.0f) ); osg::ref_ptr<osg::Geometry> quad = new osg::Geometry; quad->setVertexArray(vertices.get()); quad->setNormalArray(normals.get()); quad->setNormalBinding(osg::Geometry::BIND_OVERALL); quad->setTexCoordArray(0, texcoords.get()); quad->addPrimitiveSet( new osg::DrawArrays(GL_QUADS, 0, 4) ); osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D; osg::ref_ptr<osg::Image> image = osgDB::readImageFile("../data/Images/lz.rgb"); texture->setImage(image.get()); osg::ref_ptr<osg::Geode> root = new osg::Geode; root->addDrawable(quad.get()); root->getOrCreateStateSet()->setTextureAttributeAndModes(0, texture.get()); osgViewer::Viewer viewer; viewer.setSceneData(root.get()); return viewer.run(); } 


Crea una matriz de vértices y normales a la cara

 osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array; vertices->push_back( osg::Vec3(-0.5f, 0.0f, -0.5f) ); vertices->push_back( osg::Vec3( 0.5f, 0.0f, -0.5f) ); vertices->push_back( osg::Vec3( 0.5f, 0.0f, 0.5f) ); vertices->push_back( osg::Vec3(-0.5f, 0.0f, 0.5f) ); osg::ref_ptr<osg::Vec3Array> normals = new osg::Vec3Array; normals->push_back( osg::Vec3(0.0f, -1.0f, 0.0f) ); 

Crea una matriz de coordenadas de textura

 osg::ref_ptr<osg::Vec2Array> texcoords = new osg::Vec2Array; texcoords->push_back( osg::Vec2(0.0f, 0.0f) ); texcoords->push_back( osg::Vec2(0.0f, 1.0f) ); texcoords->push_back( osg::Vec2(1.0f, 1.0f) ); texcoords->push_back( osg::Vec2(1.0f, 0.0f) ); 

El punto es que cada vértice del modelo tridimensional corresponde a un punto en la textura bidimensional, y las coordenadas de los puntos en la textura son relativas: están normalizadas al ancho y la altura reales de la imagen. Queremos estirar toda la imagen cargada en un cuadrado, respectivamente, las esquinas del cuadrado corresponderán a los puntos de textura (0, 0), (0, 1), (1, 1) y (1, 0). El orden de los vértices en la matriz de vértices debe coincidir con el orden de los vértices de textura.

Luego, crea un cuadrado, asignando a la geometría una matriz de vértices y una matriz de normales

 osg::ref_ptr<osg::Geometry> quad = new osg::Geometry; quad->setVertexArray(vertices.get()); quad->setNormalArray(normals.get()); quad->setNormalBinding(osg::Geometry::BIND_OVERALL); quad->setTexCoordArray(0, texcoords.get()); quad->addPrimitiveSet( new osg::DrawArrays(GL_QUADS, 0, 4) ); 

Cree un objeto de textura y cargue la imagen utilizada para ello.

 osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D; osg::ref_ptr<osg::Image> image = osgDB::readImageFile("../data/Images/lz.rgb"); texture->setImage(image.get()); 

Crea el nodo raíz de la escena y coloca la geometría que creamos allí

 osg::ref_ptr<osg::Geode> root = new osg::Geode; root->addDrawable(quad.get()); 

y finalmente aplique el atributo de textura al nodo en el que se coloca la geometría

 root->getOrCreateStateSet()->setTextureAttributeAndModes(0, texture.get()); 



La clase osg :: Texture2D determina si los tamaños de imagen de textura son múltiplos de una potencia de dos (por ejemplo, 64x64 o 256x512), escalando automáticamente las imágenes que no son apropiadas para el tamaño, en realidad usando la función OpenGL gluScaleImage (). Existe un método setResizeNonPowerOfTwoHint () que determina si se debe cambiar el tamaño de la imagen o no. Algunas tarjetas de video requieren un múltiplo del tamaño de una imagen de la potencia de dos, mientras que la clase osg :: Texture2D admite trabajar con un tamaño de textura arbitrario.

Algo sobre la mezcla de texturas


Como ya dijimos, las coordenadas de textura se normalizan de 0 a 1. El punto (0, 0) corresponde a la esquina superior izquierda de la imagen, y el punto (1, 1) corresponde a la esquina inferior derecha. ¿Qué sucede si establece coordenadas de textura mayores que uno?

Por defecto, en OpenGL, como en OSG, la textura se repetirá en la dirección del eje, el valor de la coordenada de textura excederá uno. Esta técnica se usa a menudo, por ejemplo, para crear un modelo de una pared de ladrillo larga, utilizo una textura pequeña, repitiendo su superposición muchas veces tanto en ancho como en altura.

Este comportamiento se puede controlar mediante el método setWrap () de la clase osg :: Texture. Como primer parámetro, el método toma el identificador del eje al que debe aplicarse el modo de mezcla, pasado como el segundo parámetro, por ejemplo

 //     s texture->setWrap( osg::Texture::WRAP_S, osg::Texture::REPEAT ); //     r texture->setWrap( osg::Texture::WRAP_R, osg::Texture::REPEAT ); 

Este código le dice explícitamente al motor que repita la textura a lo largo de los ejes syr si los valores de coordenadas de textura exceden 1. Una lista completa de modos de mapeo de textura:

  1. REPETIR - repite la textura.
  2. ESPEJO - repita la textura, reflejándola.
  3. CLAMP_TO_EDGE: las coordenadas que van más allá de 0 a 1 se ajustan al borde correspondiente de la textura.
  4. CLAMP_TO_BORDER: las coordenadas que van más allá de 0 a 1 proporcionarán el color del borde establecido por el usuario.

4. Renderizado a textura


La técnica de representación de texturas permite al desarrollador crear una textura basada en una subescena o modelo tridimensional y aplicarla a la superficie de la escena principal. Una tecnología similar a menudo se llama textura "hornear".

Para hornear dinámicamente una textura, debe completar tres pasos:

  1. Cree un objeto de textura para representarlo.
  2. Renderiza la escena en una textura.
  3. Use la textura resultante según lo previsto.

Debemos crear un objeto de textura vacío. OSG le permite crear una textura vacía de un tamaño determinado. El método setTextureSize () le permite establecer el ancho y el alto de la textura, así como la profundidad como un parámetro adicional (para texturas 3D).

Para renderizar a una textura, debe adjuntarla al objeto de la cámara llamando al método attach (), que toma un objeto de textura como argumento. Además, este método acepta un argumento que indica qué parte del búfer de trama se debe representar en esta textura. Por ejemplo, para transferir un búfer de color a una textura, debe ejecutar el siguiente código

 camera->attach( osg::Camera::COLOR_BUFFER, texture.get() ); 

Otras partes del búfer de trama disponibles para renderizar incluyen el búfer de profundidad DEPTH_BUFFER, el búfer de plantilla STENCIL_BUFFER y los búferes de color adicionales de COLOR_BUFFER0 a COLOR_BUFFER15. La presencia de buffers de color adicionales y su número está determinado por el modelo de la tarjeta de video.

Además, para una cámara que se convierte en una textura, debe establecer los parámetros de la matriz de proyección y la vista, cuyo tamaño corresponde al tamaño de la textura. La textura se actualizará a medida que se dibuja cada cuadro. Tenga en cuenta que la cámara principal no debe usarse para renderizar en una textura, ya que proporciona la representación de la escena principal y solo obtiene una pantalla en negro. Es posible que este requisito no se cumpla solo cuando realice el procesamiento fuera de la pantalla.

5. Un ejemplo de la implementación de renderizado a textura


Para demostrar la técnica de renderizado en una textura, implementamos la siguiente tarea: crear un cuadrado, dibujar una textura cuadrada en él y renderizar una escena animada en la textura, por supuesto con el cessna que amamos. El programa que implementa el ejemplo es bastante voluminoso. Sin embargo, le daré su código fuente completo de todos modos.

Ejemplo de Texrender
main.h

 #ifndef MAIN_H #define MAIN_H #include <osg/Camera> #include <osg/Texture2D> #include <osg/MatrixTransform> #include <osgDB/ReadFile> #include <osgGA/TrackballManipulator> #include <osgViewer/Viewer> #endif 

main.cpp

 #include "main.h" //------------------------------------------------------------------------------ // //------------------------------------------------------------------------------ osg::Geometry *createQuad(const osg::Vec3 &pos, float w, float h) { osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array; vertices->push_back( pos + osg::Vec3( w / 2, 0.0f, -h / 2) ); vertices->push_back( pos + osg::Vec3( w / 2, 0.0f, h / 2) ); vertices->push_back( pos + osg::Vec3(-w / 2, 0.0f, h / 2) ); vertices->push_back( pos + osg::Vec3(-w / 2, 0.0f, -h / 2) ); osg::ref_ptr<osg::Vec3Array> normals = new osg::Vec3Array; normals->push_back(osg::Vec3(0.0f, -1.0f, 0.0f)); osg::ref_ptr<osg::Vec2Array> texcoords = new osg::Vec2Array; texcoords->push_back( osg::Vec2(1.0f, 1.0f) ); texcoords->push_back( osg::Vec2(1.0f, 0.0f) ); texcoords->push_back( osg::Vec2(0.0f, 0.0f) ); texcoords->push_back( osg::Vec2(0.0f, 1.0f) ); osg::ref_ptr<osg::Geometry> quad = new osg::Geometry; quad->setVertexArray(vertices.get()); quad->setNormalArray(normals.get()); quad->setNormalBinding(osg::Geometry::BIND_OVERALL); quad->setTexCoordArray(0, texcoords.get()); quad->addPrimitiveSet(new osg::DrawArrays(GL_QUADS, 0, 4)); return quad.release(); } //------------------------------------------------------------------------------ // //------------------------------------------------------------------------------ int main(int argc, char *argv[]) { (void) argc; (void) argv; osg::ref_ptr<osg::Node> sub_model = osgDB::readNodeFile("../data/cessna.osg"); osg::ref_ptr<osg::MatrixTransform> transform1 = new osg::MatrixTransform; transform1->setMatrix(osg::Matrix::rotate(0.0, osg::Vec3(0.0f, 0.0f, 1.0f))); transform1->addChild(sub_model.get()); osg::ref_ptr<osg::Geode> model = new osg::Geode; model->addChild(createQuad(osg::Vec3(0.0f, 0.0f, 0.0f), 2.0f, 2.0f)); int tex_widht = 1024; int tex_height = 1024; osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D; texture->setTextureSize(tex_widht, tex_height); texture->setInternalFormat(GL_RGBA); texture->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::LINEAR); texture->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::LINEAR); model->getOrCreateStateSet()->setTextureAttributeAndModes(0, texture.get()); osg::ref_ptr<osg::Camera> camera = new osg::Camera; camera->setViewport(0, 0, tex_widht, tex_height); camera->setClearColor(osg::Vec4(1.0f, 1.0f, 1.0f, 1.0f)); camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); camera->setRenderOrder(osg::Camera::PRE_RENDER); camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT); camera->attach(osg::Camera::COLOR_BUFFER, texture.get()); camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF); camera->addChild(transform1.get()); osg::ref_ptr<osg::Group> root = new osg::Group; root->addChild(model.get()); root->addChild(camera.get()); osgViewer::Viewer viewer; viewer.setSceneData(root.get()); viewer.setCameraManipulator(new osgGA::TrackballManipulator); viewer.setUpViewOnSingleScreen(0); camera->setProjectionMatrixAsPerspective(30.0, static_cast<double>(tex_widht) / static_cast<double>(tex_height), 0.1, 1000.0); float dist = 100.0f; float alpha = 10.0f * 3.14f / 180.0f; osg::Vec3 eye(0.0f, -dist * cosf(alpha), dist * sinf(alpha)); osg::Vec3 center(0.0f, 0.0f, 0.0f); osg::Vec3 up(0.0f, 0.0f, -1.0f); camera->setViewMatrixAsLookAt(eye, center, up); float phi = 0.0f; float delta = -0.01f; while (!viewer.done()) { transform1->setMatrix(osg::Matrix::rotate(static_cast<double>(phi), osg::Vec3(0.0f, 0.0f, 1.0f))); viewer.frame(); phi += delta; } return 0; } 


Para crear un cuadrado, escriba una función libre separada

 osg::Geometry *createQuad(const osg::Vec3 &pos, float w, float h) { osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array; vertices->push_back( pos + osg::Vec3( w / 2, 0.0f, -h / 2) ); vertices->push_back( pos + osg::Vec3( w / 2, 0.0f, h / 2) ); vertices->push_back( pos + osg::Vec3(-w / 2, 0.0f, h / 2) ); vertices->push_back( pos + osg::Vec3(-w / 2, 0.0f, -h / 2) ); osg::ref_ptr<osg::Vec3Array> normals = new osg::Vec3Array; normals->push_back(osg::Vec3(0.0f, -1.0f, 0.0f)); osg::ref_ptr<osg::Vec2Array> texcoords = new osg::Vec2Array; texcoords->push_back( osg::Vec2(1.0f, 1.0f) ); texcoords->push_back( osg::Vec2(1.0f, 0.0f) ); texcoords->push_back( osg::Vec2(0.0f, 0.0f) ); texcoords->push_back( osg::Vec2(0.0f, 1.0f) ); osg::ref_ptr<osg::Geometry> quad = new osg::Geometry; quad->setVertexArray(vertices.get()); quad->setNormalArray(normals.get()); quad->setNormalBinding(osg::Geometry::BIND_OVERALL); quad->setTexCoordArray(0, texcoords.get()); quad->addPrimitiveSet(new osg::DrawArrays(GL_QUADS, 0, 4)); return quad.release(); } 

La función acepta la posición del centro del cuadrado y sus dimensiones geométricas como entrada. A continuación, se crea una matriz de vértices, una matriz de normales y coordenadas de textura, después de lo cual la geometría creada se devuelve de la función.

En el cuerpo del programa principal, cargue el modelo de cessna

 osg::ref_ptr<osg::Node> sub_model = osgDB::readNodeFile("../data/cessna.osg"); 

Para animar este modelo, cree e inicialice la transformación de rotación alrededor del eje Z

 osg::ref_ptr<osg::MatrixTransform> transform1 = new osg::MatrixTransform; transform1->setMatrix(osg::Matrix::rotate(0.0, osg::Vec3(0.0f, 0.0f, 1.0f))); transform1->addChild(sub_model.get()); 

Ahora cree un modelo para la escena principal: un cuadrado en el que representaremos

 osg::ref_ptr<osg::Geode> model = new osg::Geode; model->addChild(createQuad(osg::Vec3(0.0f, 0.0f, 0.0f), 2.0f, 2.0f)); 

Cree una textura vacía para un cuadrado de 1024x1024 píxeles con un formato de píxel RGBA (color de tres componentes de 32 bits con canal alfa)

 int tex_widht = 1024; int tex_height = 1024; osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D; texture->setTextureSize(tex_widht, tex_height); texture->setInternalFormat(GL_RGBA); texture->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::LINEAR); texture->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::LINEAR); 

Aplica esta textura al modelo cuadrado.

 model->getOrCreateStateSet()->setTextureAttributeAndModes(0, texture.get()); 

Luego crea una cámara que hornee la textura

 osg::ref_ptr<osg::Camera> camera = new osg::Camera; camera->setViewport(0, 0, tex_widht, tex_height); camera->setClearColor(osg::Vec4(1.0f, 1.0f, 1.0f, 1.0f)); camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 

La vista de la cámara tiene el mismo tamaño que la textura. Además, no olvide establecer el color de fondo al limpiar la pantalla y la máscara de limpieza, lo que indica que debe borrar tanto el búfer de color como el búfer de profundidad. Luego, configure la cámara para renderizar a textura

 camera->setRenderOrder(osg::Camera::PRE_RENDER); camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT); camera->attach(osg::Camera::COLOR_BUFFER, texture.get()); 

El orden de renderizado de PRE_RENDER indica que esta cámara está renderizando antes de renderizar a la escena principal. Especifique el FBO como el objetivo de representación y adjunte nuestra textura a la cámara. Ahora configuramos la cámara para que funcione en un sistema de coordenadas absoluto y, como escena, configuramos nuestro subárbol, que queremos convertir en una textura: transformación de rotación con un modelo Cessna adjunto

 camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF); camera->addChild(transform1.get()); 

Cree un nodo de grupo raíz agregando el modelo principal (cuadrado) y una textura de procesamiento de cámara.

 osg::ref_ptr<osg::Group> root = new osg::Group; root->addChild(model.get()); root->addChild(camera.get()); 

Crea y personaliza un visor

 osgViewer::Viewer viewer; viewer.setSceneData(root.get()); viewer.setCameraManipulator(new osgGA::TrackballManipulator); viewer.setUpViewOnSingleScreen(0); 

Configure la matriz de proyección para la cámara: una proyección en perspectiva a través de los parámetros de la pirámide de recorte

 camera->setProjectionMatrixAsPerspective(30.0, static_cast<double>(tex_widht) / static_cast<double>(tex_height), 0.1, 1000.0); 

Configuramos una matriz de vista que establece la posición de la cámara en el espacio con respecto al origen de la subcesa

 float dist = 100.0f; float alpha = 10.0f * 3.14f / 180.0f; osg::Vec3 eye(0.0f, -dist * cosf(alpha), dist * sinf(alpha)); osg::Vec3 center(0.0f, 0.0f, 0.0f); osg::Vec3 up(0.0f, 0.0f, -1.0f); camera->setViewMatrixAsLookAt(eye, center, up); 

Finalmente, anime y muestre la escena, cambiando el ángulo de rotación de la aeronave alrededor del eje Z en cada cuadro

 float phi = 0.0f; float delta = -0.01f; while (!viewer.done()) { transform1->setMatrix(osg::Matrix::rotate(static_cast<double>(phi), osg::Vec3(0.0f, 0.0f, 1.0f))); viewer.frame(); phi += delta; } 

Como resultado, obtenemos una imagen bastante interesante



En este ejemplo, implementamos algunas animaciones de escena, pero recuerde que expandir el ciclo run () y cambiar los parámetros de representación antes o después de representar el cuadro es una actividad insegura en términos de organizar el acceso a los datos de diferentes flujos. Dado que OSG utiliza la representación de subprocesos múltiples, también existen mecanismos regulares para incorporar sus propias acciones en el proceso de representación, que proporcionan acceso seguro a los datos.

6. Guardar el resultado de la representación en un archivo


OSG admite la capacidad de adjuntar un objeto osg :: Image a la cámara y guardar el contenido del búfer de cuadros en el búfer de datos de imagen. Después de eso, es posible guardar estos datos en el disco usando la función osg :: writeImageFile ()

 osg::ref_ptr<osg::Image> image = new osg::Image; image->allocateImage( width, height, 1, GL_RGBA, GL_UNSIGNED_BYTE ); camera->attach( osg::Camera::COLOR_BUFFER, image.get() ); ... osgDB::writeImageFile( *image, "saved_image.bmp" ); 

Conclusión


Quizás el material presentado en el artículo parece trivial. Sin embargo, describe los conceptos básicos de trabajar con texturas en OpenSceneGraph, en el que se basan técnicas más complejas para trabajar con este motor, de las que definitivamente hablaremos en el futuro.

Continuará ...

Source: https://habr.com/ru/post/437624/


All Articles