segunda-feira, 11 de novembro de 2013

Texturas em OpenGL

Em nossos últimos posts, estávamos tratando de como usar o OpenGL no Visual Studio. No último post nós criamos um cubo e fizemos ele girar em torno de seu eixo. Neste post vamos aplicar uma textura às faces deste cubo.

Criando texturas

Como estamos usando um cubo, nada melhor do que uma textura de uma caixa para ilustrar nosso exemplo. Estamos portanto pegando a textura de exemplo dos tutoriais da Nehe Productions.
Como aplicar esta textura às faces de nosso cubo?
Bem, o primeiro passo é habilitar as texturas, através do comando:
GL::Enable(EnableCap::Texture2D);
Assim, habilitamos as texturas. Depois disto, precisamos criar uma textura e atribuir a ela uma imagem. Quando criarmos nosso cubo, devemos então indicar qual o ponto da textura que estamos empregando.
Para criar e carregar a textura, vamos criar uma função, assim como no tutorial da Widget-101, que pode ser lido em espanhol para aqueles que querem fazer o mesmo em C#. A nossa função ficará como está descrito abaixo:
public: int loadTexture(String^ filename)
        {
                int id = GL::GenTexture();

                GL::BindTexture(TextureTarget::Texture2D, id);
                //LOAD FILE
                Bitmap^ image;
                try 
                { 
                        image = gcnew Bitmap(filename);
                }
                catch (...) 
                { 
                        MessageBox::Show("Imagem não pode ser carregada", "Erro", MessageBoxButtons::OK, MessageBoxIcon::Exclamation);
                        return -1; 
                };

                BitmapData^ imageData = image->LockBits(Rectangle(0, 0, image->Width, image->Height), ImageLockMode::ReadOnly, System::Drawing::Imaging::PixelFormat::Format32bppRgb);
                GL::TexImage2D(TextureTarget::Texture2D, 0, PixelInternalFormat::Rgb, imageData->Width, imageData->Height, 0, OpenTK::Graphics::OpenGL::PixelFormat::Bgra, PixelType::UnsignedByte, imageData->Scan0);
                image->UnlockBits(imageData);
                //TEXTURE PROPERTY
                GL::TexParameter(TextureTarget::Texture2D, TextureParameterName::TextureMinFilter, (int)TextureMinFilter::Linear);
                GL::TexParameter(TextureTarget::Texture2D, TextureParameterName::TextureMagFilter, (int)TextureMagFilter::Linear);
                return id;
        }
A primeira função nova é GenTexture, que informa ao OpenGL que queremos gerar um nome de textura. Depois disto usamos a função BindTexture, para vincular o nome que criamos a dados de textura. Isto na prática informa ao OpenGL que estamos usando a textura fornecida no segundo argumento. O próximo passo é carregar a imagem. Isto é feito através do objeto Bitmap, que pega o caminho da imagem passado como argumento para a função.
Depois disto, usamos o objeto BitmapData. Este objeto é o que nos dá acesso aos dados do Bitmap, a seus pixels. A propriedade Scan0 deste objeto é o que nos permite fazer isto, e ele será usado como argumento para a função TexImage2D do OpenGL. É esta a função que carrega os dados da imagem para o OpenGL. O que esta função recebe como parâmetros?
O primeiro parâmetro é qual o tipo de textura que estamos carregando, aqui, uma textura 2D. O próximo parâmetro é o nível de detalhe da imagem, geralmente deixado em 0. O próximo parâmetro é a quantidade de componentes de dados. Como nossa textura é colorida, teremos 3 componentes, vermelho, azul, verde. Os dois próximos parâmetros são largura e altura da textura. O sexto parâmetro é a borda, que geralmente é deixada em 0. O sétimo parâmetro é o formato da imagem, que em nosso caso é Azul, Verde, Vermelho, Alpha Blending, nesta ordem. O próximo parâmetro, PixelType::UnsignedByte, informa que os dados são compostos por unsigned bytes. Por fim, fornecemos a propriedade Scan0 de nossa imagem.
As duas funções TexParameter definem algumas propriedades para quando a textura for maior que a imagem usada (a imagem terá que ser "esticada") ou quando a textura for menor que a imagem usada (a imagem terá que ser "compactada"). Os dois usam o filtro linear.

Usando as texturas

