O trabalho tem
como objetivo familiarizar o aluno com o conceito de rasterização
de primitivas ( pontos, retas e triângulos) em Computação Gráfica.
Rasterização,
é a tarefa de tomar uma imagem descrita em um imagem
vetorial e
convertê-la em uma imagem raster
(pixels
ou
pontos) para a saída em vídeo ou impressora. Wikipedia.
A atividade proposta era a
de implementação de funções de desenhos de pontos, retas e
triângulos com interpolação linear das cores dos vértices e dos
pontos iniciais e finais das retas. Usando o framework,
disponibilizado pelo Prof. Christian Pagot, que simula o acesso à
memória de vídeo usando o ponteiro FBptr. Pra isso temos as
funções:
Primeiramente, criei três
tipos abstratos de dados: tPixel, tCor, tCoordenasBari. E criei duas
funções para criação de tPixel e tCor.
typedef struct {
unsigned char r;
unsigned char g;
unsigned char b;
unsigned char a;
}tCor;
typedef struct {
int x;
int y;
tCor cor;
}tPixel;
typedef struct {
double v;
double u;
double w;
}tCoordenasBari;
tPixel InitPixel( int x, int y, tCor cor )
{
tPixel p;
p.x = x;
p.y = y;
p.cor = cor;
return p;
}
tCor InitCor( unsigned char r, unsigned char g, unsigned char b, unsigned char a )
{
tCor cor;
cor.r = r;
cor.g = g;
cor.b = b;
cor.a = a;
return cor;
}
Com todos tipos abstratos
criados. Comecei implementando a função PutPixel, pois ela é
essencial para implementação das outras duas. Para que possamos
desenhar um pixel na tela necessitamos desenhar os valores RGBA na
posição na memória de vídeo correspondente àquele pixel. Usando
a fórmula de offset consigo saber o espaço correspondente de
qualquer pixel na memória.
FBptr[4*x + 4*y*IMAGE_WIDTH]
Obs.: IMAGE_WIDTH é a
constante encontrada no arquivo definitions.h do framework que
corresponde a largura da tela.
Cade pixel são 4 bytes
na memória correspondentes ao valores RGBA, ou seja, precisamos
pintar em cada byte o valor de cada componente RGBA do determinado
pixel. Com isso temos:
void PutPixel( tPixel *p )
{
int x = p->x;
int y = p->y;
/* Esse bloco leva o FBptr pra area de memoria de 4bytes correspondente
à posição do pixel passado por parametro e insere os valores RGBA do pixel,
onde 4*x + 4*y*IMAGE_WIDTH é a formula usada para levar o FBptr pro lugar correto na memoria. */
FBptr[4*x + 4*y*IMAGE_WIDTH + 0] = p->cor.r;
FBptr[4*x + 4*y*IMAGE_WIDTH + 1] = p->cor.g;
FBptr[4*x + 4*y*IMAGE_WIDTH + 2] = p->cor.b;
FBptr[4*x + 4*y*IMAGE_WIDTH + 3] = p->cor.a;
}
Porém ainda não estava
pronto. Precisava-se garantir que a função não iria ter
possibilidade de desenhar um pixel fora da tela e possivelmente
acessando uma área restrita da memória. Exemplo de um possível
erro que poderia ocorrer:
Resolvi esse problema com
a inserção da seguinte condição antes de acessar o ponteiro FBptr
. Que avalia se o x ou o y está fora do seguinte intervalo 0 <= x
<= IMAGE_WIDTH e 0 <= y <= IMAGE_HEIGHT.
/*Se o pixel passado por parâmetro estiver na coordenada x-y fora da tela.
Então ele não é pintado */
if( x < 0 || y < 0 || x > IMAGE_WIDTH || y > IMAGE_HEIGHT )
return;
O mesmo exemplo anterior
com a mudança e outro com um pixel dentro dos limites.
 |
| PutPixel( -10, -10, 255,255,255,255) |
 |
