
1. Introdução
Falando sobre técnicas de programação específicas para OSG
, a última vez que falamos sobre o mecanismo de retorno de chamada e sua implementação no mecanismo. É hora de examinar as possibilidades que esse mecanismo oferece para gerenciar o conteúdo de uma cena tridimensional.
Se falamos sobre animação de objetos, o OSG fornece ao desenvolvedor duas opções para sua implementação:
- Animação processual implementada programaticamente através da transformação de objetos e seus atributos
- Exporte a animação e controle-a de um editor 3D a partir do código do aplicativo
Para começar, considere a primeira possibilidade, como a mais óbvia. Definitivamente falaremos sobre o segundo um pouco mais tarde.
1. Animação morphing processual
Ao percorrer o gráfico de cena, o OSG transfere dados para o pipeline OpenGL, que é executado em um encadeamento separado. Esse encadeamento deve ser sincronizado com outros encadeamentos de processamento em cada quadro. Caso contrário, o método frame () será concluído antes do processamento dos dados da geometria. Isso levará a um comportamento imprevisível do programa e a falhas. O OSG oferece uma solução para esse problema na forma do método setDataVariance () da classe osg :: Object, que é a base para todos os objetos de cena. Você pode definir três modos de processamento para objetos
- NÃO ESPECIFICADO (por padrão) - OSG determina independentemente a ordem de processamento do objeto.
- ESTÁTICO - o objeto é imutável e a ordem de seu processamento não é importante. Acelera significativamente a renderização.
- DINÂMICO - o objeto deve ser processado antes do início da renderização.
Essa configuração pode ser definida a qualquer momento chamando
node->setDataVariance( osg::Object::DYNAMIC );
A prática geralmente aceita é modificar a geometria "on the fly", ou seja, alterar as coordenadas de vértices, cores normais e texturas dinamicamente em cada quadro, obtendo geometria mutável. Essa técnica é chamada de animação transformada. Nesse caso, a ordem de processamento da geometria é decisiva - todas as suas alterações devem ser recalculadas antes do início do desenho. Para ilustrar esse truque, modificamos levemente o exemplo quadrado colorido, forçando um de seus vértices a girar em torno do eixo X.
Exemplo de Animquadmain.h #ifndef MAIN_H #define MAIN_H #include <osg/Geometry> #include <osg/Geode> #include <osgViewer/Viewer> #endif
main.cpp #include "main.h"
Vamos criar um quadrado em uma função 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(); }
uma descrição da qual, em princípio, não é necessária, uma vez que realizamos tais ações repetidamente. Para modificar os vértices desse quadrado, escrevemos a classe DynamicQuadCallback, herdando-a de osg :: Drawable :: UpdateCallback
class DynamicQuadCallback : public osg::Drawable::UpdateCallback { public: virtual void update(osg::NodeVisitor *, osg::Drawable *drawable); };
substituindo o método update () nele
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(); }
Aqui temos um ponteiro para um objeto de geometria
osg::Geometry *quad = static_cast<osg::Geometry *>(drawable);
lemos da geometria uma lista de vértices (ou melhor, um ponteiro para ela)
osg::Vec3Array *vertices = static_cast<osg::Vec3Array *>(quad->getVertexArray());
Para obter o último elemento (último vértice) na matriz, a classe osg :: Array fornece o método back (). Para executar a rotação do vértice em relação ao eixo X, introduzimos o quaternion
osg::Quat quat(osg::PI * 0.01, osg::X_AXIS);
isto é, definimos um quaternion que implementa uma rotação em torno do eixo X por um ângulo de 0,01 * Pi. Gire o vértice multiplicando o quaternion por um vetor que define as coordenadas do vértice
vertices->back() = quat * vertices->back();
As duas últimas chamadas recontam a lista de exibição e o paralelepípedo dimensional para a geometria modificada
quad->dirtyDisplayList(); quad->dirtyBound();
No corpo da função main (), criamos um quadrado, configuramos o modo de desenho dinâmico para ela e adicionamos um retorno de chamada modificando a geometria
osg::Geometry *quad = createQuad(); quad->setDataVariance(osg::Object::DYNAMIC); quad->setUpdateCallback(new DynamicQuadCallback);
Vou deixar indiscriminadamente criar o nó raiz e iniciar o visualizador, como já fizemos isso pelo menos vinte vezes de maneiras diferentes. Como resultado, temos a animação morphing mais simples

