OpenSceneGraph: Noções básicas de textura

imagem

1. Introdução


Já examinamos um exemplo em que eles pintaram um quadrado em todas as cores do arco-íris. No entanto, existe outra tecnologia, a aplicação à geometria tridimensional do chamado mapa de textura ou apenas textura - uma imagem raster bidimensional. Nesse caso, o efeito não está nos vértices da geometria, mas os dados de todos os pixels obtidos durante a rasterização da cena são alterados. Essa técnica pode aumentar significativamente o realismo e os detalhes da imagem final.

O OSG suporta vários atributos de textura e modos de texturização. Mas, antes de falar sobre texturas, vamos falar sobre como o OSG lida com imagens de bitmap. Para trabalhar com imagens raster, é fornecida uma classe especial - osg :: Image, que armazena dados de imagem dentro dela, destinados, em última análise, à texturização do objeto.

1. Apresentação de dados de imagem raster. Classe Osg :: Imagem


A melhor maneira de carregar uma imagem do disco é usar a chamada osgDB :: readImageFile (). É muito semelhante à chamada osg :: readNodeFile (), que já nos aborreceu. Se tivermos um bitmap chamado picture.bmp, o carregamento será parecido com este

osg::ref_ptr<osg::Image> image = osgDB::readImageFile("picture.bmp"); 

Se a imagem for carregada corretamente, o ponteiro será válido; caso contrário, a função retornará NULL. Após o download, podemos obter informações da imagem usando os seguintes métodos públicos

  1. t (), s () er () - retorna a largura, altura e profundidade da imagem.
  2. data () - retorna um ponteiro do tipo char não assinado * para os dados da imagem "brutos". Através desse ponteiro, o desenvolvedor pode atuar diretamente nos dados da imagem. Você pode ter uma idéia do formato dos dados da imagem usando os métodos getPixalFormat () e getDataType (). Os valores retornados por eles são equivalentes aos parâmetros do formato e tipo das funções do OpenGL glTexImage * (). Por exemplo, se a imagem tiver o formato de pixel GL_RGB e o tipo for GL_UNSIGNED_BYTE, três elementos independentes (bytes não assinados) serão usados ​​para representar o componente de cor RGB



Você pode criar um novo objeto de imagem e alocar memória para ele.

 osg::ref_ptr<osg::Image> image = new osg::Image; image->allocateImage(s, t, r, GL_RGB, GL_UNSIGNED_BYTE); unsigned char *ptr = image->data(); //         

Aqui s, t, r são tamanhos de imagem; GL_RGB define o formato de pixel e GL_UNSIGNED_BYTE define o tipo de dados para descrever um único componente de cor. Um buffer de dados interno do tamanho necessário é alocado na memória e é destruído automaticamente se não houver um único link para esta imagem.

O sistema de plug-in OSG suporta o download de quase todos os formatos populares de imagem: * .jpg, * .bmp, * .png, * .tif e assim por diante. É fácil expandir essa lista escrevendo seu próprio plug-in, mas este é um tópico para outra discussão.

2. O básico da texturização


Para aplicar textura a um modelo tridimensional, você precisa executar várias etapas:

  1. Defina as coordenadas de textura dos vértices para o objeto geométrico (no ambiente de designers 3D, isso é chamado de varredura UV).
  2. Crie um objeto de atributo de textura para textura 1D, 2D, 3D ou cúbica.
  3. Defina uma ou mais imagens para um atributo de textura.
  4. Anexe um atributo e modo de textura ao conjunto de estados aplicados ao objeto que está sendo desenhado.

OSG define a classe osg :: Texture, que encapsula todos os tipos de texturas. As subclasses osg :: Texture1D, osg :: Texture2D, osg :: Texture3D e osg :: TextureCubeMap são herdadas dele, o que representa várias técnicas de texturização adotadas no OpenGL.

O método mais comum da classe osg :: Texture é setImage (), que define a imagem usada na textura, por exemplo

 osg::ref_ptr<osg::Image> image = osgDB::readImageFile("picture.bmp"); osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D; texture->setImage(image.get()); 