| PutPixel (300, 300, 255,255,255,255) |
A próxima função a ser
implementada foi a DrawLine, que certamente foi a mais complicada
devido a necessidade de generalização do algoritmo de Bresenham que
desenha retas em apenas 1 octante.
void DrawLine( tPixel pixelI, tPixel pixelF )
{
/* Algoritmo de Bresenham */
int dx = pixelF.x - pixelI.x;
int dy = pixelF.y - pixelI.y;
int d = 2 * dy - dx;
int incE = 2 * dy;
int incNE = 2 * (dy – dx);
tPixel pixelC = pixelI;
/* Desenhar o primeiro pixel */
PutPixel( &pixelC );
/* Desenhar o resto dos pixels até o pixel final */
while( pixelC.x < pixelF.x )
{
if( d <= 0 )
{
d += incE;
pixelC.x++;
}else{
d += incNE;
pixelC.x++;
pixelC.y++;
}
PutPixe( &pixelC );
}
}
Primeiramente expandi o
Bresenham não só ao primeiro octante e sim também para o quarto.
Facilmente podemos identificar quando a reta se encontra no quarto
octante. Quando o dx ( xFinal – xInicial ) é negativo. Da mesma
forma que identifiquei facilmente o problema também solucionei
rapidamente. Basta apenas inverter os pixels, o inicial passa a ser o
final e o final o inicial. Para facilitar criei a função static
SwapPixel.
static void SwapPixel( tPixel *pixelUm, tPixel *pixelDois )
{
tPixel aux;
aux = *pixelUm;
*pixelUm = *pixelDois;
*pixelDois = aux;
}
Que é chamada quando dx
for negativo, com isso inserimos o trecho a seguir na função
DrawLine após o calculo do dx e do dy.
/* O algoritmo necessita de que o x inicial seja menor que o x final
pois ele vai desenhando da esquerda pra direita.
Então, se x inicial for maior que x final invertemos os pixels. */
if( dx < 0 )
{
SwapPixel( &pixelI, &pixelF );
dx = -dx;
dy = -dy;
}
Com isso já posso
desenhar as seguintes retas.
tPixel p;
tPixel p2;
p = InitPixel( 200, 150, InitCor(255,255,255,255));
p2 = InitPixel( 350, 230, InitCor(255,255,255,255));
DrawLine( p, p2 );
p = InitPixel( 200, 150, InitCor(0,0,255,255));
p2 = InitPixel( 50, 230, InitCor(0,0,255,255));
DrawLine( p, p2 );
Logo após, modifiquei o
algoritmo de forma que desenhasse retas no 5º e 8º octantes. Se
analisarmos as retas desses dois octantes vamos notar que elas ao
invés de ir incrementando a coordenada Y, na verdade está
decrementando. Com isso temos uma solução bem simples. Usei uma
variável que determina como será o deslocamento da coordenada Y no
algoritmo. Se dy for negativo, ou seja, indo de um Y maior para um Y
menor, então mudamos a variável de deslocamento para negativo pra
que ela seja somada a coordenada Y toda vez que o pixel a Nordeste
for escolhido. Com isso inserimos a seguinte condição após a
condição do dx < 0.
/*O algoritmo de Bresenham está implementado para pintar apenas retas que tem o dy positivo.Então, devemos inverter o dy pra positivo quando ele estiver negativo e mudar a maneira que o y ira variar( incrementando ou decrementando ). */
if( dy < 0 )
{
dy = -dy;
deslocaY = -1;
}
E vamos modificar a linha.
pixelC.y++;
Para a seguinte instrução.
pixelC.y += deslocaY;
Como você pode ver, não
tive muita dificuldade para a implementação de Bresenham para
quatro octantes: o primeiro, quarto, quinto e oitavo. Com isso,
temos o seguinte printscreen mostrando as retas nesses octantes.
tPixel p;
tPixel p2;
p = InitPixel( 200, 150, InitCor(255,255,255,255));
p2 = InitPixel( 350, 230, InitCor(255,255,255,255));
DrawLine( p, p2 );
p = InitPixel( 200, 150, InitCor(0,0,255,255));
p2 = InitPixel( 50, 230, InitCor(0,0,255,255));
DrawLine( p, p2 );
p = InitPixel( 200, 150, InitCor(255,0,0,255));
p2 = InitPixel( 350, 70, InitCor(255,0,0,255));
DrawLine( p, p2 );
p = InitPixel( 200, 150, InitCor(0,255,0,255));
p2 = InitPixel( 50, 70, InitCor(0,255,0,255));
DrawLine( p, p2 );
Agora sobrou os outros
quatro octantes que possuem as retas com o coeficiente angular maior
que 1. Ou seja, o dx é menor que o dy. Pensei em fazer com que a
reta passasse a ter o coeficiente menor que 1, com isso basta
inverter os eixos de coordenadas, então o dx passa ser maior que o
dy. Inicialmente eu inseri mais uma condição antes das do dx < 0
e dy < 0 que inverte os eixos caso o dx seja menor que o dy e
muda a variável de avaliação swapEixos para 1, para que o
algoritmo possa saber que os eixos foram trocados e na hora de
desenhar o pixel apenas destrocar.
Função SwapEixos:
static void SwapEixos( tPixel *pixel )
{
int aux;
aux = pixel->x;
pixel->x = pixel->y;
pixel->y = aux;
}
Primeira implementação:
/* Variavel de certificação para poder inverter novamente os eixos antes de pinta o pixel caso dx < dy */
int swapEixos = 0;
/* Primeiro vamos verificar se a linha tera o coeficiente angular maior que 1.
Já prevendo possiveis modificações nas condições posteriores.
Se tiver, vamos inverter as coordenadas */
if( dx < dy )
{
SwapEixos( &pixelF );
SwapEixos( &pixelI );
int aux = dy;
dy = dx;
dx = aux;
swapEixos = 1;
}
E antes de desenhar os
pixels fazemos a destroca. Basta trocar o seguinte trecho em todas as
ocorrências do PutPixel( &pixelC );
if( swapEixos )
{
SwapEixos( &pixelC );
PutPixel( &pixelC );
SwapEixos( &pixelC );
}else
PutPixel( &pixelC );
Já conseguimos desenhar
retas no segundo octante e no terceiro como podemos ver na imagem
abaixo, podemos ver também que a implementação do jeito que está
não abrange o resto dos 2 octantes que faltam.
tPixel p;
tPixel p2;
p = InitPixel( 200, 300, InitCor(255,255,255,255));
p2 = InitPixel( 50, 500, InitCor(255,255,255,255));
DrawLine( p, p2 );
p = InitPixel( 200, 300, InitCor(255,255,255,255));
p2 = InitPixel( 350, 500, InitCor(255,255,255,255));
DrawLine( p, p2 );
p = InitPixel( 200, 300, InitCor(0,0,255,255));
p2 = InitPixel( 50, 100, InitCor(0,0,255,255));
DrawLine( p, p2 );
p = InitPixel( 200, 300, InitCor(0,0,255,255));
p2 = InitPixel( 350, 100, InitCor(0,0,255,255));
DrawLine( p, p2 );
Tentativa ao desenhar
novamente o quarto octante, note que o pixel final está na posição
10 na coordenada x.
tPixel p;
tPixel p2;
p = InitPixel( 200, 150, InitCor(0,0,255,255));
p2 = InitPixel( 10, 230, InitCor(0,0,255,255));
DrawLine( p, p2 );
A implementação do
jeito que está não funciona pros demais octantes e ainda acaba
fazendo com que não se desenhe mais corretamente retas no quarto
octante, pois temos que “prever” que depois dos testes de dx <
0 e dy < 0 a reta pode passar a se encaixar no dx < dy e depois
de passar pela modificação dos eixos ele ainda pode entrar nas
condições de dx < 0 e dy < 0. Então bastamos “prever”
que isso ocorrerá. A solução encontrada foi fazer a avaliação de
dx < dy usando seus valores absolutos. Portanto, agora temos o
algoritmo de desenhar linhas que abrange todos os octantes.
if( fabs(dx) < fabs(dy) )
fabs() - Função que
retorna o valor absoluto de qualquer inteiro e se encontra na
biblioteca padrão math.h.

