OpenSceneGraph: principes de base des textures

image

Présentation


Nous avons déjà considéré un exemple où ils ont peint un carré de toutes les couleurs de l'arc-en-ciel. Néanmoins, il existe une autre technologie, à savoir l'application à la géométrie tridimensionnelle de la soi-disant texture map ou simplement texture - une image raster bidimensionnelle. Dans ce cas, l'effet n'est pas sur les sommets de la géométrie, mais les données de tous les pixels obtenus lors de la pixellisation de la scène changent. Cette technique peut augmenter considérablement le réalisme et les détails de l'image finale.

OSG prend en charge plusieurs attributs de texture et modes de texturation. Mais, avant de parler des textures, parlons de la façon dont OSG gère les images bitmap. Pour travailler avec des images raster, une classe spéciale est fournie - osg :: Image, qui stocke les données d'image à l'intérieur, destinées, en fin de compte, à texturer l'objet.

1. Présentation des données d'image raster. Classe Osg :: Image


La meilleure façon de charger une image à partir du disque est d'utiliser l'appel osgDB :: readImageFile (). Il est très similaire à l'appel osg :: readNodeFile (), qui nous a déjà ennuyés. Si nous avons un bitmap nommé picture.bmp, son chargement ressemblera à ceci

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

Si l'image est correctement chargée, le pointeur sera valide, sinon la fonction renverra NULL. Après le téléchargement, nous pouvons obtenir des informations sur l'image en utilisant les méthodes publiques suivantes

  1. t (), s () et r () - renvoient la largeur, la hauteur et la profondeur de l'image.
  2. data () - renvoie un pointeur de type unsigned char * sur les données d'image "brutes". Grâce à ce pointeur, le développeur peut agir directement sur les données d'image. Vous pouvez vous faire une idée du format des données d'image à l'aide des méthodes getPixalFormat () et getDataType (). Les valeurs retournées par eux sont équivalentes aux paramètres du format et du type des fonctions OpenGL glTexImage * (). Par exemple, si l'image a le format de pixels GL_RGB et le type est GL_UNSIGNED_BYTE, alors trois éléments indépendants (octets non signés) sont utilisés pour représenter la composante de couleur RVB



Vous pouvez créer un nouvel objet image et lui allouer de la mémoire.

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

Ici s, t, r sont des tailles d'image; GL_RGB définit le format de pixel et GL_UNSIGNED_BYTE définit le type de données pour décrire un composant de couleur unique. Un tampon de données interne de la taille requise est alloué en mémoire et est automatiquement détruit s'il n'y a pas un seul lien vers cette image.

Le système de plug-in OSG prend en charge le téléchargement de presque tous les formats d'image populaires: * .jpg, * .bmp, * .png, * .tif, etc. Cette liste est facile à développer en écrivant votre propre plugin, mais c'est un sujet pour une autre discussion.

2. Les bases de la texturation


Pour appliquer une texture à un modèle tridimensionnel, vous devez effectuer un certain nombre d'étapes:

  1. Définissez les coordonnées de texture des sommets de l'objet géométrique (dans l'environnement des concepteurs 3D, cela s'appelle un scan UV).
  2. Créez un objet attribut de texture pour une texture 1D, 2D, 3D ou cubique.
  3. Définissez une ou plusieurs images pour un attribut de texture.
  4. Attachez un attribut de texture et un mode à l'ensemble d'états appliqués à l'objet en cours de dessin.

OSG définit la classe osg :: Texture, qui encapsule toutes sortes de textures. Les sous-classes osg :: Texture1D, osg :: Texture2D, osg :: Texture3D et osg :: TextureCubeMap en sont héritées, qui représentent diverses techniques de texturation adoptées dans OpenGL.

La méthode de classe osg :: Texture la plus courante est setImage (), qui définit l'image utilisée dans la texture, par exemple

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

ou, vous pouvez passer l'objet image directement au constructeur de la classe de texture

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

L'image peut être récupérée à partir de l'objet texture en appelant la méthode getImage ().

Un autre point important est la définition des coordonnées de texture pour chaque sommet de l'objet osg :: Geometry. Le transfert de ces coordonnées s'effectue via les tableaux osg :: Vec2Array et osg :: Vec3Array en appelant la méthode setTexCoordArray ().

Après avoir défini les coordonnées de texture, nous devons définir le numéro d'emplacement de texture (unité), car OSG prend en charge la superposition de plusieurs textures sur la même géométrie. Lorsque vous utilisez une seule texture, le numéro d'unité est toujours 0. Par exemple, le code suivant illustre la définition des coordonnées de texture pour l'unité 0 de la géométrie

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

Après cela, nous pouvons ajouter l'attribut de texture à l'ensemble d'états, en activant automatiquement le mode de texturation correspondant (dans notre exemple GL_TEXTURE_2D) et appliquer l'attribut à la géométrie ou au nœud contenant cette géométrie

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

