OpenSceneGraph: principes de base de la géométrie de scène

image

Présentation


OpenGL, qui est le backend d'OpenSceneGraph, utilise des primitives géométriques (telles que des points, des lignes, des triangles et des faces polygonales) pour construire tous les objets dans le monde tridimensionnel.

Ces primitives sont définies par des données sur leurs sommets, qui incluent les coordonnées des sommets, les composants normaux, les données de couleur et les coordonnées de texture. Ces données sont stockées dans des tableaux spéciaux. Les primitives peuvent être formées, par exemple, en spécifiant pour les objets qui les décrivent une liste d'indices de vertex. Cette méthode est appelée la méthode des sommets, elle élimine le stockage des sommets redondants en mémoire et a de bonnes performances.

En outre, OpenGL peut utiliser le mécanisme des listes dites d' affichage , lorsque les primitives préparées dans la mémoire vidéo peuvent être réutilisées, ce qui accélère considérablement l'affichage des objets statiques.

Par défaut, OSG utilise la méthode de tableau de sommets et la méthode de liste d'affichage pour rendre la géométrie. Cependant, la stratégie de rendu peut être modifiée, selon la façon dont les données de géométrie sont présentées. Dans cet article, nous couvrirons les techniques de base pour travailler avec la géométrie dans OSG.

1. Classes Geode et Drawable


La classe osg :: Geode est un terminal, le nœud dit "feuille" de l'arbre de la scène. Il ne peut pas avoir de nœuds enfants, mais il contient toutes les informations nécessaires au rendu de la géométrie. Son nom, Geode, est l'abréviation de nœud de géométrie.

Les données géométriques à traiter par le moteur sont stockées dans l'ensemble d'objets de la classe osg :: Drawable, géré par la classe osg :: Geode. La classe osg :: Drawable est une classe purement virtuelle. Un certain nombre de sous-classes en sont héritées, qui sont des modèles tridimensionnels, des images et du texte traités par le pipeline OpenGL. OSG désigne dessinable comme tous les éléments pouvant être dessinés par le moteur.

La classe osg :: Geode fournit un certain nombre de méthodes pour attacher et détacher des drawables:

  • MĂ©thode publique addDrawable () - passe un pointeur sur un Ă©lĂ©ment dessinable dans une instance de la classe osg :: Geode. Tous ces Ă©lĂ©ments sont contrĂ´lĂ©s par des pointeurs intelligents osg :: ref_ptr <>.
  • La mĂ©thode publique removeDrawable () et removeDrawables () supprime l'objet d'osg :: Geode et diminue le nombre de rĂ©fĂ©rences pour lui. La mĂ©thode removeDrawable () prend comme paramètre unique un pointeur sur un Ă©lĂ©ment d'intĂ©rĂŞt, et la mĂ©thode removeDrawables () prend deux paramètres: l'index initial et le nombre d'Ă©lĂ©ments Ă  supprimer du tableau d'objets osg :: Geode.
  • La mĂ©thode getDrawable () renvoie un pointeur sur un Ă©lĂ©ment de l'index passĂ© en paramètre.
  • La mĂ©thode getNumDrawables () renvoie le nombre total d'Ă©lĂ©ments attachĂ©s Ă  osg :: Geode. Par exemple, pour supprimer tous les Ă©lĂ©ments d'osg :: Geode, vous pouvez utiliser ce code

geode->removeDrawables(0, geode->getNumDrawables()); 

2. Dessiner des formes simples


OSG fournit la classe osg :: ShapeDrawable, qui est la descendante de la classe osg :: Drawable et conçue pour créer des primitives tridimensionnelles simples. Cette classe comprend un objet osg :: Shape qui stocke des informations sur une géométrie spécifique et d'autres paramètres. Les primitives sont générées à l'aide de la méthode setShape (), par exemple

 shapeDrawable->setShape(new osg::Box(osg::Vec3(1.0f, 0.0f, 0.0f), 10.0f, 10.0f, 5.0f)); 

