OpenSceneGraph: animaci贸n procesal de geometr铆a y atributos de estado

imagen

Introduccion


Hablando de t茅cnicas de programaci贸n espec铆ficas para OSG , la 煤ltima vez que hablamos sobre el mecanismo de devoluci贸n de llamada y su implementaci贸n en el motor. Es hora de ver las posibilidades que ofrece este mecanismo para administrar el contenido de una escena tridimensional.

Si hablamos de animaci贸n de objetos, OSG proporciona al desarrollador dos opciones para su implementaci贸n:

  1. Animaci贸n procesal implementada program谩ticamente a trav茅s de la transformaci贸n de objetos y sus atributos.
  2. Exportar animaci贸n desde un editor 3D y administrarla desde el c贸digo de la aplicaci贸n

Para comenzar, considere la primera posibilidad, como la m谩s obvia. Definitivamente hablaremos sobre el segundo un poco m谩s tarde.

1. Animaci贸n de transformaci贸n procesal


Al atravesar el gr谩fico de escena, OSG transfiere datos a la canalizaci贸n de OpenGL, que se ejecuta en un hilo separado. Este hilo debe estar sincronizado con otros hilos de procesamiento en cada cuadro. De lo contrario, el m茅todo frame () puede completarse antes de procesar los datos de geometr铆a. Esto conducir谩 a comportamientos y bloqueos impredecibles del programa. OSG ofrece una soluci贸n a este problema en forma del m茅todo setDataVariance () de la clase osg :: Object, que es la base para todos los objetos de escena. Puede configurar tres modos de procesamiento para objetos

  1. NO ESPECIFICADO (por defecto): OSG determina independientemente el orden de procesamiento del objeto.
  2. EST脕TICO: el objeto es inmutable y el orden de su procesamiento no es importante. Acelera significativamente el renderizado.
  3. DIN脕MICO: el objeto debe procesarse antes del inicio de la representaci贸n.

Esta configuraci贸n se puede establecer en cualquier momento llamando

node->setDataVariance( osg::Object::DYNAMIC ); 

La pr谩ctica generalmente aceptada es modificar la geometr铆a "sobre la marcha", es decir, cambiar las coordenadas de v茅rtices, colores normales y texturas din谩micamente en cada cuadro, obteniendo geometr铆a mutable. Esta t茅cnica se llama animaci贸n de transformaci贸n. En este caso, el orden de procesamiento de la geometr铆a es decisivo: todos sus cambios deben recalcularse antes de que comience el dibujo. Para ilustrar este truco, modificamos ligeramente el ejemplo cuadrado de color, obligando a uno de sus v茅rtices a girar alrededor del eje X.

Ejemplo de Animquad
main.h

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

main.cpp

 #include "main.h" //------------------------------------------------------------------------------ // //------------------------------------------------------------------------------ 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(); } //------------------------------------------------------------------------------ // //------------------------------------------------------------------------------ class DynamicQuadCallback : public osg::Drawable::UpdateCallback { public: virtual void update(osg::NodeVisitor *, osg::Drawable *drawable); }; //------------------------------------------------------------------------------ // //------------------------------------------------------------------------------ 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(); } //------------------------------------------------------------------------------ // //------------------------------------------------------------------------------ int main(int argc, char *argv[]) { (void) argc; (void) argv; osg::Geometry *quad = createQuad(); quad->setDataVariance(osg::Object::DYNAMIC); quad->setUpdateCallback(new DynamicQuadCallback); osg::ref_ptr<osg::Geode> root = new osg::Geode; root->addDrawable(quad); osgViewer::Viewer viewer; viewer.setSceneData(root.get()); return viewer.run(); } 


Crearemos un cuadrado en una funci贸n separada

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

una descripci贸n de la cual, en principio, no es necesaria, ya que hemos realizado tales acciones repetidamente. Para modificar los v茅rtices de este cuadrado, escribimos la clase DynamicQuadCallback, heredando de osg :: Drawable :: UpdateCallback

 class DynamicQuadCallback : public osg::Drawable::UpdateCallback { public: virtual void update(osg::NodeVisitor *, osg::Drawable *drawable); }; 

