Rasterização de Primitivas em C++ (Algorítmo de Bresenham)

Rasterização de Primitivas - Pontos, Retas e Triângulos


Descrição do problema:
  
Este post é destinado ao primeiro trabalho da disciplina de Introdução à Computação Gráfica, ministrada pelo professor Christian Azambuja Pagot, o desenvolvedor do framework, que simula o acesso a memória (através do ponteiro FBptr), já que os sistemas operacionais não liberam este acesso.

Bem, neste trabalho foi solicitado a criação de três funções, listada abaixo:

PutPixel: Função que rasteriza um pixel na tela.
DrawLine: Função que rasteriza um conjunto de pixel na tela, formando linhas.
DrawTriangle: Função que rasteriza três linhas, formando triângulos.
Como se deu a implementação?
Primeiro de tudo, precisamos saber alguns conceito básico para melhor entendemos a implementaçã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. O termo Rasterização também é utilizado para converter uma imagem formada por vetores para um arquivo de formato bitmap (SVG para PNG).



2- O que é Pixel? 

Pixel ou Píxel (sendo o plural pixels ou píxeis) (aglutinação de Picture e Element, ou seja, elemento de imagem, sendo Pix a abreviatura em inglês para Pictures) é o menor elemento num dispositivo de exibição (como por exemplo um monitor), ao qual é possível atribuir-se uma cor. De uma forma mais simples, um pixel é o menor ponto que forma uma imagem digital, sendo que o conjunto de milhares de pixels formam a imagem inteira.

3- O que são primitivas geométrica? 
São conceitos primitivos (e, portanto, aceitos sem definição) na Geometria espacial os conceitos de ponto, reta e plano. Neste trabalho precisaremos saber a característica de uma a reta, que é só possui uma dimensão, comprimento. E a característica do ponto, que é não possuir dimensão.
Depois de explicamos esse conceito vamos ao que realmente interesa, a implementação, mãos a massa.

Pintando o Pixel na tela
Para isso iremos criar uma função chamada de putpixel que terá como argumento x e y, ou seja, vamos  converter a localização de um determinado ponto (descrito por coordenadas cartesianas discretas) em um espaço de memória, e escrever suas informações de cor neste espaço.

Considerando que a tela possui w pixels de largura e h pixels de altura, e que a origem do sistema de coordenadas esteja localizada no canto superior esquerdo da tela, como pode ser visto abaixo.

1- O que é Rasterização?



Representação da tela

Endereçamento do RGBA

Os pixels são endereçados como pontos de coordenadas (x, y), onde x varia entre 0 e w-1, e y varia entre 0 e h-1. Já  no color buffer, o armazenamento é em série, a primeira posição corresponde ao primeiro componente do primeiro pixel, como poderá ser visto na imagem abaixo.

O primeiro byte do pixel (0,2) é 4*w*2+0. Com esse entendimento e generalizando, chegamos a fórmula para endereçamento de memória do 1º byte para pixel qualquer (x,y).

Como vamos pintar um pixels na tela?
Rasterização de Pontos O acesso aos pixels na região do framebuffer é realizado por meio do ponteiro FBptr, que está apontado inicialmente para a sua primeira posição de memória como vimos acima, ou seja, para o pixel de coordenadas (0,0) - canto superior esquerdo da tela. O framebuffer consiste em uma matriz unidimensional, com tamanho relativo a dimensão da tela. Com isso, para acessar o pixel que deseja-se pintar, o ponteiro FBptr move-se na direção horizontal, passando por um determinado número de bytes, até chegar ao pixel desejado. 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 (1 byte para cada componente). Assim, para acessarmos um pixel de coordenadas (x,y), em uma tela de dimensões w*h (largura*altura), usa-se a fórmula:

4*w*y + 4*x = 4*(w*y + x)


Antes de apresentar como se deu a implementação, foi apropriado fazer a modelagem dos pixels e das cores, isto é, agrupá-los em estruturas de forma que sejam identificáveis ao longo do programa como objetos, Assim, foram criadas as seguintes estruturas(Classes):






A seguir veremos a função PUTPIXEL que tem como argumentos coordenadas(X,Y) e Cor(RGBA) e a mesma é básica para qualquer algoritmo de rasterização:


Representação do pixel


Draw Line

Desenhando linhas fazendo uso da função  PutPixel().