crée une boîte rectangulaire avec un centre géométrique au point (1.0, 0.0, 0.0) avec une largeur et une hauteur de 10 et une profondeur de 5 unités. La classe osg :: Vec3 définit un vecteur dans un espace tridimensionnel (en outre, les classes osg :: Vec2 et osg :: Vec4 décrivant des vecteurs de la dimension correspondante sont également présentées).

Les primitives les plus populaires sont représentées dans OSG par les classes osg :: Box, osg :: Capsule, osg :: Cone, osg :: Cylinder et osg :: Sphere.

Prenons un exemple d'application de ce mécanisme.

main.h
 #ifndef MAIN_H #define MAIN_H #include <osg/ShapeDrawable> #include <osg/Geode> #include <osgViewer/Viewer> #endif // MAIN_H 

main.cpp
 #include "main.h" int main(int argc, char *argv[]) { (void) argc; (void) argv; osg::ref_ptr<osg::ShapeDrawable> shape1 = new osg::ShapeDrawable; shape1->setShape(new osg::Box(osg::Vec3(-3.0f, 0.0f, 0.0f), 2.0f, 2.0f, 1.0f)); osg::ref_ptr<osg::ShapeDrawable> shape2 = new osg::ShapeDrawable; shape2->setShape(new osg::Cone(osg::Vec3(0.0f, 0.0f, 0.0f), 1.0f, 1.0f)); shape2->setColor(osg::Vec4(0.0f, 1.0f, 0.0f, 1.0f)); osg::ref_ptr<osg::ShapeDrawable> shape3 = new osg::ShapeDrawable; shape3->setShape(new osg::Sphere(osg::Vec3(3.0f, 0.0f, 0.0f), 1.0f)); shape3->setColor(osg::Vec4(0.0f, 0.0f, 1.0f, 1.0f)); osg::ref_ptr<osg::Geode> root = new osg::Geode; root->addDrawable(shape1.get()); root->addDrawable(shape2.get()); root->addDrawable(shape3.get()); osgViewer::Viewer viewer; viewer.setSceneData(root.get()); return viewer.run(); } 

Cet exemple n'a surtout pas besoin de commentaires: dans le programme trois formes simples sont créées, après compilation et lancement nous verrons un tel résultat



Le mécanisme montré dans l'exemple est simple et direct, mais ce n'est pas le moyen le plus efficace pour créer une géométrie et peut être utilisé exclusivement pour des tests. La classe osg :: Geometry est utilisée pour créer une géométrie dans des applications OSG hautes performances.

3. Stockage des données de géométrie: classes osg :: Array et osg :: Geometry


La classe osg :: Array est une classe abstraite de base, dont plusieurs descendants sont hérités, conçue pour stocker des données transmises aux fonctions OpenGL. Travailler avec cette classe est similaire à travailler avec std :: vector de la bibliothèque standard C ++. Le code suivant illustre l'ajout d'un vecteur à un tableau de sommets à l'aide de la méthode push_back ()

 vertices->push_back(osg::Vec3(1.0f, 0.0f, 0.0f)); 

Les tableaux OSG sont alloués sur le tas et gérés par des pointeurs intelligents. Cependant, cela ne s'applique pas aux éléments de tableau, tels que osg :: Vec3 ou osg :: Vec2, qui peuvent également être créés sur la pile.

La classe osg :: Geometry est un wrapper sur les fonctions OpenGL qui fonctionnent avec les tableaux de vertex. Il est dérivé de la classe osg :: Drawable et peut être facilement ajouté à la liste d'objets osg :: Geode. Cette classe prend en entrée les tableaux décrits ci-dessus et les utilise pour générer une géométrie à l'aide d'OpenGL.

4. Sommets et leurs attributs


Un sommet est une unité atomique de primitives de géométrie. Il possède un certain nombre d'attributs qui décrivent un point dans un espace à deux ou trois dimensions. Les attributs incluent: position, couleur, vecteur normal, coordonnées de texture, coordonnées de brouillard, etc. Le sommet doit toujours avoir une position dans l'espace, comme pour les autres attributs, ils peuvent être présents en option. OpenGL prend en charge 16 attributs de sommet de base et peut utiliser différents tableaux pour les stocker. Tous les tableaux d'attributs sont pris en charge par la classe osg :: Geometry et peuvent être définis à l'aide des méthodes du formulaire set * Array ().