Agora que temos nossa textura, chegou a hora de usá-la. Nós escolhemos a textura com a mesma função que escolhemos uma textura ao criá-la: BindTexture. Como criamos só uma textura, nem precisamos chamá-la aqui. Mas se você criar mais de uma textura, é necessário selecionar qual você deseja através desta função. Um detalhe importante desta função é que ela não pode ser chamada entre GL::Begin e GL:End. Você sempre terá que chamá-la antes destas duas funções.
Dito isto, vamos agora reescrever nosso evento Paint:
private: System::Void glGraphicWindow_Paint(System::Object^  sender, System::Windows::Forms::PaintEventArgs^  e) 
         {
                  GL::Clear(ClearBufferMask::ColorBufferBit | ClearBufferMask::DepthBufferBit);
                  OpenTK::Matrix4 lookat = OpenTK::Matrix4::LookAt(OpenTK::Vector3::UnitZ * 3, OpenTK::Vector3::Zero, OpenTK::Vector3::UnitY);
                  GL::MatrixMode(MatrixMode::Modelview);
                  GL::LoadMatrix(lookat);
                  GL::Rotate(angle, OpenTK::Vector3::UnitY);
                  GL::Begin(BeginMode::Quads);
                  {
                           GL::Color3(Color::White);
                           // Face frontal
                           GL::TexCoord2(1.0f, 1.0f);
                           GL::Vertex3(0.5f, 0.5f, -0.5f);
                           GL::TexCoord2(0.0f, 1.0f);
                           GL::Vertex3(-0.5f, 0.5f, -0.5f);
                           GL::TexCoord2(0.0f, 0.0f);
                           GL::Vertex3(-0.5f, -0.5f, -0.5f);
                           GL::TexCoord2(1.0f, 0.0f);
                           GL::Vertex3(0.5f, -0.5f, -0.5f);
                           // Face traseira
                           GL::TexCoord2(1.0f, 1.0f);
                           GL::Vertex3(0.5f, -0.5f, 0.5f);
                           GL::TexCoord2(0.0f, 1.0f);
                           GL::Vertex3(-0.5f, -0.5f, 0.5f);
                           GL::TexCoord2(0.0f, 0.0f);
                           GL::Vertex3(-0.5f, 0.5f, 0.5f);
                           GL::TexCoord2(1.0f, 0.0f);
                           GL::Vertex3(0.5f, 0.5f, 0.5f);
                           // Face Direita
                           GL::TexCoord2(1.0f, 1.0f);
                           GL::Vertex3(0.5f, 0.5f, 0.5f);
                           GL::TexCoord2(0.0f, 1.0f);
                           GL::Vertex3(0.5f, -0.5f, 0.5f);
                           GL::TexCoord2(0.0f, 0.0f);
                           GL::Vertex3(0.5f, -0.5f, -0.5f);
                           GL::TexCoord2(1.0f, 0.0f);
                           GL::Vertex3(0.5f, 0.5f, -0.5f);
                           // Face esquerda
                           GL::TexCoord2(1.0f, 1.0f);
                           GL::Vertex3(-0.5f, 0.5f, 0.5f);
                           GL::TexCoord2(0.0f, 1.0f);
                           GL::Vertex3(-0.5f, -0.5f, 0.5f);
                           GL::TexCoord2(0.0f, 0.0f);
                           GL::Vertex3(-0.5f, -0.5f, -0.5f);
                           GL::TexCoord2(1.0f, 0.0f);
                           GL::Vertex3(-0.5f, 0.5f, -0.5f);
                           // Face superior
                           GL::TexCoord2(1.0f, 1.0f);
                           GL::Vertex3(0.5f, 0.5f, 0.5f);
                           GL::TexCoord2(0.0f, 1.0f);
                           GL::Vertex3(-0.5f, 0.5f, 0.5f);
                           GL::TexCoord2(0.0f, 0.0f);
                           GL::Vertex3(-0.5f, 0.5f, -0.5f);
                           GL::TexCoord2(1.0f, 0.0f);
                           GL::Vertex3(0.5f, 0.5f, -0.5f);
                           // Face inferior
                           GL::TexCoord2(1.0f, 1.0f);
                           GL::Vertex3(0.5f, -0.5f, 0.5f);
                           GL::TexCoord2(0.0f, 1.0f);
                           GL::Vertex3(-0.5f, -0.5f, 0.5f);
                           GL::TexCoord2(0.0f, 0.0f);
                           GL::Vertex3(-0.5f, -0.5f, -0.5f);
                           GL::TexCoord2(1.0f, 0.0f);
                           GL::Vertex3(0.5f, -0.5f, -0.5f);
                  }
                  GL::End();
                  glGraphicWindow->SwapBuffers();
         }
Fizemos algumas modificações na função do último post. A primeira foi retirar todas as seleções de cores. Deixamos apenas a seleção da cor branca, no início do processo. Isto acontece por que o OpenGL mistura a cor da textura com a cor que você seleciona. Assim, se você usar vermelho, sua textura irá se misturar com o vermelho, dando uma textura avermelhada. Deixamos em branco, pois assim o OpenGL usará apenas a cor da textura.
A textura é aplicada através do comando TexCoord2, usada acima. Ela usa dois argumentos, que são as coordenadas X e Y da textura. Algo precisa ser dito sobre estas coordenadas. Elas variam entre 0 e 1, sendo que 0 é a origem do eixo e 1 é a coordenada máxima daquele eixo (largura ou altura da textura). Agora que nosso evento Paint está modificado, vamos modificar o evento Load para carregar nossa textura:
private: System::Void glGraphicWindow_Load(System::Object^  sender, System::EventArgs^  e) 
         {
                  GL::ClearColor(Color::Black);
                  GL::Enable(EnableCap::DepthTest);
                  GL::Enable(EnableCap::Texture2D);
                  textureID = loadTexture("Crate.bmp");
                  SetupViewport();
                  angle = 0.0f;
                  tmrAnimate->Enabled = true;
         }
Chamamos a função que criamos com o nome da imagem como parâmetro (a imagem está salva no mesmo diretório do executável). A variável textureID é uma variável global que usamos para salvar o ID de nossa textura. Agora que habilitamos as texturas e carregamos a textura do exemplo, podemos executar o programa, que nos dará o seguinte resultado:


O que acontece se mudarmos a cor de Branco para Azul?


E assim, terminamos a aplicação de texturas.

Conclusão

O programa de hoje ensina a aplicar vários tipos de texturas aos seus polígonos. Há ainda muitos outros efeitos que podemos usar em nossos programas, como transparência e névoa. Estes dois serão tratados no próximo post.

Nenhum comentário:

Postar um comentário

Você também poderá gostar de