ou, você pode passar o objeto de imagem diretamente para o construtor da classe de textura

 osg::ref_ptr<osg::Image> image = osgDB::readImageFile("picture.bmp"); osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D(image.get()); 

A imagem pode ser recuperada do objeto de textura chamando o método getImage ().

Outro ponto importante é definir as coordenadas da textura para cada vértice no objeto osg :: Geometry. A transferência dessas coordenadas ocorre através da matriz osg :: Vec2Array e osg :: Vec3Array chamando o método setTexCoordArray ().

Após definir as coordenadas da textura, precisamos definir o número do slot da textura (unidade), pois o OSG suporta a sobreposição de várias texturas na mesma geometria. Ao usar uma única textura, o número da unidade é sempre 0. Por exemplo, o código a seguir ilustra a configuração das coordenadas da textura para a unidade 0 da geometria

 osf::ref_ptr<osg::Vec2Array> texcoord = new osg::Vec2Array; texcoord->push_back( osg::Vec2(...) ); ... geom->setTexCoordArray(0, texcoord.get()); 

Depois disso, podemos adicionar o atributo de textura ao conjunto de estados, ativando automaticamente o modo de textura correspondente (no nosso exemplo GL_TEXTURE_2D) e aplicar o atributo à geometria ou nó que contém essa geometria

 geom->getOrCreateStateSet()->setTextureAttributeAndModes(texture.get()); 

Observe que o OpenGL gerencia os dados da imagem na memória gráfica da placa de vídeo, mas o objeto osg :: Image, juntamente com os mesmos dados, está localizado na memória do sistema. Como resultado, encontraremos o fato de que armazenamos duas cópias dos mesmos dados, ocupando a memória do processo. Se essa imagem não for compartilhada por vários atributos de textura, ela poderá ser excluída da memória do sistema imediatamente após o OpenGL transferi-la para a memória do adaptador de vídeo. A classe osg :: Texture fornece o método apropriado para ativar esta função.

 texture->setUnRefImageDataAfterApply( true ); 

3. Carregue e aplique uma textura 2D


A técnica mais usada é a texturização 2D - sobreposição de uma imagem bidimensional (ou imagens) na borda de uma superfície tridimensional. Considere o exemplo mais simples de aplicação de uma única textura a um polígono quadrangular

Exemplo de textura
main.h

 #ifndef MAIN_H #define MAIN_H #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::Geometry> quad = new osg::Geometry; quad->setVertexArray(vertices.get()); quad->setNormalArray(normals.get()); quad->setNormalBinding(osg::Geometry::BIND_OVERALL); quad->setTexCoordArray(0, texcoords.get()); quad->addPrimitiveSet( new osg::DrawArrays(GL_QUADS, 0, 4) ); 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::Geode> root = new osg::Geode; root->addDrawable(quad.get()); root->getOrCreateStateSet()->setTextureAttributeAndModes(0, texture.get()); osgViewer::Viewer viewer; viewer.setSceneData(root.get()); return viewer.run(); } 


Crie uma matriz de vértices e normais para a face

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

Crie uma matriz de coordenadas de textura

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

O ponto é que cada vértice do modelo tridimensional corresponde a um ponto na textura bidimensional e as coordenadas dos pontos na textura são relativas - elas são normalizadas para a largura e altura reais da imagem. Queremos esticar a imagem inteira a ser carregada no quadrado, respectivamente, os cantos do quadrado corresponderão aos pontos de textura (0, 0), (0, 1), (1, 1) e (1, 0). A ordem dos vértices na matriz de vértices deve corresponder à ordem dos vértices da textura.