Agora tente remover (comentar) a chamada setDataVariance (). Talvez não veremos nada de criminoso nesse caso - por padrão, o OSG tenta determinar automaticamente quando atualizar os dados da geometria, tentando sincronizar com a renderização. Em seguida, tente alterar o modo de DINÂMICO para ESTÁTICO e você verá que a imagem não é renderizada sem problemas, com reflexos visíveis, erros e avisos como este
Warning: detected OpenGL error 'invalid value' at after RenderBin::draw(..)
Se você não executar o método dirtyDisplayList (), o OpenGL ignorará todas as alterações na geometria e usará a lista de exibição criada no início para criar o quadrado para renderização. Exclua esta chamada e você verá que não há animação.
Sem chamar o método dirtyBound (), a caixa delimitadora não será recalculada e o OSG cortará incorretamente as faces invisíveis.
2. O conceito de interpolação de movimento
Suponha que um trem que vai da estação A à estação B leve 15 minutos para viajar. Como podemos simular essa situação alterando a posição do trem no retorno de chamada? A maneira mais fácil é correlacionar a posição da estação A com o tempo 0 e da estação B com 15 minutos e mover o trem uniformemente entre esses horários. Essa abordagem mais simples é chamada interpolação linear. Na interpolação linear, um vetor especificando a posição de um ponto intermediário é descrito pela fórmula
p = (1 - t) * p0 + t * p1
onde p0 é o ponto de partida; p1 é o ponto final; t é um parâmetro que varia uniformemente de 0 a 1. No entanto, o movimento do trem é muito mais complicado: sai da estação A, acelera, depois se move a uma velocidade constante e depois desacelera, parando na estação B. Esse processo não é mais capaz de descrever interpolação linear e Parece antinatural.
O OSG fornece ao desenvolvedor a biblioteca osgAnimation, que contém vários algoritmos de interpolação padrão usados para animar suavemente o movimento de objetos de cena. Cada uma dessas funções geralmente possui dois argumentos: o valor inicial do parâmetro (geralmente 0) e o valor final do parâmetro (geralmente 1). Essas funções podem ser aplicadas ao início do movimento (InMotion), ao final do movimento (OutMotion) ou ao início e fim do movimento (InOutMotion)
Tipo de movimento | na aula | fora da aula | classe de entrada / saída |
---|
Interpolação linear | LinearMotion | - | - |
Interpolação quadrática | InQuadMotion | OutQuadMotion | InOutQuadMotion |
Interpolação cúbica | InCubicMotion | Outcubicmotion | InOutCubicMotion |
Interpolação de 4 ordens | InQuartMotion | OutQuartMotion | InOutQuartMotion |
Interpolação de efeito de rejeição | InBounceMotion | OutBounceMotion | InOutBounceMotion |
Interpolação de rebote elástico | InElasticMotion | OutElasticMotion | InOutElasticMotion |
Interpolação sinusoidal | InSineMotion | Outsinemotion | InOutSineMotion |
Interpolação inversa | Inbackmotion | Outbackmotion | InOutBackMotion |
Interpolação circular | InCircMotion | Outcircmotion | InOutCircMotion |
Interpolação exponencial | InExpoMotion | Outexpomotion | InOutExpoMotion |
Para criar uma interpolação linear do movimento de um objeto, escrevemos esse código
osg::ref_ptr<osgAnimation::LinearMotion> motion = new osgAnimation::LinearMotion(0.0f, 1.0f);
3. Animação de nós de transformação
A animação de trajetória é o tipo mais comum de animação em aplicativos gráficos. Essa técnica pode ser usada para animar o movimento de um carro, o voo de um avião ou o movimento da câmera. A trajetória é predefinida, com todas as posições, rotações e mudanças de escala em pontos-chave no tempo. Quando o ciclo de simulação começa, o estado do objeto é recalculado em cada quadro, usando interpolação linear para posição e escala e interpolação linear esférica para quaternions de rotação. Para fazer isso, use o método interno slerp () da classe osg :: Quat.
O OSG fornece a classe osg :: AnimationPath para descrever um caminho variável no tempo. O método desta classe insert () é usado para adicionar pontos de controle correspondentes a determinados pontos no tempo à trajetória. O ponto de controle é descrito pela classe osg :: AnimationPath :: ControlPoint, cujo construtor assume a posição como parâmetros e, opcionalmente, os parâmetros de rotação e dimensionamento de objetos. Por exemplo
osg::ref_ptr<osg::AnimationPath> path = new osg::AnimationPath; path->insert(t1, osg::AnimationPath::ControlPoint(pos1, rot1, scale1)); path->insert(t2, ...);
Aqui t1, t2 são instantes de tempo em segundos; rot1 é o parâmetro de rotação no tempo t1, descrito pelo quaternion osg :: Quat.
É possível controlar loops de animação através do método setLoopMode (). Por padrão, o modo LOOP está ativado - a animação será repetida continuamente. Outros valores possíveis: NO_LOOPING - reproduz a animação uma vez e SWING - faz um loop do movimento nas direções para frente e para trás.
Após a conclusão de toda a inicialização, anexamos o objeto osg :: AnimationPath ao objeto interno osg :: AnimationPathCallback, derivado da classe osg :: NodeCallback.
4. Um exemplo de uma animação de movimento ao longo de um caminho
Agora faremos nossa cessna se mover em círculo com o centro no ponto (0,0,0). A posição da aeronave na trajetória será calculada interpolando linearmente a posição e a orientação entre os quadros principais.
Exemplo de 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"
Começamos criando a trajetória da aeronave, levando esse código para uma função 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, a função leva o raio do círculo ao longo do qual o avião se move e o tempo durante o qual ele fará uma revolução. Dentro da função, crie um objeto de trajetória e ative o modo de loop de animação
osg::ref_ptr<osg::AnimationPath> path = new osg::AnimationPath; path->setLoopMode(osg::AnimationPath::LOOP);
Código a seguir
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 os parâmetros de aproximação da trajetória. Dividimos toda a trajetória em vários exemplos de seções retas e calculamos a mudança no ângulo de rotação do plano em torno do eixo vertical (guinada) delta_yaw e a mudança no tempo delta_time ao passar de uma seção para outra. Agora crie os pontos de controle necessários
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)); }
No ciclo, todas as seções da trajetória do primeiro ao último são classificadas. Cada ponto de controle é caracterizado por um ângulo de guinada
double yaw = delta_yaw * i;
a posição do centro de massa da aeronave no espaço
osg::Vec3d pos(radius * sin(yaw), radius * cos(yaw), 0.0);
A rotação da aeronave para o ângulo de guinada desejado (em relação ao eixo vertical) é definida pelo quaternion
osg::Quat rot(-yaw, osg::Z_AXIS);
e adicione os parâmetros calculados à lista de pontos de controle do caminho
path->insert(delta_time * i, osg::AnimationPath::ControlPoint(pos, rot));
No programa principal, prestamos atenção às nuances na indicação do nome do arquivo de modelo da aeronave no momento da inicialização
osg::ref_ptr<osg::Node> model = osgDB::readNodeFile("../data/cessna.osg.0,0,90.rot");
- um sufixo ".0,0,90.rot" foi adicionado ao nome do arquivo. O mecanismo para carregar a geometria de um arquivo usado no OSG permite especificar a posição inicial e a orientação do modelo após o carregamento. Nesse caso, queremos que o modelo seja girado 90 graus ao redor do eixo Z após o carregamento.
Em seguida, o nó raiz é criado, que é o nó de transformação, e o objeto de modelo é adicionado a ele como um nó filho
osg::ref_ptr<osg::MatrixTransform> root = new osg::MatrixTransform; root->addChild(model.get());
Agora crie um retorno de chamada de animação de trajetória, adicionando a ele o caminho criado pela função createAnimationPath ()
osg::ref_ptr<osg::AnimationPathCallback> apcb = new osg::AnimationPathCallback; apcb->setAnimationPath(createAnimationPath(50.0, 6.0));
Anexe esse retorno de chamada ao nó de transformação
root->setUpdateCallback(apcb.get());
O visualizador é inicializado e iniciado como de costume.
osgViewer::Viewer viewer; viewer.setSceneData(root.get()); return viewer.run();
Obter uma animação em movimento de avião