anulando el m茅todo update () en 茅l

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

Aqu铆 obtenemos un puntero a un objeto de geometr铆a

 osg::Geometry *quad = static_cast<osg::Geometry *>(drawable); 

leemos de la geometr铆a una lista de v茅rtices (o m谩s bien un puntero a ella)

 osg::Vec3Array *vertices = static_cast<osg::Vec3Array *>(quad->getVertexArray()); 

Para obtener el 煤ltimo elemento (煤ltimo v茅rtice) en la matriz, la clase osg :: Array proporciona el m茅todo back (). Para rotar el v茅rtice en relaci贸n con el eje X, presentamos

 osg::Quat quat(osg::PI * 0.01, osg::X_AXIS); 

es decir, establecemos un cuaterni贸n que implementa una rotaci贸n alrededor del eje X en un 谩ngulo de 0.01 * Pi. Gire el v茅rtice multiplicando el cuaterni贸n por un vector que defina las coordenadas del v茅rtice

 vertices->back() = quat * vertices->back(); 

Las 煤ltimas dos llamadas relatan la lista de visualizaci贸n y el paralelep铆pedo dimensional para la geometr铆a modificada.

 quad->dirtyDisplayList(); quad->dirtyBound(); 

En el cuerpo de la funci贸n main (), creamos un cuadrado, establecemos el modo de dibujo din谩mico para 茅l y agregamos una devoluci贸n de llamada modificando la geometr铆a

 osg::Geometry *quad = createQuad(); quad->setDataVariance(osg::Object::DYNAMIC); quad->setUpdateCallback(new DynamicQuadCallback); 

Dejar茅 indiscriminadamente la creaci贸n del nodo ra铆z y el lanzamiento del visor, ya que ya lo hemos hecho al menos veinte veces de diferentes maneras. Como resultado, tenemos la animaci贸n de transformaci贸n m谩s simple



Ahora intente eliminar (comentar) la llamada setDataVariance (). Quiz谩s no veamos nada criminal en este caso: de forma predeterminada, OSG intenta determinar autom谩ticamente cu谩ndo actualizar los datos de geometr铆a, tratando de sincronizar con el renderizado. Luego intente cambiar el modo de DIN脕MICO a EST脕TICO y ver谩 que la imagen no se procesa sin problemas, con sacudidas, errores y advertencias notables como esta

 Warning: detected OpenGL error 'invalid value' at after RenderBin::draw(..) 

Si no ejecuta el m茅todo dirtyDisplayList (), OpenGL ignorar谩 todos los cambios en la geometr铆a y utilizar谩 la lista de visualizaci贸n creada al principio para crear el cuadrado para la representaci贸n. Elimine esta llamada y ver谩 que no hay animaci贸n.

Sin llamar al m茅todo dirtyBound (), el cuadro delimitador no se volver谩 a calcular y OSG recortar谩 incorrectamente las caras invisibles.

2. El concepto de interpolaci贸n de movimiento.


Suponga que un tren que va de la estaci贸n A a la estaci贸n B tarda 15 minutos en viajar. 驴C贸mo podemos simular esta situaci贸n cambiando la posici贸n del tren en la devoluci贸n de llamada? La forma m谩s f谩cil es correlacionar la posici贸n de la estaci贸n A con el tiempo 0 y la estaci贸n B con 15 minutos y mover el tren de manera uniforme entre estos tiempos. Este enfoque m谩s simple se llama interpolaci贸n lineal. En la interpolaci贸n lineal, la f贸rmula describe un vector que especifica la posici贸n de un punto intermedio

 p = (1 - t) * p0 + t * p1 

donde p0 es el punto de partida; p1 es el punto final; t es un par谩metro que var铆a uniformemente de 0 a 1. Sin embargo, el movimiento del tren es mucho m谩s complicado: sale de la estaci贸n A, acelera, luego se mueve a una velocidad constante y luego disminuye la velocidad, deteni茅ndose en la estaci贸n B. Tal proceso ya no puede describir la interpolaci贸n lineal y Se ve antinatural.