Em seguida, crie um quadrado, atribuindo à geometria uma matriz de vértices e uma matriz de normais

 osg::ref_ptr<osg::Geometry> quad = new osg::Geometry; quad->setVertexArray(vertices.get()); quad->setNormalArray(normals.get()); quad->setNormalBinding(osg::Geometry::BIND_OVERALL); quad->setTexCoordArray(0, texcoords.get()); quad->addPrimitiveSet( new osg::DrawArrays(GL_QUADS, 0, 4) ); 

Crie um objeto de textura e carregue a imagem usada para ele.

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

Crie o nó raiz da cena e coloque a geometria que criamos lá

 osg::ref_ptr<osg::Geode> root = new osg::Geode; root->addDrawable(quad.get()); 

e, finalmente, aplique o atributo texture ao nó no qual a geometria é colocada

 root->getOrCreateStateSet()->setTextureAttributeAndModes(0, texture.get()); 



A classe osg :: Texture2D determina se os tamanhos de imagem de textura são múltiplos de uma potência de dois (por exemplo, 64x64 ou 256x512), dimensionando automaticamente imagens inadequadas para o tamanho, na verdade usando a função gluScaleImage () do OpenGL. Existe um método setResizeNonPowerOfTwoHint () que determina se deve ou não redimensionar a imagem. Algumas placas de vídeo requerem um múltiplo do tamanho de uma imagem com a capacidade de duas, enquanto a classe osg :: Texture2D suporta o trabalho com um tamanho de textura arbitrário.

Algo sobre a mistura de texturas


Como já dissemos, as coordenadas da textura são normalizadas de 0 a 1. O ponto (0, 0) corresponde ao canto superior esquerdo da imagem e o ponto (1, 1) corresponde ao canto inferior direito. O que acontece se você definir coordenadas de textura maiores que uma?

Por padrão, no OpenGL, como no OSG, a textura será repetida na direção do eixo, o valor da coordenada da textura excederá um. Essa técnica é frequentemente usada, por exemplo, para criar um modelo de uma longa parede de tijolos, uso uma textura pequena, repetindo sua superposição muitas vezes tanto em largura quanto em altura.

Esse comportamento pode ser controlado pelo método setWrap () da classe osg :: Texture. Como primeiro parâmetro, o método usa o identificador do eixo ao qual o modo de mesclagem deve ser aplicado, passado como o segundo parâmetro, por exemplo

 //     s texture->setWrap( osg::Texture::WRAP_S, osg::Texture::REPEAT ); //     r texture->setWrap( osg::Texture::WRAP_R, osg::Texture::REPEAT ); 

Esse código diz explicitamente ao mecanismo para repetir a textura ao longo dos eixos se er os valores das coordenadas da textura excederem 1. Uma lista completa dos modos de mapeamento de textura:

  1. REPETIR - repita a textura.
  2. ESPELHO - repita a textura, espelhando-a.
  3. CLAMP_TO_EDGE - as coordenadas que vão além de 0 a 1 são ajustadas na borda correspondente da textura.
  4. CLAMP_TO_BORDER - coordenadas que vão além de 0 a 1 fornecerão a cor da borda definida pelo usuário.

4. Renderização para textura


A técnica de renderização de textura permite ao desenvolvedor criar uma textura com base em alguma subcena ou modelo tridimensional e aplicá-la à superfície na cena principal. Uma tecnologia semelhante é freqüentemente chamada de textura de cozimento.

Para assar uma textura dinamicamente, você deve concluir três etapas:

  1. Crie um objeto de textura para renderizar nele.
  2. Renderize a cena em uma textura.
  3. Use a textura resultante como pretendido.

Nós devemos criar um objeto de textura vazio. O OSG permite criar uma textura vazia de um determinado tamanho. O método setTextureSize () permite definir a largura e a altura da textura, bem como a profundidade como um parâmetro adicional (para texturas 3D).

Para renderizar em uma textura, você deve anexá-lo ao objeto da câmera chamando o método attach (), que aceita um objeto de textura como argumento. Além disso, esse método aceita um argumento indicando qual parte do buffer do quadro deve ser renderizada nessa textura. Por exemplo, para transferir um buffer de cores para uma textura, você deve executar o seguinte código

 camera->attach( osg::Camera::COLOR_BUFFER, texture.get() ); 

