
Présentation
Parlant des techniques de programmation spécifiques à OSG
, la dernière fois nous avons parlé du mécanisme de rappel et de son implémentation dans le moteur. Il est temps d'examiner les possibilités que ce mécanisme offre pour gérer le contenu d'une scène en trois dimensions.
Si nous parlons d'animation d'objet, OSG offre au développeur deux options pour sa mise en œuvre:
- Animation procédurale implémentée par programmation grâce à la transformation des objets et de leurs attributs
- Exporter l'animation à partir d'un éditeur 3D et la gérer à partir du code d'application
Pour commencer, considérez la première possibilité, comme la plus évidente. Nous parlerons certainement de la seconde un peu plus tard.
1. Animation de morphing procédural
Lors de la traversée du graphique de la scène, OSG transfère les données vers le pipeline OpenGL, qui s'exécute dans un thread séparé. Ce thread doit être synchronisé avec d'autres threads de traitement dans chaque trame. Sinon, la méthode frame () peut se terminer avant le traitement des données de géométrie. Cela entraînera un comportement imprévisible du programme et des plantages. OSG propose une solution à ce problème sous la forme de la méthode setDataVariance () de la classe osg :: Object, qui est la base de tous les objets jalons de la scène. Vous pouvez définir trois modes de traitement pour les objets
- NON SPÉCIFIÉ (par défaut) - OSG détermine indépendamment l'ordre de traitement de l'objet.
- STATIQUE - l'objet est immuable et l'ordre de son traitement n'est pas important. Accélère considérablement le rendu.
- DYNAMIQUE - l'objet doit être traité avant le début du rendu.
Ce paramètre peut être défini à tout moment en appelant
node->setDataVariance( osg::Object::DYNAMIC );
La pratique généralement acceptée consiste à modifier la géométrie "à la volée", c'est-à -dire à modifier dynamiquement les coordonnées des sommets, des normales de couleur et des textures dans chaque image, pour obtenir une géométrie mutable. Cette technique est appelée animation de morphing. Dans ce cas, l'ordre de traitement de la géométrie est déterminant - toutes ses modifications doivent être recalculées avant le début du dessin. Pour illustrer cette astuce, nous modifions légèrement l'exemple carré coloré, forçant l'un de ses sommets à tourner autour de l'axe X.
Exemple Animquadmain.h #ifndef MAIN_H #define MAIN_H #include <osg/Geometry> #include <osg/Geode> #include <osgViewer/Viewer> #endif
main.cpp #include "main.h"
Nous allons créer un carré dans une fonction séparée
osg::Geometry *createQuad() { 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)); return quad.release(); }
une description dont, en principe, n'est pas nécessaire, car nous avons fait de telles actions à plusieurs reprises. Pour modifier les sommets de ce carré, nous écrivons la classe DynamicQuadCallback, l'héritant de osg :: Drawable :: UpdateCallback
class DynamicQuadCallback : public osg::Drawable::UpdateCallback { public: virtual void update(osg::NodeVisitor *, osg::Drawable *drawable); };
remplacer la méthode update ()
void DynamicQuadCallback::update(osg::NodeVisitor *, osg::Drawable *drawable) { osg::Geometry *quad = static_cast<osg::Geometry *>(drawable); if (!quad) return; osg::Vec3Array *vertices = static_cast<osg::Vec3Array *>(quad->getVertexArray()); if (!vertices) return; osg::Quat quat(osg::PI * 0.01, osg::X_AXIS); vertices->back() = quat * vertices->back(); quad->dirtyDisplayList(); quad->dirtyBound(); }
Ici, nous obtenons un pointeur sur un objet géométrique
osg::Geometry *quad = static_cast<osg::Geometry *>(drawable);
on lit dans la géométrie une liste de sommets (ou plutôt un pointeur sur celle-ci)
osg::Vec3Array *vertices = static_cast<osg::Vec3Array *>(quad->getVertexArray());
Pour obtenir le dernier élément (dernier sommet) du tableau, la classe osg :: Array fournit la méthode back (). Pour effectuer la rotation du sommet par rapport à l'axe X, nous introduisons le quaternion
osg::Quat quat(osg::PI * 0.01, osg::X_AXIS);
c'est-à -dire que nous avons défini un quaternion qui implémente une rotation autour de l'axe X d'un angle de 0,01 * Pi. Faire pivoter le sommet en multipliant le quaternion par un vecteur définissant les coordonnées du sommet
vertices->back() = quat * vertices->back();
Les deux derniers appels racontent la liste d'affichage et le parallélépipède dimensionnel pour la géométrie modifiée
quad->dirtyDisplayList(); quad->dirtyBound();
Dans le corps de la fonction main (), nous créons un carré, définissons le mode de dessin dynamique et ajoutons un rappel modifiant la géométrie
osg::Geometry *quad = createQuad(); quad->setDataVariance(osg::Object::DYNAMIC); quad->setUpdateCallback(new DynamicQuadCallback);
Je vais quitter sans discernement la création du nœud racine et le lancement de la visionneuse, comme nous l'avons déjà fait au moins vingt fois de différentes manières. En conséquence, nous avons l'animation de morphing la plus simple