OSG proporciona al desarrollador la biblioteca osgAnimation, que contiene varios algoritmos de interpolaci贸n est谩ndar utilizados para animar sin problemas el movimiento de los objetos de la escena. Cada una de estas funciones generalmente tiene dos argumentos: el valor inicial del par谩metro (generalmente 0) y el valor final del par谩metro (generalmente 1). Estas funciones se pueden aplicar al inicio del movimiento (InMotion), al final del movimiento (OutMotion) o al inicio y al final del movimiento (InOutMotion)

Tipo de movimientoen clasefuera de claseclase de entrada / salida
Interpolaci贸n linealMovimiento lineal--
Interpolaci贸n cuadr谩ticaInQuadMotionOutQuadMotionInOutQuadMotion
Interpolaci贸n c煤bicaInCubicMotionOutcubicmotionInOutCubicMotion
Interpolaci贸n de 4 贸rdenesInQuartMotionOutQuartMotionInOutQuartMotion
Interpolaci贸n de efecto de reboteInBounceMotionOutBounceMotionInOutBounceMotion
Interpolaci贸n de rebote el谩sticoInElasticMotionOutElasticMotionInOutElasticMotion
Interpolaci贸n sinusoidalInSineMotionOutsinemotionInOutSineMotion
Interpolaci贸n inversaInbackmotionOutbackmotionInOutBackMotion
Interpolaci贸n circularInCircMotionMovimiento exteriorInOutCircMotion
Interpolaci贸n exponencialInExpoMotionOutexpomotionInOutExpoMotion

Para crear una interpolaci贸n lineal del movimiento de un objeto, escribimos dicho c贸digo

 osg::ref_ptr<osgAnimation::LinearMotion> motion = new osgAnimation::LinearMotion(0.0f, 1.0f); 

3. Animaci贸n de nodos de transformaci贸n.


La animaci贸n de trayectoria es el tipo de animaci贸n m谩s com煤n en aplicaciones gr谩ficas. Esta t茅cnica se puede utilizar para animar el movimiento de un autom贸vil, el vuelo de un avi贸n o el movimiento de la c谩mara. La trayectoria est谩 predefinida, con todas las posiciones, rotaciones y cambios de escala en puntos clave en el tiempo. Cuando comienza el ciclo de simulaci贸n, el estado del objeto se recalcula en cada cuadro, utilizando la interpolaci贸n lineal para la posici贸n y la escala y la interpolaci贸n lineal esf茅rica para los cuaterniones de rotaci贸n. Para hacer esto, use el m茅todo interno slerp () de la clase osg :: Quat.

OSG proporciona la clase osg :: AnimationPath para describir una ruta que var铆a en el tiempo. El m茅todo de esta clase insert () se usa para agregar puntos de control correspondientes a ciertos puntos en el tiempo a la trayectoria. El punto de control es descrito por la clase osg :: AnimationPath :: ControlPoint, cuyo constructor toma la posici贸n como par谩metros y, opcionalmente, la rotaci贸n de objetos y los par谩metros de escala. Por ejemplo

 osg::ref_ptr<osg::AnimationPath> path = new osg::AnimationPath; path->insert(t1, osg::AnimationPath::ControlPoint(pos1, rot1, scale1)); path->insert(t2, ...); 

Aqu铆 t1, t2 son instantes de tiempo en segundos; rot1 es el par谩metro de rotaci贸n en el tiempo t1, descrito por el cuaterni贸n osg :: Quat.

Es posible controlar los bucles de animaci贸n a trav茅s del m茅todo setLoopMode (). De manera predeterminada, el modo LOOP est谩 activado: la animaci贸n se repetir谩 continuamente. Otros valores posibles: NO_LOOPING - reproduce la animaci贸n una vez y SWING - repite el movimiento en las direcciones hacia adelante y hacia atr谩s.