Outras partes do buffer de quadros disponíveis para renderização incluem o buffer de profundidade DEPTH_BUFFER, o buffer de estêncil STENCIL_BUFFER e buffers de cores adicionais de COLOR_BUFFER0 a COLOR_BUFFER15. A presença de buffers de cores adicionais e seu número é determinado pelo modelo da placa de vídeo.

Além disso, para uma câmera que é renderizada em uma textura, você deve definir os parâmetros da matriz de projeção e da janela de visualização, cujo tamanho corresponde ao tamanho da textura. A textura será atualizada à medida que cada quadro é desenhado. Lembre-se de que a câmera principal não deve ser usada para renderizar uma textura, pois fornece a renderização da cena principal e você apenas obtém uma tela preta. Esse requisito pode não ser atendido apenas quando você executa a renderização fora da tela.

5. Um exemplo da implementação de renderização em textura


Para demonstrar a técnica de renderização em uma textura, implementamos a seguinte tarefa: criar um quadrado, desenhar uma textura quadrada e renderizar uma cena animada na textura, é claro com a cessna que amamos. O programa que implementa o exemplo é bastante volumoso. No entanto, darei seu código fonte completo de qualquer maneira.

Exemplo de Texrender
main.h

 #ifndef MAIN_H #define MAIN_H #include <osg/Camera> #include <osg/Texture2D> #include <osg/MatrixTransform> #include <osgDB/ReadFile> #include <osgGA/TrackballManipulator> #include <osgViewer/Viewer> #endif 

main.cpp

 #include "main.h" //------------------------------------------------------------------------------ // //------------------------------------------------------------------------------ osg::Geometry *createQuad(const osg::Vec3 &pos, float w, float h) { osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array; vertices->push_back( pos + osg::Vec3( w / 2, 0.0f, -h / 2) ); vertices->push_back( pos + osg::Vec3( w / 2, 0.0f, h / 2) ); vertices->push_back( pos + osg::Vec3(-w / 2, 0.0f, h / 2) ); vertices->push_back( pos + osg::Vec3(-w / 2, 0.0f, -h / 2) ); 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(1.0f, 1.0f) ); texcoords->push_back( osg::Vec2(1.0f, 0.0f) ); texcoords->push_back( osg::Vec2(0.0f, 0.0f) ); texcoords->push_back( osg::Vec2(0.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->setTexCoordArray(0, texcoords.get()); quad->addPrimitiveSet(new osg::DrawArrays(GL_QUADS, 0, 4)); return quad.release(); } //------------------------------------------------------------------------------ // //------------------------------------------------------------------------------ int main(int argc, char *argv[]) { (void) argc; (void) argv; osg::ref_ptr<osg::Node> sub_model = osgDB::readNodeFile("../data/cessna.osg"); osg::ref_ptr<osg::MatrixTransform> transform1 = new osg::MatrixTransform; transform1->setMatrix(osg::Matrix::rotate(0.0, osg::Vec3(0.0f, 0.0f, 1.0f))); transform1->addChild(sub_model.get()); osg::ref_ptr<osg::Geode> model = new osg::Geode; model->addChild(createQuad(osg::Vec3(0.0f, 0.0f, 0.0f), 2.0f, 2.0f)); int tex_widht = 1024; int tex_height = 1024; osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D; texture->setTextureSize(tex_widht, tex_height); texture->setInternalFormat(GL_RGBA); texture->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::LINEAR); texture->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::LINEAR); model->getOrCreateStateSet()->setTextureAttributeAndModes(0, texture.get()); osg::ref_ptr<osg::Camera> camera = new osg::Camera; camera->setViewport(0, 0, tex_widht, tex_height); camera->setClearColor(osg::Vec4(1.0f, 1.0f, 1.0f, 1.0f)); camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); camera->setRenderOrder(osg::Camera::PRE_RENDER); camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT); camera->attach(osg::Camera::COLOR_BUFFER, texture.get()); camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF); camera->addChild(transform1.get()); osg::ref_ptr<osg::Group> root = new osg::Group; root->addChild(model.get()); root->addChild(camera.get()); osgViewer::Viewer viewer; viewer.setSceneData(root.get()); viewer.setCameraManipulator(new osgGA::TrackballManipulator); viewer.setUpViewOnSingleScreen(0); camera->setProjectionMatrixAsPerspective(30.0, static_cast<double>(tex_widht) / static_cast<double>(tex_height), 0.1, 1000.0); float dist = 100.0f; float alpha = 10.0f * 3.14f / 180.0f; osg::Vec3 eye(0.0f, -dist * cosf(alpha), dist * sinf(alpha)); osg::Vec3 center(0.0f, 0.0f, 0.0f); osg::Vec3 up(0.0f, 0.0f, -1.0f); camera->setViewMatrixAsLookAt(eye, center, up); float phi = 0.0f; float delta = -0.01f; while (!viewer.done()) { transform1->setMatrix(osg::Matrix::rotate(static_cast<double>(phi), osg::Vec3(0.0f, 0.0f, 1.0f))); viewer.frame(); phi += delta; } return 0; } 