Acha que não encontrou nada de estranho neste exemplo? Anteriormente, por exemplo, em um programa ao renderizar em uma textura, você alterava explicitamente a matriz de transformação para obter uma alteração na posição do modelo no espaço. Aqui, apenas criamos um nó de transformação e no código não há atribuição de matriz explícita em lugar algum.
O segredo é que a classe osg :: AnimationPathCallback faz esse trabalho. De acordo com a posição atual do objeto no caminho, ele calcula a matriz de transformação e a aplica automaticamente ao nó de transformação ao qual está conectado, salvando o desenvolvedor de várias operações de rotina.
Deve-se observar que anexar osg :: AnimationPathCallback a outros tipos de nós não terá apenas efeito, mas também poderá levar ao comportamento indefinido do programa. É importante lembrar que esse retorno de chamada afeta apenas os nós de transformação.
5. Animação de controle de software
A classe osg :: AnimationPathCallback fornece métodos para controlar a animação durante a execução do programa.
- reset () - redefine a animação e reproduza-a primeiro.
- setPause () - pausa a animação. Toma um valor booleano como parâmetro
- setTimeOffset () - define o deslocamento de tempo antes do início da animação.
- setTimeMultiplier () - define o fator de tempo para aceleração / desaceleração da animação.
Por exemplo, para remover a animação de pausar e redefinir, executamos esse código
apcb->setPause(false); apcb->reset();
e iniciar a animação a partir do quarto segundo após iniciar o programa com dupla aceleração, esse código
apcb->setTimeOffset(4.0f); apcb->setTimeMultiplier(2.0f);
6. A ordem de renderização de primitivas no OpenGL
O OpenGL armazena dados de vértice e primitivos em vários buffers, como um buffer de cores, um buffer de profundidade, um buffer de estêncil e assim por diante. Além disso, ele não substitui os vértices e as faces triangulares já enviadas ao seu pipeline. Isso significa que o OpenGL cria uma nova geometria, independentemente de como a geometria existente foi criada. Isso significa que a ordem na qual as primitivas são enviadas para o pipeline de renderização afeta significativamente o resultado final que vemos na tela.
Com base nos dados do buffer de profundidade, o OpenGL desenha corretamente objetos opacos, classificando os pixels de acordo com a distância do observador. No entanto, ao usar a técnica de mistura de cores, por exemplo, ao implementar objetos transparentes e translúcidos, uma operação especial será executada para atualizar o buffer de cores. Os pixels novos e antigos da imagem são misturados, levando em consideração o valor do canal alfa (quarto componente de cor). Isso leva ao fato de que a ordem de renderização das bordas translúcidas e opacas afeta o resultado final

