RASTERIZAÇÃO

APLICAÇÕES DE TÉCNICAS DE RASTERIZAÇÃO EM COMPUTAÇÃO GRÁFICA.....

PIPELINE GRÁFICO

ETAPAS DO PIPELINE GRÁFICO.....

quinta-feira, 18 de junho de 2015

PIPELINE GRÁFICO


           Pipeline Gráfico consiste em uma sequência de passos utilizados para criar uma representação raster 2D de uma cena 3D. Claramente falando, uma vez que um modelo 3D foi criado, por exemplo, em um jogo ou qualquer outra animação por computador em 3D, o pipeline de gráficos é o processo de transformar esse modelo em 3D no que o computador exibe.
         Cada passo de um pipeline, envolve uma mudança de sistema de coordenadas (mudança de espaço). Consequentemente, iremos usar diversas transformações geométricas para gerar novos sistemas de bases, por isso a importância de representar vértices e transformações por meio de matrizes.
             Abaixo temos a imagem que mostra as etapas desse pipeline:        
Figura 1. Etapas do Pipeline Gráfico
Etapas do Pipeline Gráfico


Biblioteca de álgebra linear

             Primeiramente é essencial para o desenvolvimento do trabalho, a criação de funções que realizem cálculos algébricos envolvendo vetores e matrizes. As funções criadas para esta finalidade são descritas a seguir:

float norm(const vec4f& v) - Normaliza um vetor.
vec4f normalize(const vec4f v) - Retorna o comprimento de um vetor.
float dot(const vec3f& v1, const vec3f& v2); - Retorna o valor do produto escalar (v1∙v2) entre dois vetores.
vec3f cross(const vec3f& v1, const vec3f& v2); - Realiza o produto vetorial entre dois vetores (v1 × v2).
void setIdentity(Matrix M) - Faz uma matriz M virar uma matriz identidade.

           A multiplicação de matriz por matriz foi feita similar a utilizada pela biblioteca glm, utilizando o carácter (*), como por exemplo mat4f *mat4f.
          Todos os vetores utilizados são compostos de 3 elementos e todas as matrizes são matrizes 4x4, ambos são alocados dinamicamente durante a execução do programa.

Espaço do Objeto → Espaço do Universo

          Um objeto em seu estado inicial é dito estar no “espaço do objeto”, e o seu centro está sempre localizado na origem. Ao aplicar em seus vértices transformações geométricas como escala, rotação ou translação, dizemos levar os vértices do espaço do objeto para o “espaço do universo”. A transição entre esses espaços é demonstrada na figura abaixo:

Figura 2. Transformação Espaço do Objeto em Espaço do Universo. 
             Para chegarmos a etapa mostrada acima na figura 2 é realizada através da multiplicação dos vértices do objeto descrito em coordenadas do objeto pela matriz de modelagem (model matrix). A matriz de modelagem contém as transformações que se deseja aplicar a determinado objeto, onde cada modelo pode ter a sua própria matriz. Esta matriz pode ser composta por translação, rotação, escala e shear. Caso não seja desejado aplicar nenhuma transformação ao objeto, a matriz de modelagem é a identidade.

Abaixo segue o código da implementação das matrizes de rotação, translação, escala.

              As funções Scale e Translate é responsável pela transformação de escala e rotação respectivamente. As matrizes que representam essas transformações são mostradas a seguir:

Figura 3. Matriz de Escala
Figura 4. Matriz de Translação
Figura 5. Implementação Matriz Escala e Translação

             A função Rotate é responsável pela transformação de rotação e sua implementação necessitou um pouco mais de atenção que as anteriores. Isso por que o objeto pode ser rotacionado em cada um dos eixos x, y e z. Para cada uma das rotações possíveis, existe uma matriz diferente para representá-la. Essas matrizes são mostradas nas figuras a seguir e posteriormente o código da implementação dessas matrizes:
            