Attributs de sommet dans OpenSceneGraph
AttributType de donnéesOsg :: Méthode de géométrieAppel OpenGL équivalent
Poste3 vecteurssetVertexArray ()glVertexPointer ()
Normal3 vecteurssetNormalArray ()glNormalPointer ()
La couleur4 vecteurssetColorArray ()glColorPointer ()
Couleur secondaire4 vecteurssetSecondaryColorArray ()glSecondaryColorPointerEXT ()
Coordonnées de brouillardflottersetFogCoordArray ()glFogCoordPointerEXT ()
Coordonnées de texture2 ou 3 vecteurssetTexCoordArray ()glTexCoordPointer ()
Autres attributsDĂ©fini par l'utilisateursetVertexArribArray ()glVertexAttribPointerARB ()

En principe, il est nécessaire de définir vos propres attributs pour chacun des sommets, ce qui conduit à la formation de plusieurs tableaux d'attributs de même taille - sinon un décalage dans les tailles des tableaux peut conduire à un comportement indéfini du moteur. OSG prend en charge diverses méthodes pour lier les attributs de sommet, par exemple

 geom->setColorBinding(osg::Geometry::BIND_PER_VERTEX); 

signifie que chaque sommet et chaque couleur du sommet sont corrélés l'un à l'autre. Cependant, si vous regardez ce code

 geom->setColorBinding(osg::Geometry::BIND_OVERALL); 

puis il applique une couleur à toute la géométrie. De même, les relations entre d'autres attributs peuvent être configurées en appelant les méthodes setNormalBinding (), setSecondaryColorBinding (), setFogCoordBinding () et setVertexAttribBinding ().

5. Ensembles de primitives de géométrie


L'étape suivante après avoir défini les tableaux d'attributs de sommet est de décrire comment les données de sommet seront rendues. La classe virtuelle osg :: PrimitiveSet est utilisée pour contrôler les primitives géométriques générées par le moteur de rendu à partir de l'ensemble de sommets. La classe osg :: Geometry fournit plusieurs méthodes pour travailler avec des ensembles de primitives de géométrie:

  • addPrimitiveSet () - passe un pointeur vers un ensemble de primitives dans un objet osg :: Geometry.
  • removePrimitiveSet () - supprime un ensemble de primitives. Comme paramètres, il prend l'index initial des ensembles et le nombre d'ensembles Ă  supprimer.
  • getPrimitiveSet () - retourne un ensemble de primitives Ă  l'index passĂ© en paramètre.
  • getNumPrimitiveSets () - renvoie le nombre total d'ensembles de primitives associĂ©s Ă  cette gĂ©omĂ©trie.

La classe osg :: PrimitiveSet est abstraite et ne peut pas être instanciée, mais plusieurs classes dérivées qui encapsulent les ensembles de primitives avec lesquelles OpenGL fonctionne, telles que osg :: DrawArrays et osg :: DrawElementsUInt, en héritent.

La classe osg :: DrawArrays utilise plusieurs éléments consécutifs d'un tableau de sommets pour construire une primitive géométrique. Il peut être créé et attaché à la géométrie en appelant une méthode.

 geom->addPrimitiveSet(new osg::DrawArrays(mode, first, count)); 

Le premier mode de paramètre définit le type primitif sur les types primitifs OpenGL correspondants: GL_POINTS, GL_LINE_STRIP, GL_LINE_LOOP, GL_LINES, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, GL_TRIANGLES, GL_QUAD_STRIP, GL_QUADS et GL_POLY.

Les premier et deuxième paramètres spécifient le premier index du tableau de sommets et le nombre de sommets à partir desquels la géométrie doit être générée. De plus, OSG ne vérifie pas si le nombre de sommets spécifié est suffisant pour construire la géométrie spécifiée par le mode, ce qui peut conduire au crash de l'application!