Essayez maintenant de supprimer (commenter) l'appel setDataVariance (). Peut-être que nous ne verrons rien de criminel dans ce cas - par défaut, OSG essaie de déterminer automatiquement quand mettre à jour les données de géométrie, en essayant de se synchroniser avec le rendu. Ensuite, essayez de changer le mode de DYNAMIQUE en STATIQUE et vous verrez que l'image ne s'affiche pas en douceur, avec des secousses, des erreurs et des avertissements notables comme celui-ci
Warning: detected OpenGL error 'invalid value' at after RenderBin::draw(..)
Si vous n'exécutez pas la méthode dirtyDisplayList (), OpenGL ignorera toutes les modifications de la géométrie et utilisera la liste d'affichage créée au tout début pour créer le carré pour le rendu. Supprimez cet appel et vous verrez qu'il n'y a pas d'animation.
Sans appeler la méthode dirtyBound (), la boîte englobante ne sera pas recalculée et OSG coupera incorrectement les faces invisibles.
2. Le concept d'interpolation de mouvement
Supposons qu'un train allant de la gare A à la gare B prenne 15 minutes pour voyager. Comment simuler cette situation en modifiant la position du train lors du rappel? Le moyen le plus simple est de corréler la position de la gare A avec l'heure 0 et de la gare B avec 15 minutes et de déplacer le train de manière égale entre ces heures. Cette approche la plus simple est appelée interpolation linéaire. En interpolation linéaire, un vecteur spécifiant la position d'un point intermédiaire est décrit par la formule
p = (1 - t) * p0 + t * p1
où p0 est le point de départ; p1 est le point final; t est un paramètre qui varie uniformément de 0 à 1. Cependant, le mouvement du train est beaucoup plus compliqué: il quitte la gare A, accélère, puis se déplace à vitesse constante, puis ralentit, s'arrête à la gare B.Un tel processus ne peut plus décrire l'interpolation linéaire et Ça n'a pas l'air naturel.
OSG fournit au développeur la bibliothèque osgAnimation, qui contient un certain nombre d'algorithmes d'interpolation standard utilisés pour animer en douceur le mouvement des objets de scène. Chacune de ces fonctions a généralement deux arguments: la valeur initiale du paramètre (généralement 0) et la valeur finale du paramètre (généralement 1). Ces fonctions peuvent être appliquées au début du mouvement (InMotion), à la fin du mouvement (OutMotion) ou au début et à la fin du mouvement (InOutMotion)
Type de mouvement | en classe | en classe | classe entrée / sortie |
---|
Interpolation linéaire | LinearMotion | - | - |
Interpolation quadratique | InQuadMotion | OutQuadMotion | InOutQuadMotion |
Interpolation cubique | InCubicMotion | Outcubicmotion | InOutCubicMotion |
Interpolation Ă 4 ordres | InQuartMotion | OutQuartMotion | InOutQuartMotion |
Interpolation d'effet de rebond | InBounceMotion | OutBounceMotion | InOutBounceMotion |
Interpolation de rebond élastique | InElasticMotion | OutElasticMotion | InOutElasticMotion |
Interpolation sinusoĂŻdale | InSineMotion | Outsinemotion | InOutSineMotion |
Interpolation inverse | Inbackmotion | Outbackmotion | InOutBackMotion |
Interpolation circulaire | InCircMotion | Outcircmotion | InOutCircMotion |
Interpolation exponentielle | InExpoMotion | Outexpomotion | InOutExpoMotion |
Pour créer une interpolation linéaire du mouvement d'un objet, nous écrivons un tel code
osg::ref_ptr<osgAnimation::LinearMotion> motion = new osgAnimation::LinearMotion(0.0f, 1.0f);
3. Animation des nœuds de transformation
L'animation de trajectoire est le type d'animation le plus courant dans les applications graphiques. Cette technique peut être utilisée pour animer le mouvement d’une voiture, le vol d’un avion ou le mouvement d’une caméra. La trajectoire est prédéfinie, avec toutes les positions, rotations et changements d'échelle à des moments clés. Lorsque le cycle de simulation démarre, l'état de l'objet est recalculé dans chaque image, en utilisant une interpolation linéaire pour la position et la mise à l'échelle et une interpolation linéaire sphérique pour les quaternions de rotation. Pour ce faire, utilisez la méthode interne slerp () de la classe osg :: Quat.
OSG fournit la classe osg :: AnimationPath pour décrire un chemin variant dans le temps. La méthode de cette classe insert () est utilisée pour ajouter à la trajectoire des points de contrôle correspondant à certains points dans le temps. Le point de contrôle est décrit par la classe osg :: AnimationPath :: ControlPoint, dont le constructeur prend la position comme paramètres et, éventuellement, les paramètres de rotation et de mise à l'échelle de l'objet. Par exemple
osg::ref_ptr<osg::AnimationPath> path = new osg::AnimationPath; path->insert(t1, osg::AnimationPath::ControlPoint(pos1, rot1, scale1)); path->insert(t2, ...);
Ici t1, t2 sont des instants de temps en secondes; rot1 est le paramètre de rotation à l'instant t1, décrit par le quaternion osg :: Quat.
Il est possible de contrôler les boucles d'animation via la méthode setLoopMode (). Par défaut, le mode LOOP est activé - l'animation sera répétée en continu. Autres valeurs possibles: NO_LOOPING - joue l'animation une fois et SWING - boucle le mouvement dans les directions avant et arrière.
Une fois l'initialisation terminée, nous attachons l'objet osg :: AnimationPath à l'objet intégré osg :: AnimationPathCallback, qui est dérivé de la classe osg :: NodeCallback.
4. Un exemple d'animation de mouvement le long d'un chemin
Nous allons maintenant faire bouger notre cessna dans un cercle avec le centre au point (0,0,0). La position de l'avion sur la trajectoire sera calculée en interpolant linéairement la position et l'orientation entre les images clés.
Exemple Animcessnamain.h #ifndef MAIN_H #define MAIN_H #include <osg/AnimationPath> #include <osg/MatrixTransform> #include <osgDB/ReadFile> #include <osgViewer/Viewer> #endif
main.cpp #include "main.h"
Nous commençons par créer la trajectoire de l'avion, en prenant ce code dans une fonction distincte
osg::AnimationPath *createAnimationPath(double radius, double time) { osg::ref_ptr<osg::AnimationPath> path = new osg::AnimationPath; path->setLoopMode(osg::AnimationPath::LOOP); unsigned int numSamples = 32; double delta_yaw = 2.0 * osg::PI / (static_cast<double>(numSamples) - 1.0); double delta_time = time / static_cast<double>(numSamples); for (unsigned int i = 0; i < numSamples; ++i) { double yaw = delta_yaw * i; osg::Vec3d pos(radius * sin(yaw), radius * cos(yaw), 0.0); osg::Quat rot(-yaw, osg::Z_AXIS); path->insert(delta_time * i, osg::AnimationPath::ControlPoint(pos, rot)); } return path.release(); }
Comme paramètres, la fonction prend le rayon du cercle le long duquel l'avion se déplace et le temps pendant lequel il fera un tour. À l'intérieur de la fonction, créez un objet trajectoire et activez le mode de boucle d'animation
osg::ref_ptr<osg::AnimationPath> path = new osg::AnimationPath; path->setLoopMode(osg::AnimationPath::LOOP);
Code suivant
unsigned int numSamples = 32; double delta_yaw = 2.0 * osg::PI / (static_cast<double>(numSamples) - 1.0); double delta_time = time / static_cast<double>(numSamples);
calcule les paramètres d'approximation de la trajectoire. Nous divisons la trajectoire entière en numSamples de sections droites et calculons le changement de l'angle de rotation du plan autour de l'axe vertical (lacet) delta_yaw et le changement de temps delta_time lors du passage d'une section à l'autre. Créez maintenant les points de contrôle nécessaires
for (unsigned int i = 0; i < numSamples; ++i) { double yaw = delta_yaw * i; osg::Vec3d pos(radius * sin(yaw), radius * cos(yaw), 0.0); osg::Quat rot(-yaw, osg::Z_AXIS); path->insert(delta_time * i, osg::AnimationPath::ControlPoint(pos, rot)); }
Dans le cycle, toutes les sections de la trajectoire de la première à la dernière sont triées. Chaque point de contrôle est caractérisé par un angle de lacet
double yaw = delta_yaw * i;
la position du centre de masse de l'aéronef dans l'espace
osg::Vec3d pos(radius * sin(yaw), radius * cos(yaw), 0.0);
La rotation de l'avion à l'angle de lacet souhaité (par rapport à l'axe vertical) est réglée par le quaternion
osg::Quat rot(-yaw, osg::Z_AXIS);
puis ajoutez les paramètres calculés à la liste des points de contrôle du chemin
path->insert(delta_time * i, osg::AnimationPath::ControlPoint(pos, rot));
Dans le programme principal, nous prêtons attention à la nuance en indiquant le nom du fichier de modèle d'avion au démarrage
osg::ref_ptr<osg::Node> model = osgDB::readNodeFile("../data/cessna.osg.0,0,90.rot");
- un suffixe ".0,0,90.rot" a été ajouté au nom du fichier. Le mécanisme de chargement de la géométrie à partir d'un fichier utilisé dans OSG vous permet de spécifier la position et l'orientation initiales du modèle après le chargement. Dans ce cas, nous voulons que le modèle pivote de 90 degrés autour de l'axe Z après le chargement.
Ensuite, le nœud racine est créé, qui est le nœud de transformation, et l'objet modèle lui est ajouté en tant que nœud enfant
osg::ref_ptr<osg::MatrixTransform> root = new osg::MatrixTransform; root->addChild(model.get());
Créez maintenant un rappel d'animation de trajectoire, en y ajoutant le chemin créé par la fonction createAnimationPath ()
osg::ref_ptr<osg::AnimationPathCallback> apcb = new osg::AnimationPathCallback; apcb->setAnimationPath(createAnimationPath(50.0, 6.0));
Attachez ce rappel au nœud de transformation
root->setUpdateCallback(apcb.get());
Le visualiseur est initialisé et lancé comme d'habitude.
osgViewer::Viewer viewer; viewer.setSceneData(root.get()); return viewer.run();
Obtenez une animation de mouvement d'avion

Vous pensez que vous n'avez rien trouvé d'étrange dans cet exemple? Auparavant, par exemple, dans un programme lors du rendu en texture, vous avez explicitement modifié la matrice de transformation pour obtenir un changement de position du modèle dans l'espace. Ici, nous créons simplement un nœud de transformation et dans le code, il n'y a aucune affectation de matrice explicite nulle part.
Le secret est que la classe spéciale osg :: AnimationPathCallback fait ce travail. Conformément à la position actuelle de l'objet sur le chemin, il calcule la matrice de transformation et l'applique automatiquement au noeud de transformation auquel il est attaché, ce qui évite au développeur un tas d'opérations de routine.
Il convient de noter que la connexion d'osg :: AnimationPathCallback à d'autres types de nœuds n'aura pas seulement aucun effet, mais peut également conduire à un comportement de programme non défini. Il est important de se rappeler que ce rappel n'affecte que les nœuds de transformation.
5. Animation du contrĂ´le logiciel
La classe osg :: AnimationPathCallback fournit des méthodes pour contrôler l'animation pendant l'exécution du programme.
- reset () - réinitialise l'animation et la joue d'abord.
- setPause () - interrompt l'animation. Prend une valeur booléenne comme paramètre
- setTimeOffset () - définit le décalage temporel avant le début de l'animation.
- setTimeMultiplier () - définit le facteur de temps pour l'accélération / décélération de l'animation.
Par exemple, pour supprimer l'animation de la pause et de la réinitialisation, nous exécutons ce code
apcb->setPause(false); apcb->reset();
et pour démarrer l'animation à partir de la quatrième seconde après avoir démarré le programme avec une double accélération, un tel code
apcb->setTimeOffset(4.0f); apcb->setTimeMultiplier(2.0f);
6. L'ordre de rendu des primitives dans OpenGL
OpenGL stocke les sommets et les données primitives dans divers tampons, tels qu'un tampon de couleur, un tampon de profondeur, un tampon de gabarit, etc. De plus, il n'écrase pas les sommets et faces triangulaires déjà envoyés à son pipeline. Cela signifie qu'OpenGL crée une nouvelle géométrie, quelle que soit la façon dont la géométrie existante a été créée. Cela signifie que l'ordre dans lequel les primitives sont envoyées au pipeline de rendu affecte de manière significative le résultat final que nous voyons à l'écran.
Sur la base des données du tampon de profondeur, OpenGL dessine correctement les objets opaques, triant les pixels en fonction de leur distance par rapport à l'observateur. Cependant, lors de l'utilisation de la technique de mélange de couleurs, par exemple, lors de la mise en œuvre d'objets transparents et translucides, une opération spéciale sera effectuée pour mettre à jour le tampon de couleurs. Les nouveaux et anciens pixels de l'image sont mélangés, en tenant compte de la valeur du canal alpha (quatrième composante de couleur). Cela conduit au fait que l'ordre de rendu des bords translucides et opaques affecte le résultat final

Sur la figure, dans la situation de gauche, des objets d'abord opaques puis transparents ont été envoyés au pipeline, ce qui a conduit au décalage correct du tampon de couleurs et à l'affichage correct des visages. Dans la bonne situation, les premiers objets transparents ont été dessinés, puis opaques, ce qui a conduit à un affichage incorrect.
La méthode setRenderingHint () de la classe osg :: StateSet indique à OSG l'ordre de rendu requis des nœuds et des objets géométriques, si cela doit être fait explicitement. Cette méthode indique simplement si les visages translucides doivent ou non être pris en compte lors du rendu, garantissant ainsi que s'il y a des visages translucides dans la scène, les visages opaques puis transparents seront dessinés en premier, en tenant compte de la distance de l'observateur. Pour informer le moteur que ce nœud est opaque, nous utilisons ce code
node->getOrCreateStateSet()->setRenderingHint(osg::StateSet::OPAQUE_BIN);
ou contient des bords transparents
node->getOrCreateStateSet()->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
7. Un exemple de mise en œuvre d'objets translucides
Essayons d'illustrer toute l'introduction théorique ci-dessus avec un exemple concret de mise en œuvre d'un objet translucide.
Exemple de transparencemain.h #ifndef MAIN_H #define MAIN_H #include <osg/BlendFunc> #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::Vec4Array> colors = new osg::Vec4Array; colors->push_back( osg::Vec4(1.0f, 1.0f, 1.0f, 0.5f) ); 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_OVERALL); quad->setTexCoordArray(0, texcoords.get()); quad->addPrimitiveSet(new osg::DrawArrays(GL_QUADS, 0, 4)); osg::ref_ptr<osg::Geode> geode = new osg::Geode; geode->addDrawable(quad.get()); 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::BlendFunc> blendFunc = new osg::BlendFunc; blendFunc->setFunction(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); osg::StateSet *stateset = geode->getOrCreateStateSet(); stateset->setTextureAttributeAndModes(0, texture.get()); stateset->setAttributeAndModes(blendFunc); osg::ref_ptr<osg::Group> root = new osg::Group; root->addChild(geode.get()); root->addChild(osgDB::readNodeFile("../data/glider.osg")); osgViewer::Viewer viewer; viewer.setSceneData(root.get()); return viewer.run(); }
Pour la plupart, le code présenté ici ne contient rien de nouveau: deux objets géométriques sont créés - un carré texturé et un deltaplane, dont le modèle est chargé à partir d'un fichier. Cependant, nous appliquons une couleur blanche translucide à tous les sommets du carré
colors->push_back( osg::Vec4(1.0f, 1.0f, 1.0f, 0.5f) );
- la valeur du canal alpha est de 0,5, ce qui, lorsqu'il est mélangé avec des couleurs de texture, devrait donner l'effet d'un objet translucide. En outre, la fonction de fusion des couleurs doit être définie pour le traitement de la transparence.
osg::ref_ptr<osg::BlendFunc> blendFunc = new osg::BlendFunc; blendFunc->setFunction(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
le passer à la machine d'état OpenGL
stateset->setAttributeAndModes(blendFunc);
Lors de la compilation et de l'exécution de ce programme, nous obtenons le résultat suivant

Arrête ça! Et où est la transparence? Le fait est que nous avons oublié de dire au moteur que les bords transparents doivent être traités, ce qui est facilement résolu en appelant
stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
après quoi nous obtenons le résultat dont nous avons besoin - l'aile de deltaplane brille à travers un carré texturé translucide

Les paramètres des fonctions de mélange GL_SRC_ALPHA et GL_ONE_MINUS_SRC_ALPHA signifient que le pixel d'écran résultant lors du dessin d'une face translucide aura des composantes de couleur calculées par la formule
R = srcR * srcA + dstR * (1 - srcA) G = srcG * srcA + dstG * (1 - srcA) B = srcB * srcA + dstB * (1 - srcA)
où [srcR, srcG, srcB] sont les composantes de couleur de la texture carrée; [dstR, dstG, dstB] — , , . srcA - .
seRenderingHint() , , . , .
8.
À l'aide de l'animation, vous pouvez également contrôler les attributs d'état. Une multitude d'effets visuels peuvent être générés en modifiant les propriétés d'un ou plusieurs attributs de rendu. Ce type d'animation qui modifie l'état des attributs de rendu est facile à implémenter via le mécanisme de rappel lors de la mise à jour de la scène.Des classes d'interpolations standard peuvent également être utilisées pour spécifier la fonction de modification des paramètres d'attribut.Nous avons déjà de l'expérience dans la création d'objets translucides. Nous savons que si la composante alpha de la couleur est nulle, nous obtenons un objet complètement transparent, avec une valeur de 1 - complètement opaque. Il est clair qu'en faisant varier ce paramètre de 0 à 1 dans le temps, l'effet de l'apparition ou de la disparition progressive d'un objet peut être obtenu. Nous illustrons cela avec un exemple concret.Exemple de fondumain.h #ifndef MAIN_H #define MAIN_H #include <osg/Geode> #include <osg/Geometry> #include <osg/BlendFunc> #include <osg/Material> #include <osgAnimation/EaseMotion> #include <osgDB/ReadFile> #include <osgViewer/Viewer> #endif
main.cpp #include "main.h"
- -
class AlphaFadingCallback : public osg::StateAttributeCallback { public: AlphaFadingCallback() { _motion = new osgAnimation::InOutCubicMotion(0.0f, 1.0f); } virtual void operator() (osg::StateAttribute* , osg::NodeVisitor*); protected: osg::ref_ptr<osgAnimation::InOutCubicMotion> _motion; };
_motion , . , ,
AlphaFadingCallback() { _motion = new osgAnimation::InOutCubicMotion(0.0f, 1.0f); }

InOutCubicMotion 0 1. operator()
void AlphaFadingCallback::operator()(osg::StateAttribute *sa, osg::NodeVisitor *nv) { (void) nv; osg::Material *material = static_cast<osg::Material *>(sa); if (material) { _motion->update(0.0005f); float alpha = _motion->getValue(); material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4(0.0f, 1.0f, 1.0f, alpha)); } }
osg::Material *material = static_cast<osg::Material *>(sa);
callback , , , . — ,
_motion->update(0.0005f);
float alpha = _motion->getValue();
material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4(0.0f, 1.0f, 1.0f, alpha));
main(). , — OSG
osg::ref_ptr<osg::Drawable> quad = osg::createTexturedQuadGeometry( osg::Vec3(-0.5f, 0.0f, -0.5f), osg::Vec3(1.0f, 0.0f, 0.0f), osg::Vec3(0.0f, 0.0f, 1.0f));
Le premier paramètre est le point à partir duquel le coin inférieur gauche du carré sera construit, les deux autres paramètres spécifient les coordonnées des diagonales. Après avoir compris le carré, nous créons du matériel pour lui osg::ref_ptr<osg::Material> material = new osg::Material; material->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4(0.0f, 0.0f, 0.0f, 1.0f)); material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4(0.0f, 1.0f, 1.0f, 0.5f));
Nous indiquons les options de couleur du matériau. La couleur ambiante est un paramètre qui caractérise la couleur du matériau dans la zone ombrée, inaccessible aux sources de couleur. La couleur diffuse est la couleur propre du matériau, qui caractérise la capacité d'une surface à dissiper la couleur qui y tombe, c'est-à -dire ce que nous appelons la couleur dans la vie quotidienne. Le paramètre FRONT_AND_BACK indique que cet attribut de couleur est affecté aux côtés avant et arrière des faces de la géométrie.Attribuez un matériau au gestionnaire précédemment créé. material->setUpdateCallback(new AlphaFadingCallback);
Attribuer le matériau créé au carré geode->getOrCreateStateSet()->setAttributeAndModes(material.get());
— ,
geode->getOrCreateStateSet()->setAttributeAndModes(new osg::BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); geode->getOrCreateStateSet()->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
osg::ref_ptr<osg::Group> root = new osg::Group; root->addChild(geode.get()); root->addChild(osgDB::readNodeFile("../data/glider.osg")); osgViewer::Viewer viewer; viewer.setSceneData(root.get()); return viewer.run();

:
, . – main.h
#include <osgAnimation/EaseMotion>
OSG, , , , . osgAnimation/ , , ( )
LIBS += -losgAnimation
Ă€ suivre ...