Figura 6. Matriz de Rotação Eixo X
Figura 7. Matriz de Rotação Eixo Y
Figura 8. Matriz de Rotação Eixo Z
Figura 9. Implementação da Matriz de Rotação nos Eixos X,Y,Z
             Na função, a variável “degrees” indica quantos graus deseja-se rotacinar. Os valores x, y, e z, indicam sobre quais eixos ocorrerão essa rotação, tendo-se que atribuir o valor 1 nos eixos em que se pretende rotacionar.

Espaço do Universo → Espaço da Câmera

            O próximo passo é levar o objeto agora no espaço do universo para o espaço da câmera, para isso devemos definir de onde serão visualizados os objetos, ou seja, quais as coordenadas da câmera e para onde ela está olhando. Na verdade a câmera estará sempre localizada na origem olhando para o sentido negativo do eixo z. O que muda são as coordenadas dos vértices dos objetos, que passam por uma mudança de sistema de coordenadas. Quando este processo ocorre, dizemos levar os vértices do espaço do universo paro o “espaço da câmera”.
            Para “mudar” a posição da câmera, basta definir o ponto no espaço em que ela deverá estar, e nesse ponto definir uma base ortonormal. Situação mostrada na imagem a seguir:

Figura 10. Definição base da cãmera
           Em seguida, realiza-se sobre as coordenadas dos vértices o processo chamado de “mudança de base”, que irá levar os vértices para o sistema de coordenadas definido pela base da câmera. Ao final, teremos uma situação similar à mostrada na figura a seguir:

Figura 11. Objeto Espaço da Câmera
           O processo de levar os vértices do espaço do universo para o espaço da câmera é realizado multiplicando-os por uma matriz especial chamada view. A função LookAt foi implementada com a finalidade de montar esta matriz, sua implementação é mostrada a seguir:

Figura 12. Implementação da Matriz View
            No código acima as variáveis eye, center e up, significa a posição da câmera, o sentido que a câmera está olhando e o up, respectivamente.

Espaço da Câmera → Espaço de Recorte → Espaço Canônico

          Agora os vértices serão multiplicados por uma matriz especial chamada projection. Essa matriz leva os vértices do espaço da câmera para o “espaço de recorte”. A multiplicação por essa matriz irá definir o tipo de projeção, podendo ser ortogonal ou perspectiva. A projeção perspectiva permite a sensação de profundidade dos objetos na tela, o mesmo não ocorre com a projeção ortogonal. Outro detalhe é que a projeção ortogonal preserva o paralelismo das retas, já a projeção perspectiva não. A figura abaixo exemplifica estas situações.

Figura 13. Projeção Ortogonal e Projeção Perspectiva
          A matriz de projeção simples leva em consideração somente a distância do view plane, também chamado de far plane. A matriz que representa essa projeção é mostrada a seguir:
Figura 14. Matriz Projeção Simples

             A matriz de projeção completa leva em consideração o near plane, o far plane, e também as dimensões do near plane. Esta matriz permite que os pontos estejam no espaço canônico logo após a divisão por w. A matriz utilizada pela opengl para projeção perpectiva é mostrada a seguir:
Figura 15. Matriz Projeção Perspectiva OpenGl
              O valores de n, f, l, r, b e t são como mostrados da figura abaixo:
Figura 16. Frustum
             A implementação da função que monta essa matriz é mostrada a seguir:

Figura 17. Implementação do Frustum e da Projeção Perspectiva

Espaço Canônico → Espaço da Tela

            No espaço canônico é garantido que todos os vértices da cena visível possui os valores de suas coordenadas entre -1 e 1. Este espaço é obtido quando, após multiplicar os vértices pelas matrizes model, view e projection, dividi-se as coordenadas dos vétices por sua coordenada w (coordenada homogênea). 
            Após os espaço canônico é preciso preparar os vértices para serem rasteirados na tela. Este processo e feito multiplicando os vértice por uma matriz chamada viewport. Essa matriz leva os vértices do espaço canônico para o “espaço da tela e é formada pela multiplicação da matrizes mostradas na figura abaixo:

Figura 18. Multiplicação de matrizes que gera a viewport
           Na imagem acima, w é o número de colunas de pixels da tela e h o número de linhas de pixel da tela. Estas matrizes consistem em duas escalas e uma translação. Uma das escalas é para inverter as coordenadas y dos vértices, pois na tela o eixo y cresce para baixo. A outra escala é pra fazer com que todo os espaço entre 1 e -1 fique nas mesmas dimensões da tela. E a translação é para levar os objetos para o centro da tela. A função implementada para montar a matriz viewport é mostrada a seguir:

Figura 19. Implementação da viewport

           Por último utilizando conceitos anteriores de rasterização e carregando um objeto, será rasterizado na tela, passando por cada um dos espaços mostrados anteriormente:

Figura 20. Função de Rasterização do Objeto
Simulações

           O vídeo 1 mostra a simulação realizada utilizando a matriz de projeção simples que leva somente o far plane em consideração.

Vídeo 1

           O vídeo 2 mostra a comparação entre o Pipeline Gráfico implementado com o Pipeline Gráfico utilizado pela OpenGl. A janela da esquerda é a rasterização implementada no trabalho, e a janela da direita, a rasterização da opengl.

Vídeo 2


Dificuldades encontradas:

          Uma das dificuldades encontradas foi configurar as bibliotecas a serem utilizadas na IDE onde foi desenvolvido o trabalho.

Referências bibliográficas:

PAGOT, C. A. Introduction to the Graphics Pipeline. Universidade Federal da Paraíba. Junho de 2015. Disponível em: <http://presencial.virtual.ufpb.br/pluginfile.php/29162/mod_resource/content/1/5_graphics_pipeline.pdf> Acesso em: Junho de 2015.

PAGOT, C. A. Geometric Transformations. Universidade Federal da Paraíba. Junho de 2015. Disponível em: <http://presencial.virtual.ufpb.br/pluginfile.php/30803/mod_resource/content/1/6_geometric_transformations.pdf> Acesso em: Junho de 2015.

PAGOT, C. A. Change of Coordinate System. Universidade Federal da Paraíba. Junho de 2015. Disponível em: <http://presencial.virtual.ufpb.br/pluginfile.php/30800/mod_resource/content/1/7_coordinate_system_change.pdf> Acesso em: Junho de 2015.

PAGOT, C. A. Modeling and View Transform. Universidade Federal da Paraíba. Junho de 2015. Disponível em: <http://presencial.virtual.ufpb.br/pluginfile.php/30801/mod_resource/content/1/8_model_view_transform.pdf> Acesso em: Junho de 2015.

PAGOT, C. A. Projection Transform. Universidade Federal da Paraíba. Junho de 2015. Disponível em: <http://presencial.virtual.ufpb.br/pluginfile.php/30805/mod_resource/content/1/9_projection_transform.pdf> Acesso em: Junho de 2015.

PAGOT, C. A. Canonical and Screen Space. Universidade Federal da Paraíba. Junho de 2015. Disponível em: <http://presencial.virtual.ufpb.br/pluginfile.php/31860/mod_resource/content/1/10_canonical_screen_space.pdf> Acesso em: Junho de 2015.

sexta-feira, 1 de maio de 2015

Rasterização


Rasterização, é a tarefa de converter uma imagem vetorial em uma imagem raster (pixels ou pontos) para a saída em vídeo ou impressora.

Configuração Inicial

Antes de começar o trabalho foi preciso instalar o OpenGL/Glut onde foi instalado o FreeGlut e FreeGlut-Dev em ambiente Linux.

Rasterização de Ponto

Para desenhar um pixel foi utilizado um ponteiro FBptr onde o mesmo aponta para o inicio da memória de vídeo, onde especificamente trata-se do pixel(0,0).
Todo pixel da tela é formado por quatro componentes (R,G,B,A), as quais em conjunto, ocupam um espaço em memória de 4 bytes, sendo 1 byte para cada componente. Podemos acessar qualquer pixel da tela.

Foi feito uma estrutura para organizar e armazenar os valores do pixel.










Em seguida temos a função PutPixel, onde recebe como parâmetros as coordenadas do pixel e a cor ao qual será exibido na tela.









