Aula Prática: Mapeamento
Disciplina: Computação Gráfica
Professores: Isabel Harb Manssour e Márcio Pinho

 

O objetivo desta aula é aprender a utilizar a biblioteca OpenGL e verificar como funciona o mapeamento através da implementação das operações de zoom e pan. Para isto, será utilizada a API JOGL e a linguagem de programação Java, tendo como resultado um programa que pode ser executado tanto no ambiente Windows como no ambiente Linux.

Para implementação de aplicações com JOGL na linguagem de programação Java é necessário configurar um projeto para linkar as bibliotecas. Na primeira aula prática já foi explicado como configurar o projeto para o ambiente Eclipse, portanto, basta criar um novo projeto, configurá-lo conforme explicado e incluir os programas fonte que serão usados nesta aula.

Inicialmente, verifique o conteúdo do programa fonte fornecido para tentar entender o seu conteúdo. Depois, compile e execute o programa que abrirá uma janela com fundo branco e um triângulo, conforme mostra a próxima figura.

Observe atentamente o código para entender o seu funcionamento, considerando a estrutura básica de um programa OpenGL:

  • Criação de uma janela swing com um GLCanvas;
  • Criação de um objeto que irá gerenciar os eventos (GLEventListener);
  • Implementação da classe responsável pelo rendering e pelo gerenciamento dos eventos (no exemplo, a Renderer2D.java);

Agora altere o código fonte conforme descrito a seguir e, a cada alteração, execute novamente o programa para verificar os resultados obtidos. Em caso de dúvida, consulte a professora ou o tutorial de introdução à OpenGL disponível em http://www.inf.pucrs.br/~manssour/OpenGL/Tutorial.html.

  • Ao invés de passarmos três números float para especificar a cor do desenho, é possível passar três números inteiros, entre 0 e 255, ou seja, um byte. Para isso, altere a chamada da função gl.glColor3f para gl.glColor3ub e passe números entre 0 e 255 para especificar a cor do objeto - use um cast para byte, como, por exemplo, gl.glColor3ub((byte)0, (byte)0, (byte)255);

·         Desenhe duas linhas que representem os eixos cartesianos do universo (use gl.glBegin(GL_LINES) e gl.glEnd(), acrescentando os quatro vértices necessários para o desenho dos eixos); Considere que as coordenadas mínimas e máximas são, respectivamente, -1.0 e 1.0. Importante: todas as rotinas de desenho devem ser chamadas a partir do método display;

·         Substitua a chamada à função gl.glBegin(GL_TRIANGLES);....gl.glEnd(); pelo desenho de um triângulo e de um quadrado, que devem ser definidos de maneira a formar uma casinha, como mostra a próxima figura. Os comandos para desenhar o "telhado" da casinha são apresentados a seguir. Tente desenhar o resto da casinha usando GL_LINE_LOOP. Observe que quando a cor é trocada de um vértice para o outro, OpenGL faz um degrade entre as cores.
gl.glLineWidth(3); //Determina espessura da linha a ser desenhada
gl.glBegin(GL.GL_TRIANGLES);
    gl.glColor3f(0.0f, 0.0f, 1.0f);
    gl.glVertex2f(-0.2f,0.1f);
    gl.glColor3f(1.0f, 0.0f, 0.0f);
    gl.glVertex2f(0.0f,0.22f);
    gl.glColor3f(0.0f, 0.0f, 1.0f);
    gl.glVertex2f(0.2f,0.1f);
gl.glEnd();

·         Para que o código fique mais organizado, coloque os comandos de desenho em métodos separados e chame estes métodos dentro do método display da seguinte maneira:
      public void display(GLAutoDrawable drawable)

     {

          gl.glClear(GL.GL_COLOR_BUFFER_BIT);

          gl.glLoadIdentity();   

 

          gl.glColor3ub((byte)0, (byte)0, (byte)255);

 

          desenhaEixos();

         

          desenhaCasinha();             

   }

  • Aplique uma tranformação geométrica de translação na casinha para mudá-la de lugar utilizando o método gl.glTranslatef(float, float, float) (importante: a translação, bem como as transformações de escala e rotação, deve ser aplicada antes que a casinha seja desenhada).

Após estas alterações, o programa deve abrir uma janela com fundo branco, uma casinha e duas linhas que representam os eixos cartesianos. Agora, como fazer para interagir com o programa sem que seja necessário alterar e executar o código cada vez que o objeto, por exemplo, é trocado de lugar? Neste caso, é necessário gerenciar eventos do teclado e/ou mouse. Assim como já é feito com a tecla ESC, vamos tratar o evento de outras teclas para que o usuário possa interagir com a aplicação.

Portanto, declare os seguintes atributos na classe Renderer2D que serão inicializados, por default, com zero:
private float translacaoX, translacaoY;

Agora altere os parâmetros passados para o método gl.glTranslatef que é chamado no método display, para que sejam passados estes atributos, da seguinte maneira:
gl.glTranslatef(translacaoX, translacaoY, 0f);

