
1. Introdução
Como regra, ao trabalhar com parâmetros de renderização, o OpenGL atua como uma máquina de estado. Um estado de renderização é uma coleção de atributos de estado, como fontes de luz, materiais, texturas e modos de exibição, ativados e desativados pelas funções glEnable () e glDisable (). Quando um determinado estado é definido, ele permanece em vigor até que alguma outra função o altere. O pipeline do OpenGL suporta uma pilha de estados para salvar e restaurar estados a qualquer momento. A máquina de estados fornece ao desenvolvedor controle total sobre os estados de renderização atuais e salvos na pilha.
No entanto, essa abordagem é inconveniente ao trabalhar com OSG. Por esse motivo, a máquina de estados OpenGL é encapsulada pela classe osg :: StateSet, que cuida de trabalhar com a pilha de estados e configurá-las no processo de percorrer o gráfico de cena.
Uma instância da classe osg :: StateSet contém um subconjunto dos vários estados de renderização e pode aplicá-los aos objetos geométricos osg :: Node e osg :: Drawable objetos geométricos usando o método setStateSet ()
osg::StateSet *stateset = new osg::StateSet; node->setStateSet(stateset);
Uma maneira mais segura seria usar o método getOrCreateStateSet (), que garante o retorno do estado correto e sua conexão com o nó ou objeto extraível
osg::StateSet *stateset = node->getOrCreateStateSet();
As classes osg :: Node e osg :: Drawable controlam a variável de membro osg :: StateSet através do ponteiro inteligente osg :: ref_ptr <>. Isso significa que um conjunto de estados pode ser dividido entre vários objetos na cena e será destruído somente quando todos esses objetos forem destruídos.
1. Atributos e modos
OSG define a classe osg :: StateAttribute para armazenar atributos de renderização. Essa é uma classe base virtual que é herdada por vários atributos de renderização, como luz, material e névoa.
Os modos de renderização funcionam como interruptores que podem ser ativados e desativados. Além disso, eles estão associados a enumeradores, que são usados para indicar o tipo de modo OpenGL. Às vezes, o modo de renderização é associado a um atributo, por exemplo, o modo GL_LIGHTING inclui variáveis para fontes de luz enviadas para o pipeline OpenGL quando está ligado e, caso contrário, desliga a iluminação.
A classe osg :: StateSet divide atributos e modos em dois grupos: textura e não textura. Possui vários métodos públicos para adicionar atributos e modos que não são de textura a um conjunto de estados:
- setAttribute () - Adiciona um objeto do tipo osg :: StateAttribute ao conjunto de estados. Atributos do mesmo tipo não podem coexistir no mesmo conjunto de estados. O ponto de ajuste anterior será substituído pelo novo.
- setMode () - anexa um enumerador de modo a um conjunto de estados e define seu valor como osg :: StateAttribute :: ON ou osg :: StateAttribute :: OFF, o que significa ativar ou desativar o modo.
- setAttributeAndModes () - anexa o atributo de renderização e seu modo associado e define o valor do comutador (o padrão é ON). Deve-se ter em mente que nem todos os atributos têm um modo correspondente, mas você pode usar esse método em qualquer caso.
Para definir o atributo e seu modo associado, você pode usar este código
stateset->setAttributeAndModes(attr, osg::StateAttribute::ON);
Para definir atributos de textura, um parâmetro adicional deve ser passado para indicar a textura à qual deve ser aplicada. Osg :: StateSet fornece vários outros métodos públicos para isso, como setTextureAttribute (), setTextureMode () e setTextureAttributeAndModes ()
stateset->setTextureAttributeAndModes(0, textattr, osg::StateAttribute::ON);
aplica o atributo textattr à textura com o identificador 0.
2. Configurando o modo de exibição de polígonos para nós de cena
Ilustramos a teoria acima com um exemplo prático - alterando o modo de rasterização de polígonos OpenGL usando a classe osg :: PolygonMode, que herda de osg :: StateAttribute. Essa classe encapsula a função glPolygonMode () e fornece uma interface para definir o modo de exibição de polígono para um nó de cena específico.
Exemplo de Polygonmodemain.h #ifndef MAIN_H #define MAIN_H #include <osg/PolygonMode> #include <osg/MatrixTransform> #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::Node> model = osgDB::readNodeFile("../data/cessna.osg"); osg::ref_ptr<osg::MatrixTransform> transform1 = new osg::MatrixTransform; transform1->setMatrix(osg::Matrix::translate(-25.0f, 0.0f, 0.0f)); transform1->addChild(model.get()); osg::ref_ptr<osg::MatrixTransform> transform2 = new osg::MatrixTransform; transform2->setMatrix(osg::Matrix::translate(25.0f, 0.0f, 0.0f)); transform2->addChild(model.get()); osg::ref_ptr<osg::PolygonMode> pm = new osg::PolygonMode; pm->setMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE); transform1->getOrCreateStateSet()->setAttribute(pm.get()); osg::ref_ptr<osg::Group> root = new osg::Group; root->addChild(transform1.get()); root->addChild(transform2.get()); osgViewer::Viewer viewer; viewer.setSceneData(root.get()); return viewer.run(); }
Aqui, carregamos o modelo de nossa amada cessna e, aplicando transformações, obtemos duas instâncias do modelo. Em um deles, à esquerda, aplicamos um atributo que define o modo de exibição da estrutura de arame dos polígonos
osg::ref_ptr<osg::PolygonMode> pm = new osg::PolygonMode; pm->setMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE); transform1->getOrCreateStateSet()->setAttribute(pm.get());