Para testar a função foram feitas modificações na função MyGlDraw, como mostrado no trecho de código abaixo:









Após compilar o código e executá-lo, temos o seguinte resultado:


Rasterização de Retas

Para determinada tarefa utilizou-se a função DrawLine onde a mesma é responsável pela rasterização da reta. Ela receberá como parâmetros dois pontos e suas respectivas cores. A reta formada por esses dois pontos, partindo do ponto inicial com coordenadas (x0, y0)  até chegar ao ponto final com coordenadas (x1, y1) . O algoritmo utilizado como base para a execução desta função será o algoritmo de Bresenham.

Algoritmo de Brasenham

O algoritmo de Bresenham é um algoritmo utilizado para criar retas, ele utiliza uma variável de decisão normalmente chamada “d”. A cada novo ponto desenhado numa determinada posição (x,y), o valor de d é recalculado para determinar onde será desenhado o próximo ponto. Se o novo valor for positivo, o próximo ponto será desenhado no pixel que se encontra nas coordenadas (x+1, y+1), caso contrário, o próximo ponto será desenhado nas coordenada (x+1, y).

void DrawLine(ponto p_inicial, ponto p_final, color cor){

    int slope;
    int dx, dy, incremento_E, incremento_NE, d, x, y;

    //inverte a linha x1 > x2
    if (p_inicial.x > p_final.x){
        DrawLine(p_final, p_inicial, cor);
         return;
    }//if
    dx = p_final.x - p_inicial.x;
    dy = p_final.y - p_inicial.y;

    if (dy < 0){ //Incremento decrescente
        slope = -1;
        dy = -dy;
    }
    else{//Incremento Crescente
       slope = 1;
    }   

    // Constante de Bresenham
    incremento_E = 2 * dy;
    incremento_NE = 2 * dy - 2 * dx;
    d = 2 * dy - dx;

    x = p_inicial.x;/**inicia x com o valor de p_inicial**/
    y = p_inicial.y;

    PutPixel(p_inicial.x, p_inicial.y, cor);

    while(x != p_final.x){
        if (d <= 0){
          d += incremento_E;
          x++;/**Adicionado incremento de x**/
        }
        else{
          d += incremento_NE;
          x++;/**Adicionado incremento de x**/
          y += slope;
        }
        PutPixel(x, y, cor);
    }

}//fim da DrawLine


Para testar este algoritmo,  a modificação na função MyGlDraw é mostrada no trecho de código a seguir e posteriormente o resultado obtido:





Interpolação Linear e Rasterização para todos os quadrantes.