Veuillez noter que OpenGL gère les données d'image dans la mémoire graphique de la carte vidéo, mais l'objet osg :: Image avec les mêmes données se trouve dans la mémoire système. En conséquence, nous rencontrerons le fait que nous avons stocké deux copies des mêmes données, occupant la mémoire de processus. Si cette image n'est pas partagée par plusieurs attributs de texture, elle peut être supprimée de la mémoire système immédiatement après le transfert d'OpenGL vers la mémoire de la carte vidéo. La classe osg :: Texture fournit la méthode appropriée pour activer cette fonction.

 texture->setUnRefImageDataAfterApply( true ); 

3. Charger et appliquer une texture 2D


La technique la plus couramment utilisée est la texturation 2D - superposition d'une image (ou d'images) bidimensionnelle sur le bord d'une surface tridimensionnelle. Prenons l'exemple le plus simple d'application d'une texture unique à un polygone quadrangulaire

Exemple de texture
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(); } 


Créer un tableau de sommets et de normales à la face

 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) ); 

Créer un tableau de coordonnées de texture

 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) ); 

Le fait est que chaque sommet du modèle tridimensionnel correspond à un point sur la texture bidimensionnelle, et les coordonnées des points sur la texture sont relatives - elles sont normalisées à la largeur et à la hauteur réelles de l'image. Nous voulons étirer toute l'image chargée sur un carré, respectivement, les coins du carré correspondront aux points de texture (0, 0), (0, 1), (1, 1) et (1, 0). L'ordre des sommets dans le tableau de sommets doit correspondre à l'ordre des sommets de texture.

Ensuite, créez un carré, en affectant à la géométrie un tableau de sommets et un tableau 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) ); 

Créez un objet de texture et chargez l'image utilisée pour celui-ci.

 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()); 

Créez le nœud racine de la scène et mettez la géométrie que nous avons créée là

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

et enfin appliquer l'attribut de texture au nœud dans lequel la géométrie est placée

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



La classe osg :: Texture2D détermine si les tailles d'image de texture sont des multiples d'une puissance de deux (par exemple, 64x64 ou 256x512), mettant automatiquement à l'échelle les images qui ne conviennent pas à la taille, en utilisant en fait la fonction OpenGL gluScaleImage (). Il existe une méthode setResizeNonPowerOfTwoHint () qui détermine s'il faut ou non redimensionner l'image. Certaines cartes vidéo nécessitent un multiple de la taille d'une image de la puissance de deux, tandis que la classe osg :: Texture2D prend en charge le travail avec des tailles de texture arbitraires.

Quelque chose au sujet du mélange de texture


Comme nous l'avons déjà dit, les coordonnées de texture sont normalisées de 0 à 1. Le point (0, 0) correspond au coin supérieur gauche de l'image et le point (1, 1) correspond au coin inférieur droit. Que se passe-t-il si vous définissez des coordonnées de texture supérieures à un?

Par défaut, dans OpenGL, comme dans OSG, la texture sera répétée dans la direction de l'axe, la valeur de la coordonnée de texture dépassera un. Cette technique est souvent utilisée, par exemple, pour créer un modèle d'un long mur de briques, j'utilise une petite texture, répétant sa superposition plusieurs fois en largeur et en hauteur.

Ce comportement peut être contrôlé via la méthode setWrap () de la classe osg :: Texture. En tant que premier paramètre, la méthode prend l'identifiant de l'axe auquel le mode de fusion doit être appliqué, transmis comme deuxième paramètre, par exemple

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

Ce code indique explicitement au moteur de répéter la texture le long des axes s et r si les valeurs des coordonnées de texture dépassent 1. Une liste complète des modes de mappage de texture:

  1. REPEAT - répéter la texture.
  2. MIROIR - répétez la texture, en la reflétant.
  3. CLAMP_TO_EDGE - les coordonnées qui vont au-delà de 0 à 1 sont accrochées au bord correspondant de la texture.
  4. CLAMP_TO_BORDER - les coordonnées qui vont au-delà de 0 à 1 donneront la couleur de bordure définie par l'utilisateur.

4. Rendu Ă  la texture


La technique de rendu de texture permet au développeur de créer une texture basée sur une sous-scène ou un modèle tridimensionnel et de l'appliquer à la surface de la scène principale. Une technologie similaire est souvent appelée «cuisson» de la texture.

Pour cuire dynamiquement une texture, vous devez suivre trois étapes:

  1. Créez un objet de texture pour y effectuer le rendu.
  2. Rendez la scène en texture.
  3. Utilisez la texture résultante comme prévu.

Nous devons créer un objet de texture vide. OSG vous permet de créer une texture vide d'une taille donnée. La méthode setTextureSize () vous permet de définir la largeur et la hauteur de la texture, ainsi que la profondeur comme paramètre supplémentaire (pour les textures 3D).