Se voltarmos para a especificação OpenGL, podemos facilmente imaginar quais opções de exibição de polígono estarão disponíveis para nós ao usar setMode () nesse caso específico. O primeiro parâmetro pode assumir os valores osg :: PolygonMode :: FRONT, BACK e FRONT_AND_BACK, correspondentes aos enumeradores OpenGL GL_FRONT, GL_BACK, GL_FRONT_AND_BACK. O segundo parâmetro pode assumir os valores osg :: PolygonMode :: POINT, LINE e FILL, que correspondem a GL_POINT, GL_LINE e GL_FILL. Nenhum outro truque, como costuma acontecer no desenvolvimento de um OpenGL puro, não é necessário aqui - o OSG cuida da maior parte do trabalho. O modo de exibição de polígono não possui um modo associado e não requer a chamada do par glEnable () / glDisable (). O método setAttributeAndModes () também funcionará bem neste caso, mas o valor do terceiro parâmetro será inútil.
3. Herança dos estados de renderização. Aplicando atributos e modos
O conjunto de estados do nó afeta o nó atual e todos os seus filhos. Por exemplo, o atributo osg :: PolygonMode definido para transform1 do exemplo anterior será aplicado a todos os filhos deste nó. No entanto, o nó filho pode substituir os atributos pai, ou seja, o estado de renderização será herdado do nó pai se o nó filho não alterar o comportamento.
Às vezes, você precisa redefinir o comportamento de um nó em termos de uso de atributo. Por exemplo, na maioria dos editores 3D, o usuário pode carregar vários modelos e alterar o modo de exibição de todos os modelos carregados ao mesmo tempo, independentemente de como eles foram exibidos anteriormente. Em outras palavras, todos os modelos no editor devem herdar um único atributo, independentemente de como foram definidos anteriormente para cada um dos modelos. No OSG, isso pode ser implementado usando o sinalizador osg :: StateAttribute :: OVERRIDE, por exemplo
stateset->StateAttribute(attr, osg::StateAttribute::OVERRIDE);
Ao definir modos e modos com atributos, o operador OR bit a bit é usado
stateset->StateAttributeAndModes(attr, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
Além disso, o atributo também pode ser protegido contra substituição - para isso, ele deve ser marcado com o sinalizador osg :: StateAttribute :: PROTECTED.
Há um terceiro sinalizador, osg :: StateAttribute :: INHERIT, que é usado para indicar que esse atributo deve ser herdado do conjunto de estados do nó pai.
Aqui está um pequeno exemplo usando os sinalizadores OVERRIDE e PROTECTED. O nó raiz será configurado como OVERRIDE para forçar todos os nós filhos a herdarem seus atributos e modos. Nesse caso, os nós filhos tentarão alterar seu estado com ou sem a ajuda do sinalizador PROTECTED, o que levará a resultados diferentes.
Herdar texto de exemplomain.h #ifndef MAIN_H #define MAIN_H #include <osg/PolygonMode> #include <osg/MatrixTransform> #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::Node> model = osgDB::readNodeFile("../data/glider.osg"); osg::ref_ptr<osg::MatrixTransform> transform1 = new osg::MatrixTransform; transform1->setMatrix(osg::Matrix::translate(-0.5f, 0.0f, 0.0f)); transform1->addChild(model.get()); osg::ref_ptr<osg::MatrixTransform> transform2 = new osg::MatrixTransform; transform2->setMatrix(osg::Matrix::translate(0.5f, 0.0f, 0.0f)); transform2->addChild(model.get()); osg::ref_ptr<osg::Group> root = new osg::Group; root->addChild(transform1.get()); root->addChild(transform2.get()); transform1->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF); transform2->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED); root->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); osgViewer::Viewer viewer; viewer.setSceneData(root.get()); return viewer.run(); }

Para entender o que está acontecendo, você precisa ver como é uma asa-delta normalmente iluminada baixando o OSGviewer em tempo integral
$ osgviewer glider.osg
No exemplo, estamos tentando alterar o modo de iluminação para os nós transform1 e transform2 desativando completamente a iluminação.
transform1->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF); transform2->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED);
Nesse caso, ativamos o modo de iluminação para o nó raiz e usamos o sinalizador OVERRIDE para todos os nós filhos, para que eles herdem o estado do nó raiz. No entanto, o trnsform2 usa o sinalizador PROTECTED para impedir que as configurações do nó raiz sejam afetadas.
transform2->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED);
Como resultado, apesar de desligar a iluminação no nó transform1, a asa esquerda ainda está acesa porque as configurações da raiz da cena bloquearam nossa tentativa de desativar a iluminação. A asa direita é exibida sem iluminação (parece mais brilhante apenas porque é inundada com uma cor simples sem renderizar iluminação), pois o transform2 está protegido contra a herança dos atributos do nó raiz.
4. Lista de atributos OpenGL suportados no OpenSceneGraph
O OSG suporta quase todos os atributos e modos de renderização suportados pelo OpenGL por meio de classes derivadas de osg :: StateAttribute. A tabela mostra todos os parâmetros da máquina de estados OpenGL disponíveis no mecanismo.
ID do tipo de atributo | Nome da classe | Modo associado | Função OpenGL equivalente |
---|
ALPHEFUNC | osg :: AlphaFunc | GL_ALPHA_TEST | glAlphaFunc () |
BLENDFUNC | osg :: BlendFunc | GL_BLEND | glBlendFunc () e glBlendFuncSeparate () |
CLIPPLANE | osg :: ClipPlane | GL_CLIP_PLANEi (i de 1 a 5) | glClipPlane () |
Colormask | osg :: ColorMask | - | glColorMask () |
CULLFACE | osg :: CullFace | GL_CULLFACE | glCullFace () |
Profundidade | osg :: Profundidade | GL_DEPTH_TEST | glDepthFunc (), glDepthRange () e glDepthMask () |
FOG | osg :: Nevoeiro | GL_FOG | glFog () |
FRONTFACE | osg :: FrontFace | - | glFrontFace () |
Light | osg :: Luz | GL_LIGHTi (i de 1 a 7) | glLight () |
LIGHTMODEL | osg :: LightModel | - | glLightModel () |
LINESTRIPPLE | osg :: LineStripple | GL_LINE_STRIPPLE | glLineStripple () |
LARGURA DE LINHA | osg :: LineWidth | - | glLineWidht () |
LOGICOP | osg :: LogicOp | GL_COLOR_LOGIC_OP | glLogicOp () |
MATERIAL | osg :: Material | - | glMaterial () e glColorMaterial () |
PONTO | osg :: Point | GL_POINT_SMOOTH | glPointParameter () |
POINTSPRITE | osg :: PointSprite | GL_POINT_SPRITE_ARB | Funções para trabalhar com sprites OpenGL |
POLYGONMODE | osg :: PolygonMode | - | glPolygonMode () |
POLYGONOFFSET | osg :: PolygonOffset | GL_POLYGON_OFFSET_POINT | glPolygonOffset () |
POLYGONSTRIPPLE | osg :: PolygonStripple | GL_POLYGON_STRIPPLE | glPolygonStripple () |
TESOURA | osg :: Tesoura | GL_SCISSOR_TEST | glScissor () |
SHADEMODEL | osg :: ShadeModel | - | glShadeModel () |
STENCIL | osg :: Estêncil | GL_STENCIL_TEST | glStencilFunc (), glStencilOp () e glStencilMask () |
Texenv | osg :: TexEnv | - | glTexEnv () |
Texgen | osg :: TexGen | GL_TEXTURE_GEN_S | glTexGen () |
A coluna ID do tipo de atributo indica o identificador OSG específico que identifica esse atributo nos enumeradores da classe osg :: StateAttribute. Ele pode ser usado no método getAttribute para obter o valor de um atributo específico.
osg::PolygonMode *pm = dynamic_cast<osg::PolygonMode *>(stateset->getAttribute(osg::StateAttribute::POLYGONMODE));
Um ponteiro válido indica que o atributo foi definido anteriormente. Caso contrário, o método será NULL. Você também pode obter o valor do modo atual usando a chamada
osg::StateAttribute::GLModeValue value = stateset->getMode(GL_LIGHTING);
Aqui, o enumerador GL_LIGHTING é usado para ativar / desativar a iluminação em toda a cena.
5. Aplicar neblina ao modelo na cena
Vamos citar o efeito de neblina como uma maneira ideal de mostrar como trabalhar com vários atributos e modos de renderização. O OpenGL usa uma equação linear e duas equações exponenciais que descrevem o modelo de neblina, suportado pela classe osg :: Fog.
Exemplo de névoa de textomain.h #ifndef MAIN_H #define MAIN_H #include <osg/Fog> #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::Fog> fog = new osg::Fog; fog->setMode(osg::Fog::LINEAR); fog->setStart(500.0f); fog->setEnd(2500.0f); fog->setColor(osg::Vec4(1.0f, 1.0f, 0.0f, 1.0f)); osg::ref_ptr<osg::Node> model = osgDB::readNodeFile("../data/lz.osg"); model->getOrCreateStateSet()->setAttributeAndModes(fog.get()); osgViewer::Viewer viewer; viewer.setSceneData(model.get()); return viewer.run(); }
Primeiro, crie o atributo fog. Usamos um modelo linear, ajustamos o alcance da exibição de nevoeiro pela distância do modelo
osg::ref_ptr<osg::Fog> fog = new osg::Fog; fog->setMode(osg::Fog::LINEAR); fog->setStart(500.0f); fog->setEnd(2500.0f); fog->setColor(osg::Vec4(1.0f, 1.0f, 0.0f, 1.0f));
Carregamos a amostra de paisagem lz.osg e aplicamos esse atributo a ela
osg::ref_ptr<osg::Node> model = osgDB::readNodeFile("../data/lz.osg"); model->getOrCreateStateSet()->setAttributeAndModes(fog.get());
Na janela do visualizador, vemos uma paisagem borrada e podemos ver como a densidade do nevoeiro muda dependendo da distância do modelo