O caso mais simples de resolver é quando x0 > x1. Sempre que este caso ocorrer basta inverter os pontos inicial e final da reta, o resultado será a mesma reta, só que agora com x0 < x1. Com isso é garantido que x0 < x1 sempre irá ocorrer.
Para o caso de y0 > y1, um dos problemas é que dy terá sinal negativo, a solução será inverter o sinal de dy para que ele volte a ser positivo. O outro problema é que y agora deverá ser decrementado e não somente incrementado, como o algoritmo faz. A solução será criar uma variável que permita que as coordenadas y sejam incrementadas ou decrementadas dependendo da situação.
Para o caso de dx < dy, o algoritmo é praticamente o mesmo, basta apenas trocar todos valores de dx por dy e vice-versa, levando em consideração que agora y também irá variar quando d<=0, e não x.
Feitas essas modificações e implementado a interpolação linear, o código final da função DrawLine é mostrado abaixo:
void DrawLine(ponto p_inicial, ponto p_final, color color0, color color1){
    color cor;
    int slope = 1; // Diz se o incremento de y é crescente ou decrescente
    int dx, dy, incremento_E, incremento_NE, d, x, y;
    int i = 0;
   
    // Onde inverte a linha x1 > x2
    if (p_inicial.x > p_final.x){
        DrawLine(p_final, p_inicial, color0, color1);
         return;
    }//if
    dx = p_final.x - p_inicial.x;
    dy = p_final.y - p_inicial.y;

    if(p_inicial.y > p_final.y){
    dy = -dy;
    slope = -1;
    }else{
    slope = 1;       
    }

    /*if (dy < 0){ //Incremento decrescente
        slope = -1;
        dy = -dy;
    }
    else{//Incremento Crescente
       slope = 1;
    }*/
    PutPixel(p_inicial.x, p_inicial.y, color0);
   
    /*// Constante de Bresenham
    incremento_E = 2 * dy;
    incremento_NE = 2 * dy - 2 * dx;
    d = 2 * dy - dx;
*/
    x = p_inicial.x;
    y = p_inicial.y;

    //PutPixel(p_inicial.x, p_inicial.y, color);/**Antes não tinha essa linha**/
    if(dx >= dy){
    // Constante de Bresenham
        incremento_E = 2 * dy;
        incremento_NE = 2 * dy - 2 * dx;
        d = 2 * dy - dx;

    while(x != p_final.x){
        if (d <= 0){
          d += incremento_E;
          x++;/**Adicionado incremento de x**/
        }
        else{
          d += incremento_NE;
          x++;/**Adicionado incremento de x**/
          y += slope;
        }

    /* Interpolação Linear de cores*/
    i++;
    int R = color0.R - ((i)/(float)dx) * (color0.R - color1.R);
    int G = color0.G - ((i)/(float)dx) * (color0.G - color1.G);
    int B = color0.B - ((i)/(float)dx) * (color0.B - color1.B);
    int A = color0.A - ((i)/(float)dx) * (color0.A - color1.A);
    cor.R = R;
    cor.G = G;
    cor.B = B;
    cor.A = A;   
    PutPixel(x, y, cor);
    }//while
 
  }else{
    int d = 2 * dx - dy;
        int incremento_E = 2 * dx;
        int incremento_NE = 2 * dx - 2 * dy;

        while (y != p_final.y){//Enquanto y for diferente de y1
            if (d <= 0) {//se d <= 0
                d += incremento_E;//d será somado ao valor de incr_e
                y+=slope;
            } else {//Senão,
                d += incremento_NE;//d será somado ao valor de incr_ne
                x++;
                y+=slope;
            }

    /* Interpolação Linear de cores*/
    i++;
    int R = color0.R - ((i)/(float)dx) * (color0.R - color1.R);
    int G = color0.G - ((i)/(float)dx) * (color0.G - color1.G);
    int B = color0.B - ((i)/(float)dx) * (color0.B - color1.B);
    int A = color0.A - ((i)/(float)dx) * (color0.A - color1.A);
    cor.R = R;
    cor.G = G;
    cor.B = B;
    cor.A = A;   
   
       PutPixel(x, y, cor);
    }   
}
}//fim da DrawLine
 
Ao compilar e executar temos o seguinte resultado: 
Rasterização de Triângulos

Feito todos os processos anteriores, rasterizar triângulo passa a ser o mais fácil, onde é só chamar a função DrawLine 3 vezes. Para isso, foi criado a função DrawTriangle, onde ela recebe como parâmetros os 3 pontos do triângulo e suas cores, onde a partir daí será feito as retas que formaram o triângulo.



Por fim, temos o seguinte resultado:


  

Referências

RASTERIZAÇÃO. In: WIKIPÉDIA, a enciclopédia livre. Flórida: Wikimedia Foundation, 2015. Disponível em: http://pt.wikipedia.org/w/index.php?title=Rasteriza%C3%A7%C3%A3o&oldid=35151352. Acesso em: 01 maio 2015.
 
ALGORITMO DE BRESENHAM. In: WIKIPÉDIA, a enciclopédia livre. Flórida: Wikimedia Foundation, 2015. Disponível em: http://pt.wikipedia.org/w/index.php?title=Algoritmo_de_Bresenham&oldid=34646662. Acesso em: 02 maio 2015.
 
LUCIANA MONTERA. INTERPOLAÇÃO LINEAR. Disponível em: http://www.facom.ufms.br/~montera/Interpolacao_alunos.pdf. Acesso em: 02 maio 2015.
 
 

Página inicial