Para criar um quadrado, escreva uma função livre separada

 osg::Geometry *createQuad(const osg::Vec3 &pos, float w, float h) { osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array; vertices->push_back( pos + osg::Vec3( w / 2, 0.0f, -h / 2) ); vertices->push_back( pos + osg::Vec3( w / 2, 0.0f, h / 2) ); vertices->push_back( pos + osg::Vec3(-w / 2, 0.0f, h / 2) ); vertices->push_back( pos + osg::Vec3(-w / 2, 0.0f, -h / 2) ); 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(1.0f, 1.0f) ); texcoords->push_back( osg::Vec2(1.0f, 0.0f) ); texcoords->push_back( osg::Vec2(0.0f, 0.0f) ); texcoords->push_back( osg::Vec2(0.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->setTexCoordArray(0, texcoords.get()); quad->addPrimitiveSet(new osg::DrawArrays(GL_QUADS, 0, 4)); return quad.release(); } 

A função aceita a posição do centro do quadrado e suas dimensões geométricas como entrada. Em seguida, uma matriz de vértices, uma matriz de normais e coordenadas de textura são criadas, após o qual a geometria criada é retornada da função.

No corpo do programa principal, carregue o modelo de cessna

 osg::ref_ptr<osg::Node> sub_model = osgDB::readNodeFile("../data/cessna.osg"); 

Para animar esse modelo, crie e inicialize a transformação de rotação em torno do eixo Z

 osg::ref_ptr<osg::MatrixTransform> transform1 = new osg::MatrixTransform; transform1->setMatrix(osg::Matrix::rotate(0.0, osg::Vec3(0.0f, 0.0f, 1.0f))); transform1->addChild(sub_model.get()); 

Agora crie um modelo para a cena principal - um quadrado no qual renderizaremos

 osg::ref_ptr<osg::Geode> model = new osg::Geode; model->addChild(createQuad(osg::Vec3(0.0f, 0.0f, 0.0f), 2.0f, 2.0f)); 

Crie uma textura vazia para um quadrado de 1024x1024 pixels com um formato de pixel RGBA (cor de três componentes de 32 bits com canal alfa)

 int tex_widht = 1024; int tex_height = 1024; osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D; texture->setTextureSize(tex_widht, tex_height); texture->setInternalFormat(GL_RGBA); texture->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::LINEAR); texture->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::LINEAR); 

Aplique essa textura ao modelo quadrado.

 model->getOrCreateStateSet()->setTextureAttributeAndModes(0, texture.get()); 

Em seguida, crie uma câmera que cozerá a textura

 osg::ref_ptr<osg::Camera> camera = new osg::Camera; camera->setViewport(0, 0, tex_widht, tex_height); camera->setClearColor(osg::Vec4(1.0f, 1.0f, 1.0f, 1.0f)); camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 