6. Exemple - dessiner un carré peint


Nous mettons en œuvre tout ce qui précède comme un exemple simple

Le code source complet pour l'exemple quad
main.h

 #ifndef MAIN_H #define MAIN_H #include <osg/Geometry> #include <osg/Geode> #include <osgViewer/Viewer> #endif // MAIN_H 

main.cpp

 #include "main.h" int main(int argc, char *argv[]) { osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array; vertices->push_back(osg::Vec3(0.0f, 0.0f, 0.0f)); vertices->push_back(osg::Vec3(1.0f, 0.0f, 0.0f)); vertices->push_back(osg::Vec3(1.0f, 0.0f, 1.0f)); vertices->push_back(osg::Vec3(0.0f, 0.0f, 1.0f)); osg::ref_ptr<osg::Vec3Array> normals = new osg::Vec3Array; normals->push_back(osg::Vec3(0.0f, -1.0f, 0.0f)); osg::ref_ptr<osg::Vec4Array> colors = new osg::Vec4Array; colors->push_back(osg::Vec4(1.0f, 0.0f, 0.0f, 1.0f)); colors->push_back(osg::Vec4(0.0f, 1.0f, 0.0f, 1.0f)); colors->push_back(osg::Vec4(0.0f, 0.0f, 1.0f, 1.0f)); colors->push_back(osg::Vec4(1.0f, 1.0f, 1.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->setColorArray(colors.get()); quad->setColorBinding(osg::Geometry::BIND_PER_VERTEX); quad->addPrimitiveSet(new osg::DrawArrays(GL_QUADS, 0, 4)); osg::ref_ptr<osg::Geode> root = new osg::Geode; root->addDrawable(quad.get()); osgViewer::Viewer viewer; viewer.setSceneData(root.get()); return viewer.run(); } 


Après compilation et exécution, nous obtenons un résultat similaire à celui-ci



Cet exemple doit être clarifié. Donc, tout d'abord, nous créons un tableau de sommets du carré, dans lequel leurs coordonnées sont stockées

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

Ensuite, nous définissons le tableau de normales. Dans notre cas simple, nous n'avons pas besoin de créer une normale pour chaque sommet - il suffit de décrire un vecteur unitaire dirigé perpendiculairement au plan du carré

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

DĂ©finissez une couleur pour chaque sommet

 osg::ref_ptr<osg::Vec4Array> colors = new osg::Vec4Array; colors->push_back(osg::Vec4(1.0f, 0.0f, 0.0f, 1.0f)); colors->push_back(osg::Vec4(0.0f, 1.0f, 0.0f, 1.0f)); colors->push_back(osg::Vec4(0.0f, 0.0f, 1.0f, 1.0f)); colors->push_back(osg::Vec4(1.0f, 1.0f, 1.0f, 1.0f)); 

Créez maintenant un objet géométrique où la description de notre carré sera stockée, qui sera traitée par le rendu. Nous passons un tableau de sommets à cette géométrie

 osg::ref_ptr<osg::Geometry> quad = new osg::Geometry; quad->setVertexArray(vertices.get()); 

En passant un tableau de normales, nous informons le moteur qu'une seule normale sera utilisée pour tous les sommets, en spécifiant la méthode de liaison ("binding") des normales BIND_OVAERALL

 quad->setNormalArray(normals.get()); quad->setNormalBinding(osg::Geometry::BIND_OVERALL); 

En passant les couleurs des sommets, au contraire, nous indiquons que chaque sommet aura sa propre couleur

 quad->setColorArray(colors.get()); quad->setColorBinding(osg::Geometry::BIND_PER_VERTEX); 

Créez maintenant un ensemble de primitives pour la géométrie. Nous indiquons que les faces carrées (GL_QUADS) doivent être générées à partir du tableau de sommets, en prenant le sommet avec l'index 0 comme premier sommet, et le nombre total de sommets sera de 4

 quad->addPrimitiveSet(new osg::DrawArrays(GL_QUADS, 0, 4)); 

