quinta-feira, 11 de julho de 2013

Mudança do Espaço Canônico para o de Tela – Computação Gráfica

O segundo trabalho da disciplina de Introdução a Computação Gráfica da UFPB lecionada pelo Prof. Christian Pagot tem como objetivo a implementação de transformações entre espaços de coordenadas. A atividade consiste em implementar o penúltimo estágio do Pipeline Gráfico, a mudança do espaço canônico (espaço delimitado pelas coordenadas de [-1,-1,-1] até [1,1,1] ) para o espaço de tela (espaço delimitado pelas coordenadas inteiras [0,0,0] até as dimensões da tela) para que possa ser feita a Rasterização (último estágio do pipeline), assunto do meu primeiro post.

Para maior facilidade de implementação do segundo e do possível terceiro trabalho, fiz algumas melhoras. São elas:

- Implementei uma biblioteca que gerenciasse toda interface com o usuário e implementação do pipeline gráfico ( Até agora apenas só os dois últimos passos do pipeline);
- Orientação a Objeto em C++;
- Criação dos módulos de Matrizes e Vetores para facilitar e abstrair algumas operações.

Segue os cabeçalhos de todos os módulos utilizados.

1:  #include "definitions.h"  
2:  #include "lib/Color.h"  
3:  #include "lib/Vertex.h"  
4:  #include "lib/Matrix.h"  
5:  #include "lib/Vector.h"  


Implementação

A implementação foi feita toda na função private void myglCanonicalToScreen(void) encontrada na classe MyGL. Seguindo a sequencia de passos:
  1. Inverter a coordenada Y ( Transformação de Escala );
  2. Escalar as coordenadas X e Y pela metade do tamanho da tela;
  3. Transladar pela metade do tamanho da tela nas coordenadas X e Y;
  4. Enfim, arrendondar e truncar os valores para que possa ser feita a rasterização corretamente.

Obs.: O desenhos nesse momento estarão invertidos, porem a tela tem o eixo Y direcionado para baixo fazendo assim com que o desenho seja desenhado na sua forma original.

Podemos perceber que toda sequencia consiste em duas matrizes a de translação e a de escala, são elas:
Translação

  
   Escala
Essas são as transpostas das matrizes de translação e de escala, pois eu estou implementando usando vetor linha. Implementei duas funções myglTranslate() e myglScale() que retorna as matrizes correspondentes.

1:  /********************************************************  
2:   * Transformações  
3:   ********************************************************/  
4:  void MyGL::myglTranslate(MYGLmatrix &matrix, float dx = 0, float dy = 0, float dz = 0, float dw = 1 )  
5:  {  
6:     MYGLmatrix temp{{1,0,0,0},  
7:                {0,1,0,0},  
8:                {0,0,1,0},  
9:                {dx,dy,dz,dw}};  
10:     matrix = temp;   
11:  }  
12:  void MyGL::myglScale(MYGLmatrix &matrix, float sx = 1, float sy = 1, float sz = 1, float sw = 1 )  
13:  {  
14:     MYGLmatrix temp{{sx,0,0,0},  
15:                {0,sy,0,0},  
16:                {0,0,sz,0},  
17:                {0,0,0,sw}};  
18:     matrix = temp;   
19:  }  

Segue abaixo o exemplo que eu utilizarei ainda no espaço canônico:


1. Inverter a coordenada Y

Invertemos as coordenadas Y de todos os vertices usando a matriz de escala seguinte:



myglScale(invertY, 1,-1);




Ilustração depois de invertemos as coordenadas Y dos vertices.



2. Escalamos as coordenadas X e Y com o valor da metade do tamanho da largura e altura da janela. Sendo IMAGE_WIDTH e IMAGE_HEIGHT constantes de largura e altura. Usando a seguinte matriz:


myglScale(scale,(IMAGE_WIDTH – 1) / 2,(IMAGE_HEIGHT – 1) / 2);