Despu茅s de completar toda la inicializaci贸n, adjuntamos el objeto osg :: AnimationPath al objeto incorporado osg :: AnimationPathCallback, que se deriva de la clase osg :: NodeCallback.

4. Un ejemplo de una animaci贸n de movimiento a lo largo de un camino.


Ahora haremos que nuestro cessna se mueva en c铆rculo con el centro en el punto (0,0,0). La posici贸n de la aeronave en la trayectoria se calcular谩 interpolando linealmente la posici贸n y orientaci贸n entre cuadros clave.

Ejemplo de animcessna
main.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" //------------------------------------------------------------------------------ // //------------------------------------------------------------------------------ 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(); } //------------------------------------------------------------------------------ // //------------------------------------------------------------------------------ int main(int argc, char *argv[]) { (void) argc; (void) argv; osg::ref_ptr<osg::Node> model = osgDB::readNodeFile("../data/cessna.osg.0,0,90.rot"); osg::ref_ptr<osg::MatrixTransform> root = new osg::MatrixTransform; root->addChild(model.get()); osg::ref_ptr<osg::AnimationPathCallback> apcb = new osg::AnimationPathCallback; apcb->setAnimationPath(createAnimationPath(50.0, 6.0)); root->setUpdateCallback(apcb.get()); osgViewer::Viewer viewer; viewer.setSceneData(root.get()); return viewer.run(); } 


Comenzamos creando la trayectoria del avi贸n, tomando este c贸digo en una funci贸n separada

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

Como par谩metros, la funci贸n toma el radio del c铆rculo a lo largo del cual se mueve el avi贸n y el tiempo durante el cual har谩 una revoluci贸n. Dentro de la funci贸n, cree un objeto de trayectoria y active el modo de bucle de animaci贸n

 osg::ref_ptr<osg::AnimationPath> path = new osg::AnimationPath; path->setLoopMode(osg::AnimationPath::LOOP); 

C贸digo siguiente

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

calcula los par谩metros de aproximaci贸n de la trayectoria. Dividimos toda la trayectoria en numSamples de secciones rectas, y calculamos el cambio en el 谩ngulo de rotaci贸n del plano alrededor del eje vertical (yaw) delta_yaw y el cambio en el tiempo delta_time cuando se mueve de una secci贸n a otra. Ahora crea los puntos de control necesarios

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

En el ciclo, se ordenan todas las secciones de la trayectoria, desde la primera hasta la 煤ltima. Cada punto de control se caracteriza por un 谩ngulo de gui帽ada

 double yaw = delta_yaw * i; 

la posici贸n del centro de masa de la aeronave en el espacio

 osg::Vec3d pos(radius * sin(yaw), radius * cos(yaw), 0.0); 

El cuaterni贸n establece la rotaci贸n de la aeronave al 谩ngulo de gui帽ada deseado (en relaci贸n con el eje vertical)

 osg::Quat rot(-yaw, osg::Z_AXIS); 

y luego agregue los par谩metros calculados a la lista de puntos de control de la ruta

 path->insert(delta_time * i, osg::AnimationPath::ControlPoint(pos, rot)); 

En el programa principal, prestamos atenci贸n al matiz al indicar el nombre del archivo de modelo de la aeronave al momento del arranque

 osg::ref_ptr<osg::Node> model = osgDB::readNodeFile("../data/cessna.osg.0,0,90.rot"); 

- se agreg贸 un sufijo ".0,0,90.rot" al nombre del archivo. El mecanismo para cargar geometr铆a desde un archivo utilizado en OSG le permite especificar la posici贸n inicial y la orientaci贸n del modelo despu茅s de la carga. En este caso, queremos que el modelo gire 90 grados alrededor del eje Z despu茅s de cargarlo.

A continuaci贸n, se crea el nodo ra铆z, que es el nodo de transformaci贸n, y el objeto modelo se agrega como un nodo secundario

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

Ahora cree una devoluci贸n de llamada de animaci贸n de trayectoria, agregando la ruta creada por la funci贸n createAnimationPath ()

 osg::ref_ptr<osg::AnimationPathCallback> apcb = new osg::AnimationPathCallback; apcb->setAnimationPath(createAnimationPath(50.0, 6.0)); 