Eh bien, je pense que ça ne vaut pas la peine d'expliquer le transfert de géométrie et le lancement du rendu

 osg::ref_ptr<osg::Geode> root = new osg::Geode; root->addDrawable(quad.get()); osgViewer::Viewer viewer; viewer.setSceneData(root.get()); return viewer.run(); 

Le code ci-dessus est Ă©quivalent Ă  la conception suivante en OpenGL pur

 static const GLfloat vertices[][3] = { … }; glEnableClientState( GL_VERTEX_ARRAY ); glVertexPointer( 4, GL_FLOAT, 0, vertices ); glDrawArrays( GL_QUADS, 0, 4 ); 

7. Indexation des sommets dans les primitives


La classe osg :: DrawArrays fonctionne bien lors de la lecture de données de vertex directement à partir de tableaux, sans lacunes. Cependant, cela n'est pas aussi efficace lorsqu'un même sommet peut appartenir à plusieurs faces d'un objet. Regardons un exemple.



Le cube a huit sommets. Cependant, comme on peut le voir sur la figure (nous regardons le déroulement du cube sur le plan), certains sommets appartiennent à plus d'une face. Si vous construisez un cube de 12 faces triangulaires, alors ces sommets seront répétés, et au lieu d'un tableau de 8 sommets, nous obtenons un tableau de 36 sommets, dont la plupart sont en fait le même sommet!

Les classes OSG osg :: DrawElementsUInt, osg :: DrawElementsUByte et osg :: DrawElementsUShort, qui utilisent des tableaux d'index de sommets comme données, sont conçues pour résoudre le problème décrit. Les tableaux d'index stockent des index de sommets de primitives qui décrivent des faces et d'autres éléments de la géométrie. Lors de l'application de ces classes à un cube, il suffit de stocker un tableau de huit sommets associés à des faces via des tableaux d'indices.

Les classes de type osg :: DrawElements * sont construites de la même manière que la classe standard std :: vector. Un tel code peut être utilisé pour ajouter des indices.

 osg::ref_ptr<osg::DrawElementsUInt> de = new osg::DrawElementsUInt(GL_TRIANGLES); de->push_back(0); de->push_back(1); de->push_back(2); de->push_back(3); de->push_back(0); de->push_back(2); 

Ce code définit la face avant du cube illustré dans la figure.

Prenons un autre exemple illustratif - un octaèdre



C'est intéressant car il ne contient que six sommets, mais chaque sommet entre jusqu'à quatre faces triangulaires! Nous pouvons créer un tableau de 24 sommets pour afficher les huit faces en utilisant osg :: DrawArrays. Cependant, nous ferons autrement - nous stockerons les sommets dans un tableau de six éléments, et nous générerons des faces en utilisant la classe osg :: DrawElementsUInt.

Source complète pour l'exemple octaèdre
main.h
 #ifndef MAIN_H #define MAIN_H #include <osg/Geometry> #include <osg/Geode> #include <osgUtil/SmoothingVisitor> #include <osgViewer/Viewer> #endif 

main.cpp
 #include "main.h" int main(int argc, char *argv[]) { osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array(6); (*vertices)[0].set( 0.0f, 0.0f, 1.0f); (*vertices)[1].set(-0.5f, -0.5f, 0.0f); (*vertices)[2].set( 0.5f, -0.5f, 0.0f); (*vertices)[3].set( 0.5f, 0.5f, 0.0f); (*vertices)[4].set(-0.5f, 0.5f, 0.0f); (*vertices)[5].set( 0.0f, 0.0f, -1.0f); osg::ref_ptr<osg::DrawElementsUInt> indices = new osg::DrawElementsUInt(GL_TRIANGLES, 24); (*indices)[ 0] = 0; (*indices)[ 1] = 1; (*indices)[ 2] = 2; (*indices)[ 3] = 0; (*indices)[ 4] = 4; (*indices)[ 5] = 1; (*indices)[ 6] = 4; (*indices)[ 7] = 5; (*indices)[ 8] = 1; (*indices)[ 9] = 4; (*indices)[10] = 3; (*indices)[11] = 5; (*indices)[12] = 3; (*indices)[13] = 2; (*indices)[14] = 5; (*indices)[15] = 1; (*indices)[16] = 5; (*indices)[17] = 2; (*indices)[18] = 3; (*indices)[19] = 0; (*indices)[20] = 2; (*indices)[21] = 0; (*indices)[22] = 3; (*indices)[23] = 4; osg::ref_ptr<osg::Geometry> geom = new osg::Geometry; geom->setVertexArray(vertices.get()); geom->addPrimitiveSet(indices.get()); osgUtil::SmoothingVisitor::smooth(*geom); osg::ref_ptr<osg::Geode> root = new osg::Geode; root->addDrawable(geom.get()); osgViewer::Viewer viewer; viewer.setSceneData(root.get()); return viewer.run(); } 