Pour effectuer le rendu d'une texture, vous devez l'attacher à l'objet caméra en appelant la méthode attach (), qui prend un objet texture comme argument. De plus, cette méthode accepte un argument indiquant quelle partie du tampon de trame doit être rendue dans cette texture. Par exemple, pour transférer un tampon de couleur vers une texture, vous devez exécuter le code suivant

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

Le tampon de profondeur DEPTH_BUFFER, le tampon de gabarit STENCIL_BUFFER et les tampons de couleur supplémentaires de COLOR_BUFFER0 à COLOR_BUFFER15 sont d'autres parties du tampon d'image disponibles pour le rendu. La présence de tampons de couleur supplémentaires et leur nombre sont déterminés par le modèle de la carte vidéo.

De plus, pour un rendu de caméra sur une texture, vous devez définir les paramètres de la matrice de projection et de la fenêtre, dont la taille correspond à la taille de la texture. La texture sera mise à jour à mesure que chaque image est dessinée. Gardez à l'esprit que la caméra principale ne doit pas être utilisée pour le rendu dans une texture, car elle fournit un rendu de la scène principale et vous obtenez simplement un écran noir. Cette condition peut ne pas être remplie uniquement lorsque vous effectuez un rendu hors écran.

5. Un exemple d'implémentation du rendu en texture


Pour démontrer la technique de rendu dans une texture, nous implémentons la tâche suivante: créer un carré, dessiner une texture carrée dessus et rendre une scène animée dans la texture, bien sûr avec le cessna que nous aimons. Le programme qui implémente l'exemple est assez volumineux. Cependant, je donnerai quand même son code source complet.

Exemple 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; } 


Pour créer un carré, écrivez une fonction libre distincte

 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 fonction accepte la position du centre du carré et ses dimensions géométriques en entrée. Ensuite, un tableau de sommets, un tableau de normales et des coordonnées de texture sont créés, après quoi la géométrie créée est renvoyée par la fonction.

Dans le corps du programme principal, chargez le modèle de cessna

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

Afin d'animer ce modèle, créez et initialisez la transformation de rotation autour de l'axe 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()); 

Maintenant, créez un modèle pour la scène principale - un carré sur lequel nous rendrons

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

Créez une texture vide pour un carré de 1024x1024 pixels avec un format de pixel RGBA (couleur 32 bits à trois composants avec canal alpha)

 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); 

Appliquez cette texture au modèle carré.

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

Ensuite, créez une caméra qui créera la texture

 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 fenêtre de la caméra a la même taille que la texture. En outre, n'oubliez pas de définir la couleur d'arrière-plan lors du nettoyage de l'écran et du masque de nettoyage, indiquant d'effacer à la fois le tampon de couleur et le tampon de profondeur. Ensuite, configurez la caméra pour restituer à la texture

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

L'ordre de rendu de PRE_RENDER indique que cette caméra effectue le rendu avant le rendu sur la scène principale. Spécifiez le FBO comme cible de rendu et attachez notre texture à la caméra. Maintenant, nous configurons la caméra pour qu'elle fonctionne dans un système de coordonnées absolu, et en tant que scène, nous définissons notre sous-arbre, que nous voulons rendre en texture: une transformation de rotation avec un modèle cessna attaché à elle

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

Créez un nœud de groupe racine en y ajoutant le modèle principal (carré) et une texture de traitement de caméra

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

Créer et personnaliser une visionneuse

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

Configurer la matrice de projection pour la caméra - une projection en perspective à travers les paramètres de la pyramide de découpage

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

Nous avons mis en place une matrice de vue qui définit la position de la caméra dans l'espace par rapport à l'origine du sous-cessna

 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); 

Enfin, animez et affichez la scène, en changeant l'angle de rotation de l'avion autour de l'axe Z dans chaque image

 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; } 

En conséquence, nous obtenons une image plutôt intéressante



Dans cet exemple, nous avons implémenté une animation de scène, mais rappelez-vous que l'extension de la boucle run () et la modification des paramètres de rendu avant ou après le rendu de l'image est une activité dangereuse en termes d'organisation de l'accès aux données de différents flux. Étant donné que OSG utilise le rendu multithread, il existe également des mécanismes réguliers pour incorporer leurs propres actions dans le processus de rendu, qui fournissent un accès thread-safe aux données.

6. Enregistrement du résultat du rendu dans un fichier


OSG prend en charge la possibilité d'attacher un objet osg :: Image à la caméra et d'enregistrer le contenu du tampon d'image dans le tampon de données d'image. Après cela, il est possible de sauvegarder ces données sur le disque en utilisant la fonction 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" ); 

Conclusion


Peut-être que le matériel présenté dans l'article semble insignifiant. Cependant, il décrit les bases mêmes du travail avec les textures dans OpenSceneGraph, sur lesquelles sont basées des techniques plus complexes pour travailler avec ce moteur, dont nous parlerons certainement à l'avenir.

Ă€ suivre ...

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


All Articles