Na imagem estou supondo que o tamanho da tela é 511, ou seja, de 0 até 510.


3. Agora, transladamos as coordenadas X e Y com o mesmo valor usado no passo anterior. Segue a matriz utilizada:


myglTranslate(trans,(IMAGE_WIDTH – 1) / 2,(IMAGE_HEIGHT – 1) / 2, 0);





Exemplo após a translação:


4. Arrendondamos e truncamos usando a seguinte função myglRound(), logo abaixo sua implementação:

1:  // **************************************************************************  
2:  MYGLvector &MyGL::myglRound(MYGLvector &res)  
3:  {  
4:     for(int j = 0; j < res.getSize(); j++)  
5:        res[j] = (int)(res[j] + 0.5);  
6:     return res;  
7:  }  

Podemos também executar tudo de uma vez usando apenas uma matriz então basta multiplicarmos na ordem que o mais a direita seja a primeira transformação e o mais a esquerda a ultima transformação, porem estou implementando o vetor linha então temos que inverter essa ordem, ficando assim a primeira transformação mais a esquerda. Para isso sobrecarreguei o operador de multiplicação para tipo Matrix<float> que foi definido como MYGLmatrix. Com isso temos o exemplo após todas as transformações e o eixo de coordenada Y virado para baixo como é utilizado pela tela.

Matriz após as multiplicações:




Segue abaixo a implementação de toda a função myglCanonicalToScreen(void):

1:  // ********************************************************************  
2:  void MyGL::myglCanonicalToScreen(void)  
3:  {  
4:     MYGLmatrix trans(4,4); // translação  
5:     MYGLmatrix scale(4,4); // escala  
6:     MYGLmatrix invertY(4,4); // escala invertida  
7:    
8:     MYGLmatrix MatrixScreen(4,4); // Matriz resultado  
9:    
10:     // Aplica as transformações  
11:     myglTranslate(trans,(IMAGE_WIDTH - 1)/2.0f,(IMAGE_HEIGHT - 1)/2.0f);  
12:     myglScale(invertY,1.0f,-1.0f);  
13:     myglScale(scale,(IMAGE_WIDTH - 1)/2.0f,(IMAGE_HEIGHT - 1)/2.0f);  
14:    
15:     /* Estou usando a respresentação vetor coluna então usamos as   
16:       transpostas das matrizes e invertemos a ordem de multiplicação*/  
17:     MatrixScreen = invertY * scale * trans;  
18:    
19:     // Pegamos toda malha de vertices e multiplicamos pela matriz retornando  
20:     // os vertices já transformados e armazena atualiza os valores de todos eles  
21:     for(auto p = vectorVertex.begin(); p != vectorVertex.end(); p++ )     
22:     {     
23:        MYGLvector result(4);  
24:        result = p->getVector() * MatrixScreen;  
25:        p->setVector( myglRound(result) );  
26:     }  
27:  }  

Observação: O processo de Rasterização se encontra na função myglRasterization(void) as duas funções são executadas na função Flush(void). E a classe MyGL possui um vector de Vertex que é usada tanto na hora da mudança de espaço e na de desenhar.

Agora alguns printscreens para comparação dos resultados com o do OpenGL, vale lembrar que estou usando uma interface parecida com a do OpenGL:





















1:  //-----------------------------------------------------------------------------  
2:  void MyGlDraw(void)  
3:  {  
4:     //*************************************************************************  
5:     // Chame aqui as funções do mygl.h  
6:     //*************************************************************************  
7:     MyGL::ClearColor(0,0,0);  
8:     MyGL::Clear();  
9:    
10:     MyGL::setColor(255,255,255);  
11:    
12:     MyGL::Begin(MYGL_LINES);  
13:        MyGL::setVertex(-0.5f,-0.5f);  
14:        MyGL::setVertex( 0.5f,-0.5f);  
15:        MyGL::setVertex(0.5f,-0.5f);  
16:        MyGL::setVertex(0.0f,0.5f);  
17:        MyGL::setVertex(0.0f,0.5f);  
18:        MyGL::setVertex(-0.5f,-0.5f);  
19:     MyGL::End();  
20:       
21:     MyGL::Flush();  
22:  }  