Analysons ce code plus en détail. Bien sûr, la première chose que nous faisons est de créer un tableau de six sommets

 osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array(6); (*vertices)[0].set( 0.0f, 0.0f, 1.0f); (*vertices)[1].set(-0.5f, -0.5f, 0.0f); (*vertices)[2].set( 0.5f, -0.5f, 0.0f); (*vertices)[3].set( 0.5f, 0.5f, 0.0f); (*vertices)[4].set(-0.5f, 0.5f, 0.0f); (*vertices)[5].set( 0.0f, 0.0f, -1.0f); 

Nous initialisons chaque sommet directement en accédant au vecteur de ses coordonnées en utilisant l'opération de déréférencement du pointeur et de l'opérateur operator [] (nous nous souvenons que osg :: Array est similaire dans son dispositif à std :: vector).

Créez maintenant des faces sous forme de liste d'index de sommets

 osg::ref_ptr<osg::DrawElementsUInt> indices = new osg::DrawElementsUInt(GL_TRIANGLES, 24); (*indices)[ 0] = 0; (*indices)[ 1] = 1; (*indices)[ 2] = 2; //  0 (*indices)[ 3] = 0; (*indices)[ 4] = 4; (*indices)[ 5] = 1; //  1 (*indices)[ 6] = 4; (*indices)[ 7] = 5; (*indices)[ 8] = 1; //  2 (*indices)[ 9] = 4; (*indices)[10] = 3; (*indices)[11] = 5; //  3 (*indices)[12] = 3; (*indices)[13] = 2; (*indices)[14] = 5; //  4 (*indices)[15] = 1; (*indices)[16] = 5; (*indices)[17] = 2; //  5 (*indices)[18] = 3; (*indices)[19] = 0; (*indices)[20] = 2; //  6 (*indices)[21] = 0; (*indices)[22] = 3; (*indices)[23] = 4; //  7 

Les faces seront triangulaires, il y en aura 8, ce qui signifie que la liste des indices doit contenir 24 éléments. Les indices des faces vont séquentiellement dans ce tableau: par exemple, la face 0 est formée par les sommets 0, 1 et 2; face 1 - sommets 0, 4 et 1; face 2 - sommets 4, 5 et 1 et ainsi de suite. Les sommets sont répertoriés dans le sens antihoraire si vous regardez le visage du visage (voir la figure ci-dessus).

Étapes supplémentaires pour créer la géométrie que nous avons effectuée dans les exemples précédents. La seule chose que nous n'avons pas faite est la génération automatique de normales lissées (moyennées), que nous effectuons dans cet exemple en appelant

 osgUtil::SmoothingVisitor::smooth(*geom); 

En effet, si les sommets d'une face sont donnés, alors il est facile de calculer la normale à celle-ci. Aux sommets où plusieurs faces convergent, une certaine normale moyenne est calculée - les normales des faces convergentes sont additionnées et la somme résultante est à nouveau normalisée. Ces opérations (ainsi que bien d'autres!) Peuvent être effectuées par le moteur lui-même à l'aide des classes de la bibliothèque osgUtil. Par conséquent, dans notre exemple, nous allons ajouter une instruction à l'éditeur de liens pour construire notre programme avec cette bibliothèque dans le fichier * .pro