Adjunte esta devoluci贸n de llamada al nodo de transformaci贸n

 root->setUpdateCallback(apcb.get()); 

El visor se inicializa y se inicia como de costumbre.

 osgViewer::Viewer viewer; viewer.setSceneData(root.get()); return viewer.run(); 

Obt茅n una animaci贸n de movimiento de avi贸n



驴Crees que no encontraste nada extra帽o en este ejemplo? Anteriormente, por ejemplo, en un programa cuando se renderizaba a una textura, cambiaba expl铆citamente la matriz de transformaci贸n para lograr un cambio en la posici贸n del modelo en el espacio. Aqu铆 solo creamos un nodo de transformaci贸n y en el c贸digo no hay una asignaci贸n de matriz expl铆cita en ning煤n lado.

El secreto es que la clase especial osg :: AnimationPathCallback hace este trabajo. De acuerdo con la posici贸n actual del objeto en la ruta, calcula la matriz de transformaci贸n y la aplica autom谩ticamente al nodo de transformaci贸n al que est谩 conectado, salvando al desarrollador de un mont贸n de operaciones de rutina.

Cabe se帽alar que adjuntar osg :: AnimationPathCallback a otros tipos de nodos no solo no tendr谩 ning煤n efecto, sino que tambi茅n puede conducir a un comportamiento indefinido del programa. Es importante recordar que esta devoluci贸n de llamada solo afecta a los nodos de transformaci贸n.

5. Animaci贸n de control de software


La clase osg :: AnimationPathCallback proporciona m茅todos para controlar la animaci贸n durante la ejecuci贸n del programa.

  1. reset (): reinicia la animaci贸n y reprod煤cela primero.
  2. setPause (): pausa la animaci贸n. Toma un valor booleano como par谩metro
  3. setTimeOffset (): establece el desplazamiento de tiempo antes del inicio de la animaci贸n.
  4. setTimeMultiplier (): establece el factor de tiempo para la aceleraci贸n / desaceleraci贸n de la animaci贸n.

Por ejemplo, para eliminar la animaci贸n de la pausa y restablecer, ejecutamos dicho c贸digo

 apcb->setPause(false); apcb->reset(); 

y para comenzar la animaci贸n desde el cuarto segundo despu茅s de comenzar el programa con doble aceleraci贸n, tal c贸digo

 apcb->setTimeOffset(4.0f); apcb->setTimeMultiplier(2.0f); 

6. El orden de renderizar primitivas en OpenGL


OpenGL almacena v茅rtices y datos primitivos en varios b煤feres, como un b煤fer de color, un b煤fer de profundidad, un b煤fer de plantilla, etc. Adem谩s, no sobrescribe los v茅rtices y las caras triangulares ya enviadas a su tuber铆a. Esto significa que OpenGL crea una nueva geometr铆a, independientemente de c贸mo se cre贸 la geometr铆a existente. Esto significa que el orden en que se env铆an las primitivas a la canalizaci贸n de representaci贸n afecta significativamente el resultado final que vemos en la pantalla.

Seg煤n los datos del b煤fer de profundidad, OpenGL dibujar谩 correctamente objetos opacos, clasificando los p铆xeles de acuerdo con su distancia del observador. Sin embargo, al utilizar la t茅cnica de mezcla de colores, por ejemplo, al implementar objetos transparentes y transl煤cidos, se realizar谩 una operaci贸n especial para actualizar el b煤fer de color. Los p铆xeles nuevos y antiguos de la imagen se mezclan, teniendo en cuenta el valor del canal alfa (componente del cuarto color). Esto lleva al hecho de que el orden de renderizado de los bordes transl煤cidos y opacos afecta el resultado final