1:  void display(void)  
2:  {  
3:     glClearColor(0.0f, 0.0f, 0.0f, 1.0f);  
4:     glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);  
5:     glViewport(0, 0, 512, 512);  
6:    
7:     glOrtho(-1.0f,1.0f,-1.0f,1.0f,-1.0f,1.0f);  
8:     glColor3f(1.0f,1.0f,1.0f);  
9:     // Alterar o trecho abaixo para desenhar os pontos, linhas e triãngulos   
10:     // a serem utilizados nas comparações.  
11:     glBegin(GL_LINES);  
12:        glVertex3f(-0.5f,-0.5f,0.0f);  
13:        glVertex3f( 0.5f,-0.5f,0.0f);  
14:        glVertex3f(0.5f,-0.5f,0.0f);  
15:        glVertex3f(0.0f,0.5f,0.0f);  
16:        glVertex3f(0.0f,0.5f,0.0f);  
17:        glVertex3f(-0.5f,-0.5f,0.0f);  
18:     glEnd();  
19:     glFlush();  
20:     glutSwapBuffers();  
21:     glutPostRedisplay();  
22:  }  






1:  //-----------------------------------------------------------------------------  
2:  void MyGlDraw(void)  
3:  {  
4:     //*************************************************************************  
5:     // Chame aqui as funções do mygl.h  
6:     //*************************************************************************  
7:     MyGL::ClearColor(0,0,0);  
8:     MyGL::Clear();  
9:    
10:     MyGL::Begin(MYGL_TRIANGLES);  
11:        MyGL::setColor(255,0,0);  
12:        MyGL::setVertex(-0.5f,-0.5f);  
13:        MyGL::setColor(0,255,0);  
14:        MyGL::setVertex(0.5f,-0.5f);  
15:        MyGL::setColor(0,0,255);  
16:        MyGL::setVertex(0.0f,0.5f);  
17:     MyGL::End();  
18:       
19:     MyGL::Flush();  
20:  }  



1:  //­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­  
2:  void display(void)  
3:  {  
4:     glClearColor(0.0f, 0.0f, 0.0f, 1.0f);  
5:     glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);  
6:     glViewport(0, 0, 512, 512);  
7:    
8:     glOrtho(-1.0f,1.0f,-1.0f,1.0f,-1.0f,1.0f);  
9:     // Alterar o trecho abaixo para desenhar os pontos, linhas e triãngulos   
10:     // a serem utilizados nas comparações.  
11:     glBegin(GL_TRIANGLES);  
12:        glColor3f(1.0f,0.0f,0.0f);  
13:        glVertex3f(-0.5f,-0.5f,0.0f);  
14:        glColor3f(0.0f,1.0f,0.0f);  
15:        glVertex3f(0.5f,-0.5f,0.0f);  
16:        glColor3f(0.0f,0.0f,1.0f);  
17:        glVertex3f(0.0f,0.5f,0.0f);  
18:     glEnd();  
19:     glFlush();  
20:     glutSwapBuffers();  
21:     glutPostRedisplay();  
22:  }  


Possíveis melhoras

Não vejo uma possível melhora com a implementação do que foi proposto, porem tem muita coisa a ser feita e melhorada em relação a Classe MyGL que ainda não está completa e não possui um tratamento de exceção adequado.

Dificuldades encontradas

Também não tive problemas com a implementação do trabalho em si. A principal dificuldade encontrada foi com minha adaptação e aprendizado com o C++ que me fez perder muito tempo preparando minha classe MyGL pra que ficasse apta a implementar a mudança de espaços e a implementação da sobrecarga do operador * pois devido algumas dificuldade com a linguagem em si.

Referência bibliográfica s

- Foley, Capítulo 5: "Geometrical Transformations"