6. Trabalhe com fontes de luz e iluminação
Assim como o OpenGL, o OSG suporta até oito fontes de luz para encontrar diretamente objetos de cena. Como o OpenGL, o OSG não calcula automaticamente sombras. Os raios de luz provêm de fontes em linhas retas, são refletidos a partir de objetos e dispersos por eles, após o que são percebidos pelos olhos do espectador. Para um processamento de iluminação de alta qualidade, é necessário definir propriedades do material, geometria normal dos objetos, etc.
A classe osg :: Light fornece métodos para controlar fontes de luz, incluindo: setLightNum () e getLightNum () - para trabalhar com o número de fontes; setAmbient () e getAmbient () para controlar o componente circundante; setDiffuse () e getDiffuse () - para trabalhar com um componente disperso, etc.
O OSG também descreve a classe osg :: LightSource para adicionar fontes de luz à cena. Ele fornece o método setLight () e é o nó folha do gráfico de cena com um único atributo. Todos os outros nós do gráfico de cena são afetados pela fonte de luz se o modo correspondente para GL_LIGHTi estiver definido. Por exemplo:
Outra solução mais conveniente é o método setStateSetModes (), com o qual a fonte de luz com o número desejado é automaticamente anexada ao nó raiz
root->addChild( lightSource.get() ); lightSource->setStateSetModes( root->getOrCreateStateSet(), osg::StateAttribute::ON );
Você pode adicionar nós filhos à fonte de luz, mas isso não significa nada, você iluminará o subgrafo associado a ele de alguma maneira especial. Será processado como geometria, representada pela forma física da fonte de luz.
O nó osg :: LightSource pode ser anexado ao nó de transformação e, por exemplo, uma fonte de luz pontual pode ser movida no espaço. Isso pode ser desativado configurando o sistema de coordenadas absoluto para a fonte de luz.
lightSource->setReferenceFrame( osg::LightSource::ABSOLUTE_RF );
7. Criando fontes de luz na cena
Por padrão, o OSG define automaticamente a fonte de luz para o número 0, que emite luz direcional uniforme para a cena. No entanto, a qualquer momento, você pode adicionar várias fontes de luz adicionais e até controlá-las usando os nós de transformação de coordenadas. Somente fontes posicionais (fontes pontuais) podem ser movidas. A luz direcional tem apenas direção (uma corrente de raios paralelos vindos do infinito) e não está ligada a uma posição específica no palco. O OpenGL e OSG usam o quarto componente do parâmetro position para especificar o tipo de fonte de luz. Se for 0, então a luz é considerada como dirigida; com um valor de 1 - posicional.
Considere um pequeno exemplo de trabalho com iluminação.
Título de spoilermain.h #ifndef MAIN_H #define MAIN_H #include <osg/MatrixTransform> #include <osg/LightSource> #include <osgDB/ReadFile> #include <osgViewer/Viewer> #endif
main.cpp #include "main.h"
Para criar uma fonte de luz, temos uma função separada
osg::Node *createLightSource(int num, const osg::Vec3 &trans, const osg::Vec4 &color) { osg::ref_ptr<osg::Light> light = new osg::Light; light->setLightNum(num); light->setDiffuse(color); light->setPosition(osg::Vec4(0.0f, 0.0f, 0.0f, 1.0f)); osg::ref_ptr<osg::LightSource> lightSource = new osg::LightSource; lightSource->setLight(light); osg::ref_ptr<osg::MatrixTransform> sourceTrans = new osg::MatrixTransform; sourceTrans->setMatrix(osg::Matrix::translate(trans)); sourceTrans->addChild(lightSource.get()); return sourceTrans.release(); }
Nesta função, primeiro determinamos os parâmetros de iluminação fornecidos pela fonte, criando assim o atributo GL_LIGHTi
osg::ref_ptr<osg::Light> light = new osg::Light;
Depois disso, é criada uma fonte de luz à qual esse atributo está atribuído.
osg::ref_ptr<osg::LightSource> lightSource = new osg::LightSource; lightSource->setLight(light);
Criamos e configuramos o nó de transformação, passando para ele nossa fonte de luz como um nó filho
osg::ref_ptr<osg::MatrixTransform> sourceTrans = new osg::MatrixTransform; sourceTrans->setMatrix(osg::Matrix::translate(trans)); sourceTrans->addChild(lightSource.get());
Retornar um ponteiro para o nó de transformação
return sourceTrans.release();
No corpo do programa principal, carregamos um modelo tridimensional (novamente, nossa cessna favorita)
osg::ref_ptr<osg::Node> model = osgDB::readNodeFile("../data/cessna.osg"); osg::ref_ptr<osg::Group> root = new osg::Group; root->addChild(model.get());
Criamos duas fontes de luz com os números 0 e 1. A primeira brilha em amarelo, a segunda - azul-verde
osg::Node *light0 = createLightSource(0, osg::Vec3(-20.0f, 0.0f, 0.0f), osg::Vec4(1.0f, 1.0f, 0.0f, 1.0f)); osg::Node *light1 = createLightSource(1, osg::Vec3(0.0f, -20.0f, 0.0f), osg::Vec4(0.0f, 1.0f, 1.0f, 1.0f));
Informamos à máquina de estado OpenGL que é necessário ativar as fontes de luz 0 e 1 e adicionar as fontes que criamos à cena
root->getOrCreateStateSet()->setMode(GL_LIGHT0, osg::StateAttribute::ON); root->getOrCreateStateSet()->setMode(GL_LIGHT1, osg::StateAttribute::ON); root->addChild(light0); root->addChild(light1);
Após inicializar e iniciar o visualizador, obtemos uma imagem

Conclusão
Muito emocionado com a atenção das pessoas interessadas neste ciclo. Essa ação não começou muito, mas sinto que a comunidade precisa de artigos. Obrigado por todos os tipos de feedback positivo.
Hoje tentei considerar, novamente, as coisas básicas do mecanismo OSG. Não tenho certeza do que saiu legal. Mas até agora estou expondo precisamente coisas primitivas, na veia em que eu mesmo as compreendi. Eu verifiquei todos os exemplos pessoalmente, meu repositório está disponível
aqui . Obrigado, queridos colegas, vou tentar manter essa história
continuada ...