Na figura, na situação à esquerda, primeiro objetos opacos e depois transparentes foram enviados para o pipeline, o que levou à mudança correta no buffer de cores e à exibição correta de faces. Na situação certa, os primeiros objetos transparentes foram desenhados e depois opacos, o que levou a uma exibição incorreta.
O método setRenderingHint () da classe osg :: StateSet indica ao OSG a ordem de renderização necessária de nós e objetos geométricos, se isso precisar ser feito explicitamente. Esse método simplesmente indica se as faces translúcidas devem ou não ser levadas em consideração durante a renderização, garantindo assim que, se houver faces translúcidas na cena, as faces opacas e transparentes serão desenhadas primeiro, levando em consideração a distância do observador. Para informar o mecanismo que este nó é opaco, usamos este código
node->getOrCreateStateSet()->setRenderingHint(osg::StateSet::OPAQUE_BIN);
ou contém bordas transparentes
node->getOrCreateStateSet()->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
7. Um exemplo da implementação de objetos translúcidos
Vamos tentar ilustrar toda a introdução teórica acima com um exemplo concreto da implementação de um objeto translúcido.
Exemplo de transparênciamain.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(); }
Na maioria das vezes, o código mostrado aqui não contém nada de novo: dois objetos geométricos são criados - um quadrado texturizado e uma asa delta, cujo modelo é carregado a partir de um arquivo. No entanto, aplicamos uma cor translúcida branca a todos os vértices do quadrado
colors->push_back( osg::Vec4(1.0f, 1.0f, 1.0f, 0.5f) );
- o valor do canal alfa é 0,5, que, quando misturado com cores de textura, deve dar o efeito de um objeto translúcido. Além disso, a função de mistura de cores deve ser definida para o processamento de transparência.
osg::ref_ptr<osg::BlendFunc> blendFunc = new osg::BlendFunc; blendFunc->setFunction(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
passando para a máquina de estado OpenGL
stateset->setAttributeAndModes(blendFunc);
Ao compilar e executar este programa, obtemos o seguinte resultado

Pare com isso! E onde está a transparência? O fato é que esquecemos de dizer ao mecanismo que as bordas transparentes devem ser processadas, o que é facilmente resolvido chamando
stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
após o qual obtemos o resultado que precisamos - a asa delta brilha através de um quadrado texturizado translúcido

Os parâmetros das funções de mistura GL_SRC_ALPHA e GL_ONE_MINUS_SRC_ALPHA significam que o pixel da tela resultante ao desenhar uma face translúcida terá componentes de cores calculados pela fórmula
R = srcR * srcA + dstR * (1 - srcA) G = srcG * srcA + dstG * (1 - srcA) B = srcB * srcA + dstB * (1 - srcA)
onde [srcR, srcG, srcB] são os componentes de cor da textura quadrada; [dstR, dstG, dstB] — , , . srcA - .
seRenderingHint() , , . , .
8.
. . , .
.
. , - , , 1 — . , 0 1 .
fading-inmain.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));
, , . ,
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
Para continuar ...