En la figura, en la situaci贸n de la izquierda, primero se enviaron objetos opacos y luego transparentes a la tuber铆a, lo que condujo al cambio correcto en el b煤fer de color y a la visualizaci贸n correcta de caras. En la situaci贸n correcta, primero se dibujaron objetos transparentes y luego opacos, lo que condujo a una visualizaci贸n incorrecta.

El m茅todo setRenderingHint () de la clase osg :: StateSet indica a OSG el orden de representaci贸n requerido de nodos y objetos geom茅tricos, si esto debe hacerse expl铆citamente. Este m茅todo simplemente indica si las caras transl煤cidas deben o no ser tomadas en cuenta al renderizar, asegurando as铆 que si hay caras transl煤cidas en la escena, primero se dibujar谩n caras opacas y luego transparentes, teniendo en cuenta la distancia del observador. Para informar al motor que este nodo es opaco, usamos este c贸digo

 node->getOrCreateStateSet()->setRenderingHint(osg::StateSet::OPAQUE_BIN); 

o contiene bordes transparentes

 node->getOrCreateStateSet()->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); 

7. Un ejemplo de la implementaci贸n de objetos transl煤cidos.


Tratemos de ilustrar toda la introducci贸n te贸rica anterior con un ejemplo concreto de la implementaci贸n de un objeto transl煤cido.

Ejemplo de transparencia
main.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(); } 


En su mayor parte, el c贸digo que se muestra aqu铆 no contiene nada nuevo: se crean dos objetos geom茅tricos: un cuadrado texturizado y un ala delta, cuyo modelo se carga desde un archivo. Sin embargo, aplicamos un color blanco transl煤cido a todos los v茅rtices del cuadrado.

 colors->push_back( osg::Vec4(1.0f, 1.0f, 1.0f, 0.5f) ); 

- el valor del canal alfa es 0.5, que, cuando se mezcla con colores de textura, deber铆a dar el efecto de un objeto transl煤cido. Adem谩s, la funci贸n de mezcla de colores debe establecerse para el procesamiento de transparencia.

 osg::ref_ptr<osg::BlendFunc> blendFunc = new osg::BlendFunc; blendFunc->setFunction(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 

pasarlo a la m谩quina de estado OpenGL

 stateset->setAttributeAndModes(blendFunc); 

Al compilar y ejecutar este programa, obtenemos el siguiente resultado



Basta! 驴Y d贸nde est谩 la transparencia? La cuesti贸n es que olvidamos decirle al motor que se deben procesar los bordes transparentes, lo cual se resuelve f谩cilmente llamando

 stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); 

despu茅s de lo cual obtenemos el resultado que necesitamos: el ala del ala delta brilla a trav茅s de un cuadrado con textura transl煤cida



Los par谩metros de las funciones de mezcla GL_SRC_ALPHA y GL_ONE_MINUS_SRC_ALPHA significan que el p铆xel de la pantalla resultante al dibujar una cara transl煤cida tendr谩 componentes de color calculados por la f贸rmula

 R = srcR * srcA + dstR * (1 - srcA) G = srcG * srcA + dstG * (1 - srcA) B = srcB * srcA + dstB * (1 - srcA) 

donde [srcR, srcG, srcB] son 鈥嬧媗os componentes de color de la textura cuadrada; [dstR, dstG, dstB] 鈥 , , . srcA - .

seRenderingHint() , , . , .

8.


. . , .

.

. , - , , 1 鈥 . , 0 1 .

fading-in
main.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; }; //------------------------------------------------------------------------------ // //------------------------------------------------------------------------------ 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)); } } //------------------------------------------------------------------------------ // //------------------------------------------------------------------------------ int main(int argc, char *argv[]) { (void) argc; (void) argv; 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)); osg::ref_ptr<osg::Geode> geode = new osg::Geode; geode->addDrawable(quad.get()); 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)); material->setUpdateCallback(new AlphaFadingCallback); 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(); } 


- -

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

, , . ,

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

. Ambient color 鈥 , , . Diffuse color 鈥 , , , . FRONT_AND_BACK , , .



 material->setUpdateCallback(new AlphaFadingCallback); 



 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 

Continuar谩 ...

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


All Articles