Finalmente, para fazer o tratamento dos eventos, complemente a implementação do método keyPressed de maneira a permitir transladar o objeto para cima, para baixo, para a esquerda e para a direita sempre que o usuário pressionar cada uma das teclas de setas (incremente e decremente as variáveis translacaoX e translacaoY)

       public void keyPressed(KeyEvent e)
       {
              switch (e.getKeyCode())
              {      
                    case KeyEvent.VK_RIGHT:       
                                               break;
                    case KeyEvent.VK_LEFT:        
                                               break;     
                    case KeyEvent.VK_UP:    
                                               break;                              
                    case KeyEvent.VK_DOWN:        
                                               break;              
                    case KeyEvent.VK_ESCAPE:   System.exit(0);
                                               break;
              }  
              glDrawable.display();
       }

Agora, pressionando as teclas de setas é possível transladar a casinha, ou seja, deslocá-la para cima, para baixo, para direita e para esquerda. É importante observar atentamente os seguintes métodos implementados: init, desenhaEixos, desenhaCasinha, display e keyPressed.

Para implementar as operações de zoom e pan, altere o código fonte conforme descrito a seguir. O zoom consiste no aumento ou redução do tamanho da window no sistema de referência do universo. Portanto, para aplicá-lo basta alterar as coordenadas do método glu.gluOrtho2D sempre que o usuário, por exemplo, pressionar as teclas Home e End. Veja nas figuras abaixo um exemplo de zoom in e outro de zoom out.

Para tanto, altere o código do método keyPressed que é responsável por gerenciar eventos do teclado, acrescentando o tratamento para as teclas Home(KeyEvent.VK_HOME) e End (KeyEvent.VK_END).

Em seguida, declare quatro atributos do tipo double, chamados left, right, bottom e top, e os inicialize no método init(), conforme mostram os trechos de código abaixo.

 
public class Renderer2D extends KeyAdapter implements GLEventListener
{
        ...
        private double left, right, bottom, top;
        ...
        public void init(GLAutoDrawable drawable)
        {
               left = -1.0;
               right = 1.0;
               bottom = -1.0;
               top = 1.0;
               ...

Isso definirá uma área de trabalho, ou janela de seleção (window) com os limites -1 até 1, tanto para x como para y.

Agora, altere a chamada para o método glu.glutOrtho2D para que use estes novos atributos ao invés de colocar diretamente os valores 1.0 e -1.0, como mostra o exemplo abaixo.

 
...
               glu.gluOrtho2D (left, right, bottom, top);
...

Em seguida, complemente a implementação do método keyPressed, de forma que estes atributos sejam alterados para implementar o zoom-in e o zoom-out (incremente e decremente os atributos usando valores entre 0.0 e 0.5). Como cada vez que estes atributos forem alterados deve-se chamar os métodos listados abaixo, acrescente-os bem no início do método display.

 
public void display(GLAutoDrawable drawable) { 
               gl.glMatrixMode(GL.GL_PROJECTION);
               gl.glLoadIdentity();
               glu.gluOrtho2D (left, right, bottom, top);
               gl.glMatrixMode(GL.GL_MODELVIEW);     
...

Execute o programa após a implementação das alterações e verifique o que acontece cada vez que as teclas Home e End são pressionadas. Observe o efeito visual que ocorre quando essas teclas são pressionadas muitas vezes e coloque um limite para o incremento e decremento dos atributos (tente imaginar os valores para esse limite).

Executando o programa também é possível notar que as linhas que representam os eixos diminuem e aumentam de tamanho. Entretanto, é interessante que elas sempre fiquem dentro do limite da janela, de ponta a ponta. Para que isto aconteça, altere o método desenhaEixos para usar os atributos left, right, bottom e top no lugar dos valores fixos -1.0 e 1.0. Altere também a chamada dos métodos gl.glVertex2d para gl.glVertex2f.

Para implementar o pan, que consiste, basicamente, no deslocamento da window no sistema de referência do universo, o procedimento é semelhante ao zoom, porém os parâmetros do método glu.gluOrtho2D devem ser alterados de maneira que a window seja deslocada para cima, para baixo, para esquerda e para direita. Veja na figura abaixo o resultado de um pan para a direita.

Para implementar esta operação, vamos precisar de dois atributos que armazenem este deslocamento. Estes atributos devem ser declarados e inicializados conforme ilustram os trechos de código a seguir.

 
public class Renderer2D extends KeyAdapter implements GLEventListener
{
        ...
        private float panX, panY;
        ...
        public void init(GLAutoDrawable drawable)
        {
               panX = panY = 0;
               ...
 

Depois, altere todas as chamadas do método glu.gluOrtho2D da seguinte maneira:

glu.gluOrtho2D(left+panX, right+panX, bottom+panY, top+panY);

Agora basta definir quais teclas irão deslocar a window, ou seja, que irão alterar os valores destes atributos. Sugestão: use L, R, T e B.

Exercício: Execute o programa e altere o tamanho da janela diminuindo ao máximo a sua largura. Depois aumente a largura da janela e diminua ao máximo a sua altura. O que acontece com o desenho? Considerando a etapa de mapeamento, pense porque a imagem fica alterada e qual seria a solução. O método reshape, chamado quando o tamanho da janela é alterado, pode ser usado para obter o tamanho da viewport.

 
public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height)
{
        gl.glViewport(0, 0, width, height);      
}

 ../../Imagens/emban15.png (1469 bytes)

../../Imagens/E-MAIL.JPG (3237 bytes)Comentários, dúvidas, sugestões, envie um mail para seu (sua) professor(a)

../../Imagens/emban15.png (1469 bytes)

Última alteração em 19 de março de 2010.