A janela de exibição da câmera é do mesmo tamanho da textura. Além disso, não se esqueça de definir a cor de fundo ao limpar a tela e a máscara de limpeza, indicando para limpar o buffer de cores e o buffer de profundidade. Em seguida, configure a câmera para renderizar em textura

 camera->setRenderOrder(osg::Camera::PRE_RENDER); camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT); camera->attach(osg::Camera::COLOR_BUFFER, texture.get()); 

A ordem de renderização de PRE_RENDER indica que esta câmera está renderizando antes de renderizar para a cena principal. Especifique o FBO como o destino da renderização e anexe nossa textura à câmera. Agora configuramos a câmera para trabalhar em um sistema de coordenadas absolutas e, como cena, configuramos nossa subárvore, que queremos renderizar em uma textura: transformação de rotação com um modelo de cessna anexado a ela

 camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF); camera->addChild(transform1.get()); 

Crie um nó de grupo raiz adicionando o modelo principal (quadrado) e uma textura de processamento de câmera a ele

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

Crie e personalize um visualizador

 osgViewer::Viewer viewer; viewer.setSceneData(root.get()); viewer.setCameraManipulator(new osgGA::TrackballManipulator); viewer.setUpViewOnSingleScreen(0); 

Configure a matriz de projeção para a câmera - uma projeção em perspectiva através dos parâmetros da pirâmide de recorte

 camera->setProjectionMatrixAsPerspective(30.0, static_cast<double>(tex_widht) / static_cast<double>(tex_height), 0.1, 1000.0); 

Montamos uma matriz de vista que define a posição da câmera no espaço em relação à origem da sub-cessna

 float dist = 100.0f; float alpha = 10.0f * 3.14f / 180.0f; osg::Vec3 eye(0.0f, -dist * cosf(alpha), dist * sinf(alpha)); osg::Vec3 center(0.0f, 0.0f, 0.0f); osg::Vec3 up(0.0f, 0.0f, -1.0f); camera->setViewMatrixAsLookAt(eye, center, up); 

Por fim, anime e exiba a cena, alterando o ângulo de rotação da aeronave em torno do eixo Z em cada quadro

 float phi = 0.0f; float delta = -0.01f; while (!viewer.done()) { transform1->setMatrix(osg::Matrix::rotate(static_cast<double>(phi), osg::Vec3(0.0f, 0.0f, 1.0f))); viewer.frame(); phi += delta; } 

Como resultado, temos uma imagem bastante interessante



Neste exemplo, implementamos algumas animações de cena, mas lembre-se de que expandir o loop run () e alterar os parâmetros de renderização antes ou depois da renderização do quadro é uma atividade insegura em termos de organização do acesso aos dados de diferentes fluxos. Como o OSG usa renderização multithread, também existem mecanismos regulares para incorporar suas próprias ações no processo de renderização, que fornecem acesso seguro aos dados aos threads.

6. Salvando o resultado da renderização em um arquivo


O OSG suporta a capacidade de conectar um objeto osg :: Image à câmera e salvar o conteúdo do buffer de quadros no buffer de dados da imagem. Depois disso, é possível salvar esses dados em disco usando a função osg :: writeImageFile ()

 osg::ref_ptr<osg::Image> image = new osg::Image; image->allocateImage( width, height, 1, GL_RGBA, GL_UNSIGNED_BYTE ); camera->attach( osg::Camera::COLOR_BUFFER, image.get() ); ... osgDB::writeImageFile( *image, "saved_image.bmp" ); 

Conclusão


Talvez o material apresentado no artigo pareça trivial. No entanto, descreve os princípios básicos do trabalho com texturas no OpenSceneGraph, nos quais se baseiam técnicas mais complexas para trabalhar com esse mecanismo, sobre as quais falaremos definitivamente no futuro.

Para continuar ...

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


All Articles