Conectando dois pixels: o algoritmo de Bresenham, esse é  algoritmo mais clássico para desenhar linhas. Apesar de não ser fácil implementação, mas tem uma grande eficiência, pois não emprega nenhuma operação de divisão ou arredondamento, funções associadas diretamente à processos de discretização.
A função tem como objetivo desenhar um segmento de reta na tela, contudo seu maior desafio é identificar quais pixels devem ser pintados para que a reta obtenha a melhor representação.
Seu funcionamento ocorre da seguinte maneira: a cada iteração, a função recebe a duas coordenas e  escolhe entre dois pontos, que são o final e inicial e com base no sinal da variável de decisão calculada na iteração anterior, na sequência a variável de decisão é incrementada de acordo com o valor do pixel escolhido. Inicialmente, o valor da variável de decisão é D = 2dy – dx. Onde dx = x final – x inicial e dy = y final – y inicial. Se D<=0, o pixel escolhido está acima da reta, caso contrário está abaixo da reta. Se o pixel escolhido estiver acima da reta, o valor da variável de decisão será incrementado com o erro da aproximação E = 2dy; caso contrário o erro será SE= 2*dy – dx. O procedimento se repete até desenhar toda a reta:




Comportamento Δx e o Δy
Agora, finalmente, depois de analisamos todos os casos iremos ao algoritmo, vale ressaltar que a implementação desse algoritmo em termos de código é "simples"mas compreender completamente como fazê-la, principalmente a parte de generalizá-lo.

A primeira coisa que eu implementei foram linhas que tinha alguma coordenadas contantes ou seja desenhar foram linhas verticais e horizontais. Por isso foi criado uma função

No caso das linhas horizontais, em que o delta Δy (dy) é zero. Portanto não há a necessidade de se incrementar y. Analogamente temos as  linhas verticais o Δx (dx) é igual a zero , então logo não  precisamos incrementar x. E assim foi criada a função  DrawLineConst(Coordenadas *Pinicio, Coordenadas *Pfinal, Cor *cor)






Agora iremos mostrar de fato a função DrawLine(Coordenadas *Pinicio, Coordenadas *Pfinal, Cor *cor)





DrawTriangle

Desenhando triângulos fazendo uso da função DrawLine().

Agora, precisamos fazer uma função que recebe três retas(Coordenadas) a serem vértices do triângulo. Ela recebe como parâmetro a posição dos três vértices e a cor do triângulo e funciona da seguinte maneira: O primeiro vértice é ligado ao segundo através de uma chamada da função DrawLine(), o segundo vértice ligado ao terceiro e o terceiro vértice é ligado ao primeiro vértice.
E assim foi criada a função DrawTriangle(Coordenadas *coordenadasA, Coordenadas *coordenadasB, Coordenadas *coordenadasC, Cor *cor ).




Interpolação de Cores RGBA

Agora precisamos implementar a interpolação de cores, variar as cores gradativamente até chegar na cor da outra ponta, que preferi deixar pro final pra facilitar o raciocínio. A interpolação segue uma variação de cada componente RGBA até chegar a cor RGBA do final a partir de uma cor RGBA inicial. Para isso foi criado 2 funções, a primeira foi função distancia, que ira calcular  a distancia  total da reta. distancia(Coordenadas *Pinicio, Coordenadas *Pfinal);



Função interpolação de cores(IC) tem como parâmetro a distancia entre os pontos e suas respectivas cores. O argumento float P é calculo da distância entre os pontos, para poder ser utilizado como divisor para assim descobrir o valor de incremento de cada componente RGBA, obtido através da divisão da subtração do componente de cor final pelo inicial pela distância. e colocaremos o trecho de código sobe todas as chamada de função Putpixel.






 Dificuldades encontradas no Trabalho
A maior dificuldade enfrentada nesse trabalho foi em relação a interpolar as cores.

 Possíveis melhoras
"Limpar o código", tendo em vista que código é didático, foi criado variáveis sem utilidade efetiva.

 Referências 
PAGOT, Christian Azambuja. Material de aula . 30 mar. 2015, 11 may. 2015. Notas de Aula.

FLANAGAN, Colin. 
The Bresenham Line-Drawing Algorithm. 2015. Disponível em: http://www.cs.helsinki.fi/group/goa/mallinnus/lines/bresenh.html. Acesso em: 30/04/2015
ELETRONICA, saber. 
Algoritmo de Bresenham: O Uso de Microcontroladores para Traçar Retas em LCDs. 2008. Disponível em: http://www.sabereletronica.com.br/artigos/2605-algoritmo-de-bresenham-o-uso-de-microcontroladores-para-traar-retas-em-lcd. Acesso em: 06/04/2015

BAGATELO, Harlen Costa. 
Algoritmos de Rasterização.
 2008. Disponível em: http://www.dca.fee.unicamp.br/courses/IA725/1s2008/transparencias/raster.pdf. Acesso em: 06/04/2015





Tags:  Rasterização de Primitivas em C++,  Algorítmo de Bresenham, Computação Gráfica, Graphic Computer ,CG, Programação, programming, colorbuffer,  framebuffer, Opengl, Pipeline Gráfico, C++, T1, Rasterização de Primitivas em C++ (Algorítmo de Bresenham), Primeiro trabalho de computação gráfica - UFPB, Rasterização de Primitivas.

Comentários