octahedron.pro

 CONFIG(debug, debug|release) { TARGET = $$join(TARGET,,,_d) . . . LIBS += -L$$OSG_LIB_DIRECTORY -losgUtild } else { . . . LIBS += -L$$OSG_LIB_DIRECTORY -losgUtil } 

En conséquence, nous obtenons le résultat suivant



Pour comprendre comment cela fonctionne, considérez le pipeline OpenGL



Le mécanisme de tableau de vertex réduit le nombre d'appels OpenGL. Il stocke les données de sommet dans la mémoire de l'application, qui est utilisée côté client. Le pipeline OpenGL côté serveur a accès à différents tableaux de vertex. Comme le montre le diagramme, OpenGL reçoit des données de la mémoire tampon vertex côté client et, de manière ordonnée, effectue l'assemblage des primitives. C'est ainsi que les données sont traitées à l'aide des méthodes set * Array () de la classe osg :: Geometry. La classe osg :: DrawArrays passe directement par ces tableaux et les affiche.

Lorsque vous utilisez osg :: DrawElements *, la dimension des tableaux de sommets est réduite et le nombre de sommets transférés vers le pipeline est réduit. Un tableau d'index vous permet de créer un cache de vertex côté serveur. OpenGL lit les données de vertex dans le cache, au lieu de lire dans le tampon de vertex côté client. Cela augmente considérablement les performances globales de rendu.

8. Techniques de traitement du maillage polygonal


OpenSceneGraph prend en charge diverses techniques de traitement du maillage polygonal d'objets de géométrie de scène. Ces méthodes de prétraitement, telles que la réduction et la tessellation des polygones, sont souvent utilisées pour créer et optimiser des modèles polygonaux. Ils ont une interface simple, mais dans le processus, ils effectuent beaucoup de calculs complexes et ne sont pas très adaptés à une exécution à la volée.

Les techniques décrites comprennent:

  1. osgUtil :: Simplifier - réduire le nombre de triangles dans la géométrie. La méthode publique simplify () est utilisée pour simplifier la géométrie du modèle.
  2. osgUtil :: SmootingVisitor - calcul des normales. La méthode smooth () peut être utilisée pour générer des normales lissées pour le modèle, au lieu de les calculer indépendamment et de les définir explicitement via un tableau de normales.
  3. osgUtil :: TangentSpaceGenerator - génération de vecteurs de base tangents pour les sommets du modèle. Il est lancé en appelant la méthode generate () et enregistre le résultat renvoyé par les méthodes getTangentArray (), getNormalArray () et getBinormalArray (). Ces résultats peuvent être utilisés pour divers attributs de sommet lors de l'écriture de shaders dans GLSL.
  4. osgUtil :: Tesselator - effectue la tessellation d'un maillage polygonal - divise des primitives complexes en une séquence de primitives simples (méthode retesselatePolygons ())
  5. osgUtil :: TriStripVisitor - convertit une surface géométrique en un ensemble de bandes de faces triangulaires, ce qui permet un rendu avec une consommation de mémoire efficace. La méthode stripify () convertit un ensemble de primitives de modèle en géométrie basée sur l'ensemble GL_TRIANGLE_STRIP.

Toutes les méthodes acceptent la géométrie de l'objet en tant que paramètre transmis par osg :: Geometry & link, par exemple comme ceci

 osgUtil::TriStripVisitor tsv; tsv.stripify(*geom); 

où geom fait référence à une instance de géométrie décrite par un pointeur intelligent.

Les classes osg :: Simplifier, osg :: SmoothingVisitor et osg :: TriStripVisitor peuvent fonctionner directement avec les nœuds du graphe de scène, par exemple

 osgUtil::TriStripVisitor tsv; node->accept(tsv); 

La méthode accept () traite tous les nœuds enfants jusqu'à ce que l'opération spécifiée soit appliquée à tous les nœuds terminaux de cette partie de l'arborescence de scènes stockés dans des nœuds de type osg :: Geode.

Essayons la technique de pavage en pratique.