tPixel p;
tPixel p2;
p = InitPixel( 200, 300, InitCor(255,255,255,255));
p2 = InitPixel( 50, 500, InitCor(255,255,255,255));
DrawLine( p, p2 );
p = InitPixel( 200, 300, InitCor(255,255,255,255));
p2 = InitPixel( 350, 500, InitCor(255,255,255,255));
DrawLine( p, p2 );
p = InitPixel( 200, 300, InitCor(0,0,255,255));
p2 = InitPixel( 50, 100, InitCor(0,0,255,255));
DrawLine( p, p2 );
p = InitPixel( 200, 300, InitCor(0,0,255,255));
p2 = InitPixel( 350, 100, InitCor(0,0,255,255));
DrawLine( p, p2 );
p = InitPixel( 200, 300, InitCor(255,255,255,255));
p2 = InitPixel( 350, 380, InitCor(255,255,255,255));
DrawLine( p, p2 );
p = InitPixel( 200, 300, InitCor(0,0,255,255));
p2 = InitPixel( 50, 380, InitCor(0,0,255,255));
DrawLine( p, p2 );
p = InitPixel( 200, 300, InitCor(255,0,0,255));
p2 = InitPixel( 350, 220, InitCor(255,0,0,255));
DrawLine( p, p2 );
p = InitPixel( 200, 300, InitCor(0,255,0,255));
p2 = InitPixel( 50, 220, InitCor(0,255,0,255));
DrawLine( p, p2 );
p = InitPixel( 200, 300, InitCor(0,255,0,255));
p2 = InitPixel( 200, 100, InitCor(0,255,0,255));
DrawLine( p, p2 );
p = InitPixel( 200, 300, InitCor(255,0,0,255));
p2 = InitPixel( 200, 500, InitCor(255,0,0,255));
DrawLine( p, p2 );
p = InitPixel( 200, 300, InitCor(0,255,0,255));
p2 = InitPixel( 50, 300, InitCor(0,255,0,255));
DrawLine( p, p2 );
p = InitPixel( 200, 300, InitCor(255,0,0,255));
p2 = InitPixel( 350, 300, InitCor(255,0,0,255));
DrawLine( p, p2 );
A função DrawTriangle
tem sua implementação bastante simples precisando apenas desenhar 3
retas usando os 3 vértices passados por parâmetro. Então temos ela
da seguinte maneira.
void DrawTriangle( tPixel pUm, tPixel pDois, tPixel pTres )
{
/* Ligamos tres retas aos vertices do triangulo. Pronto */
DrawLine( pUm, pDois );
DrawLine( pDois, pTres );
DrawLine( pTres, pUm );
}
tPixel p;
tPixel p2;
tPixel p3;
p = InitPixel( 150, 150, InitCor(0,0,255,255));
p2 = InitPixel( 150, 500, InitCor(0,0,255,255));
p3 = InitPixel( 500, 500, InitCor(0,0,255,255));
DrawTriangle( p, p2, p3 );
Até agora, já posso
desenhar pontos, retas e triângulos. Agora precisa-se 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.
Com isso em mente
calculei a variação de cada componente subtraindo o final pelo
inicial e dividindo pela quantidade de pixels, que se você pensar
bem é o mesmo valor do dx, pois o algoritmo desenha o próximo pixel
sempre a leste ou nordeste do anterior e no caso do coeficiente ser
maior que 1 o eixo é modificado e o dx continua sendo o número de
pixels da reta. Logo após, calculo a cor de cada pixel multiplicando
o número de pixels já desenhados e a variação somando com a cor
do primeiro pixel. Resumindo temos os seguintes trechos.
Antes do while.
/* Calculo de variação de cor por pixel */
double variacaoR = ((double)pixelF.cor.r - pixelI.cor.r)/dx;
double variacaoG = ((double)pixelF.cor.g - pixelI.cor.g)/dx;
double variacaoB = ((double)pixelF.cor.b - pixelI.cor.b)/dx;
double variacaoA = ((double)pixelF.cor.a – pixelI.cor.a)/dx;
/* Número de pixels pintados até então */
int nPixelPintados = 1;
E antes de desenhar o
pixel calculamos a cor correspondente a ele.
Antes do PutPixel dentro
do while.
/* A mágica da interpolação acontece aqui
Onde cada pixel vai ter uma o número de pixels ja pintados vezes a variação + o número da cor do primeiro pixel*/
pixelC.cor.r = (nPixelPintados * (char)variacaoR) + pixelI.cor.r;
pixelC.cor.g = (nPixelPintados * (char)variacaoG) + pixelI.cor.g;
pixelC.cor.b = (nPixelPintados * (char)variacaoB) + pixelI.cor.b;
pixelC.cor.a = (nPixelPintados * (char)variacaoA) + pixelI.cor.a;
Após o PutPixel
incrementamos o nPixelPintados.
/* Pintou mais um, então incrementamos */
nPixelPintados++;
Essa implementação
inicial tinha um pequeno erro, o fato de estar acontecendo a
trucagem do valor flutuante antes de fazer todos os cálculos fazem
com que retas muitos grandes, mais precisamente, as com dx maior que
255, número máximo que se pode conseguir com a subtração da cor
final pela inicial, fazendo com que a variação seja menor que 1 e a
trucagem faz com que a cor não varie nunca.
Exemplo:
tPixel p;
tPixel p2;
p = InitPixel( 10, 10, InitCor(0,0,255,255));
p2 = InitPixel( 300, 10, InitCor(255,0,0,255));
DrawLine( p, p2 );
Facilmente resolvido
tirando a trucagem para o fim do cálculo com os valores de ponto
flutuante. E acrescentando a soma com 0.5 para arredondar o valor
antes da trucagem.
pixelC.cor.r = (char) (nPixelPintados * variacaoR + 0.5 ) + pixelI.cor.r;
pixelC.cor.g = (char) (nPixelPintados * variacaoG + 0.5 ) + pixelI.cor.g;
pixelC.cor.b = (char) (nPixelPintados * variacaoB + 0.5 ) + pixelI.cor.b;
pixelC.cor.a = (char) (nPixelPintados * variacaoA + 0.5 ) + pixelI.cor.a;
Está concluído o que foi
proposto.
tPixel p;
tPixel p2;
p = InitPixel( 256, 256, InitCor(255,0,0,255));
p2 = InitPixel( 500, 512, InitCor(0,0,255,255));
DrawLine( p, p2 );
p = InitPixel( 256, 256, InitCor(255,0,0,255));
p2 = InitPixel( 12, 512, InitCor(255,255,0,255));
DrawLine( p, p2 );
p = InitPixel( 256, 256, InitCor(255,0,0,255));
p2 = InitPixel( 0, 256, InitCor(0,255,0,255));
DrawLine( p, p2 );
p = InitPixel( 256, 256, InitCor(255,0,0,255));
p2 = InitPixel( 512, 256, InitCor(0,255,255,255));
DrawLine( p, p2 );
p = InitPixel( 256, 256, InitCor(255,0,0,255));
p2 = InitPixel( 12, 0, InitCor(255,0,255,255));
DrawLine( p, p2 );
p = InitPixel( 256, 256, InitCor(255,0,0,255));
p2 = InitPixel( 500, 0, InitCor(0,0,255,255));
DrawLine( p, p2 );
p = InitPixel( 256, 256, InitCor(255,0,0,255));
p2 = InitPixel( 256, 0, InitCor(0,160,255,255));
DrawLine( p, p2 );
p = InitPixel( 256, 256, InitCor(255,0,0,255));
p2 = InitPixel( 256, 512, InitCor(0,255,0,255));
DrawLine( p, p2 );
p = InitPixel( 256, 256, InitCor(255,0,0,255));
p2 = InitPixel( 512, 350, InitCor(30,160,200,255));
DrawLine( p, p2 );
p = InitPixel( 256, 256, InitCor(255,0,0,255));
p2 = InitPixel( 0, 350, InitCor(0,0,0,255));
DrawLine( p, p2 );
p = InitPixel( 256, 256, InitCor(255,0,0,255));
p2 = InitPixel( 512, 152, InitCor(0,255,0,255));
DrawLine( p, p2 );
p = InitPixel( 256, 256, InitCor(255,0,0,255));
p2 = InitPixel( 0, 152, InitCor(255,255,255,255));
DrawLine( p, p2 );
tPixel p;
tPixel p2;
tPixel p3;
p = InitPixel( 150, 150, InitCor(0,0,255,255));
p2 = InitPixel( 150, 500, InitCor(255,0,0,255));
p3 = InitPixel( 500, 500, InitCor(0,255,0,255));
DrawTriangle( p, p2, p3 );
Como terminei tudo que foi
proposto em tempo hábil. Aproveitei para implementar alguns extras.
São as funções de desenhar triângulos preenchidos usando também
interpolação de cores e desenhar quadriláteros com preenchimento
interpolado de cores. Temos as seguintes funções:
DrawFillTriangle -
Desenhar triângulos preenchidos
DrawQuadrangle –
Desenhar quadriláteros.
DrawFillQuadrangle –
Desenhar quadriláteros preenchidos.
O meu maior desafio foi a
implementação de uma função que desenhasse triângulos
preenchidos.
Obs.: Perdi o PrintScreen com o triângulo desenhado incorretamente.
Depois de várias
frustrações com alguns algoritmos errados e nada eficientes, achei
a solução em um documento que falava sobre Geometria de Triângulos.
Com auxilio desse material preenchi triângulos usando as coordenadas
baricêntricas do triângulo. São três coordenadas que expressão a
distância do ponto em relação a cada vértice que somadas é igual
a 1, com isso consegui saber se um determinado ponto se encontra
dentro do triângulo. Basta todas as coordenadas serem positivas.
Então a função percorre todos os pixels do menor quadrado que pode
cobrir todo o triângulo, e vai fazendo o teste se determinado ponto
está dentro do triângulo, se sim, basta desenha-lo com a cor sendo
a soma das coordenadas baricêntricas multiplicadas pelas componentes
do vertice correspondente a ela. Não irei postar a implementação
aqui, será visível apenas no código-fonte disponibilizado para
download após o fim do prazo de entrega.
Seguindo as seguintes
linhas de passos temos o algoritmo para preencher qualquer triângulo.
Desenhar um triângulo
simples usando o DrawTriangle
Guardas as dimensões
do menor quadrado que abrange o triângulo.
Percorrer todos os
pontos do quadrado.
Verifica se o ponto
pertence ao triângulo, se todas as coordenadas baricêntricas
forem positivas.
Se sim, calcula
cada componente RGBA do ponto somando as coordenadas baricêntricas
multiplicadas cada uma pela componente do vertice correspondente.
Desenha em seguida.
Parte do código
responsavel pela interpolação do triângulo preenchido. Para
facilitar o entendimento. Onde pUm, pDois e pTres são as pontas do triângulo e coordenadas é do tipo tCoordenasBari.
p.cor.r = (char)((coordenadas.v * pUm.cor.r + coordenadas.u * pDois.cor.r + coordenadas.w * pTres.cor.r) + 0.5);
p.cor.g = (char)((coordenadas.v * pUm.cor.g + coordenadas.u * pDois.cor.g + coordenadas.w * pTres.cor.g) + 0.5);
p.cor.b = (char)((coordenadas.v * pUm.cor.b + coordenadas.u * pDois.cor.b + coordenadas.w * pTres.cor.b) + 0.5);
p.cor.a = (char)((coordenadas.v * pUm.cor.a + coordenadas.u * pDois.cor.a + coordenadas.w * pTres.cor.a) + 0.5);
tPixel p;
tPixel p2;
tPixel p3;
p = InitPixel( 150, 150, InitCor(0,0,255,255));
p2 = InitPixel( 150, 500, InitCor(255,0,0,255));
p3 = InitPixel( 500, 500, InitCor(0,255,0,255));
DrawFillTriangle( p, p2, p3 );
Para desenhar
quadriláteros basta ligar quatro retas com os vértices do mesmo,
mas de forma que nenhuma reta cruze a outra.
Seguindo o algoritmo:
tPixel p;
tPixel p2;
tPixel p3;
tPixel p4;
p = InitPixel( 150, 150, InitCor(0,0,255,255));
p2 = InitPixel( 50, 500, InitCor(255,0,0,255));
p3 = InitPixel( 400, 500, InitCor(0,255,0,255));
p4 = InitPixel( 500, 150, InitCor(255,255,0,255));
DrawQuadrangle( p, p2, p3, p4 );
Para a função
DrawFillQuadrangle, necessitamos apenas dividir o quadriláteros em
dois, formando dois triângulos e usar a função DrawFillTriangle
para desenha-los.
Algoritmo:
Ordenar os pontos de
forma que eles não se cruzem.
Escolher dois pontos
de forma que divide o quadrilátero em dois.
Chama a função
DrawFillTriangle para desenhar os dois triângulos.
tPixel p;
tPixel p2;
tPixel p3;
tPixel p4;
p = InitPixel( 150, 150, InitCor(0,0,255,255));
p2 = InitPixel( 50, 500, InitCor(255,0,0,255));
p3 = InitPixel( 400, 500, InitCor(0,255,0,255));
p4 = InitPixel( 500, 150, InitCor(255,255,0,255));
DrawFillQuadrangle( p, p2, p3, p4 );
Possíveis melhoras
Acredito que poderia
aumentar a eficiência simplificando algumas instruções no desenho
de retas e na interpolação de cores, trabalhar mais no
preenchimento de triângulos, melhorando também a sua eficiência.
Resumindo, as possíveis melhoras seriam na eficiência.
Dificuldades encontradas
Sem duvida,
as maiores dificuldades encontradas foi a generalização o algoritmo
de Bresenham para desenhar retas com o coeficiente angular maior do
que 1 e o preenchimento de triângulos.
Referência bibliográficas
-
Livro
do Foley, seção 3.2: "Scan Converting Lines"
Geometria
dos Triângulos –
http://inf.unioeste.br/~rogerio/Geometria-Triangulos.pdf