Exemple de code complet de navire
main.h
 #ifndef MAIN_H #define MAIN_H #include <osg/Geometry> #include <osg/Geode> #include <osgUtil/Tessellator> #include <osgViewer/Viewer> #endif 

main.cpp
 #include "main.h" int main(int argc, char *argv[]) { /*    ----- | _| | |_ | | ----- */ osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array; vertices->push_back( osg::Vec3(0.0f, 0.0f, 0.0f) ); // 0 vertices->push_back( osg::Vec3(2.0f, 0.0f, 0.0f) ); // 1 vertices->push_back( osg::Vec3(2.0f, 0.0f, 1.0f) ); // 2 vertices->push_back( osg::Vec3(1.0f, 0.0f, 1.0f) ); // 3 vertices->push_back( osg::Vec3(1.0f, 0.0f, 2.0f) ); // 4 vertices->push_back( osg::Vec3(2.0f, 0.0f, 2.0f) ); // 5 vertices->push_back( osg::Vec3(2.0f, 0.0f, 3.0f) ); // 6 vertices->push_back( osg::Vec3(0.0f, 0.0f, 3.0f) ); // 7 osg::ref_ptr<osg::Vec3Array> normals = new osg::Vec3Array; normals->push_back( osg::Vec3(0.0f, -1.0f, 0.0f) ); osg::ref_ptr<osg::Geometry> geom = new osg::Geometry; geom->setVertexArray(vertices.get()); geom->setNormalArray(normals.get()); geom->setNormalBinding(osg::Geometry::BIND_OVERALL); geom->addPrimitiveSet(new osg::DrawArrays(GL_POLYGON, 0, 8)); osg::ref_ptr<osg::Geode> root = new osg::Geode; root->addDrawable(geom.get()); osgViewer::Viewer viewer; viewer.setSceneData(root.get()); return viewer.run(); } 


Sur la base de la position spatiale des sommets dans cet exemple, nous voyons que nous essayons de créer un polygone non convexe de huit sommets, en utilisant la génération d'une face de type GL_POLYGON. L'assemblage et l'exécution de cet exemple montrent que le résultat que nous attendons ne fonctionne pas - l'exemple n'est pas affiché correctement



Pour résoudre ce problème, la géométrie construite doit être tessellée avant de la transmettre au visualiseur

 osgUtil::Tessellator ts; ts.retessellatePolygons(*geom); 

après quoi nous obtenons le bon résultat



Comment ça marche? Un polygone non convexe, sans l'utilisation d'une tessellation correcte, ne sera pas affiché comme nous l'attendons, car OpenGL, cherchant à optimiser les performances, le considérera comme un polygone convexe simple ou l'ignorera simplement, ce qui peut donner des résultats complètement inattendus.

La classe osgUtil :: Tessellator utilise des algorithmes pour transformer un polygone convexe en une série de polygones non convexes - dans notre cas, elle transforme la géométrie en GL_TRIANGLE_STRIP.



Cette classe peut gérer les polygones de trous et les polygones auto-entrecroisés. À l'aide de la méthode publique setWindingType (), vous pouvez définir diverses règles de traitement, telles que GLU_TESS_WINDING_ODD ou GLU_TESS_WINDING_NONZERO, qui spécifient les régions intérieure et extérieure d'un polygone complexe.

Conclusion


Dans cet article, nous avons acquis une compréhension de base de la façon dont la géométrie des objets tridimensionnels est stockée et traitée dans le moteur OSG. Ne pensez pas que ces exemples simples et pas trop impressionnants qui sont considérés dans l'article sont la limite des capacités du moteur. Juste ces exemples peuvent aider le développeur à comprendre la mécanique d'OpenSceneGraph, et sans cette compréhension, il est difficile d'imaginer le travail de choses plus complexes.

Cet article est basé sur la traduction et le traitement du texte des chapitres correspondants du livre OpenSceneGraph 3.0. Guide du débutant . Tous les exemples sont vérifiés par moi personnellement et leur code source est disponible ici . À suivre ...

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


All Articles