O propósito desta lição é aprender funções básicas do OpenGL que tratam do preenchimento de regiões. Será mostrado um programa de desenho de polígonos com preenchimento interno com cores sólidas, padrões e combinações de cores. O programa analisado, preenchimento.c, é mostrado no Exemplo 3-1.
Exemplo 3-1. programa preenchimento.c
#include <GL/glut.h> #include <stdlib.h> GLubyte tux[] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7f, 0xfe, 0x0, 0x0, 0xc4, 0x23, 0x0, 0x1, 0x83, 0x21, 0x80, 0x1, 0x7, 0xe0, 0x80, 0x1, 0x7, 0xf0, 0x80, 0x1, 0x8f, 0xf9, 0x80, 0x0, 0xff, 0xff, 0x0, 0x0, 0x4f, 0xf1, 0x0, 0x0, 0x6f, 0xf1, 0x0, 0x0, 0x2f, 0xf3, 0x0, 0x0, 0x27, 0xe2, 0x0, 0x0, 0x30, 0x66, 0x0, 0x0, 0x1b, 0x1c, 0x0, 0x0, 0xb, 0x88, 0x0, 0x0, 0xb, 0x98, 0x0, 0x0, 0x8, 0x18, 0x0, 0x0, 0xa, 0x90, 0x0, 0x0, 0x8, 0x10, 0x0, 0x0, 0xc, 0x30, 0x0, 0x0, 0x6, 0x60, 0x0, 0x0, 0x3, 0xc0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }; GLfloat r,g,b; void init(void); void display(void); void keyboard(unsigned char key, int x, int y); void mouse(int button, int state, int x, int y); int main(int argc, char** argv){ glutInit(&argc, argv); glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB); glutInitWindowSize (256, 256); glutInitWindowPosition (100, 100); glutCreateWindow ("Preenchendo regiões"); init(); glutDisplayFunc(display); glutKeyboardFunc(keyboard); glutMouseFunc(mouse); glutMainLoop(); return 0; } void init(void){ glClearColor(1.0, 1.0, 1.0, 1.0); glOrtho (0, 256, 0, 256, -1 ,1); r=0; g=1; b=0; } void display(void){ int i; glClear(GL_COLOR_BUFFER_BIT); glDisable(GL_POLYGON_STIPPLE); glPolygonMode(GL_BACK, GL_LINE); glColor3f(1.0, 0.0, 0.0); glBegin(GL_POLYGON); glVertex2i(30,226); glVertex2i(113,226); glVertex2i(113,143); glVertex2i(30,143); glEnd(); glPolygonMode(GL_BACK, GL_FILL); glColor3f(r, g, b); glBegin(GL_POLYGON); glVertex2i(143,226); glVertex2i(226,226); glVertex2i(226,143); glVertex2i(143,143); glEnd(); glBegin(GL_POLYGON); glColor3f(1.0, 0.0, 0.0); glVertex2i(30,113); glColor3f(0.0, 1.0, 0.0); glVertex2i(113,113); glColor3f(0.0, 0.0, 1.0); glVertex2i(113,30); glColor3f(1.0, 1.0, 0.0); glVertex2i(30,30); glEnd(); glEnable(GL_POLYGON_STIPPLE); glColor3f(1.0, 0.0, 1.0); glPolygonStipple(tux); glBegin(GL_POLYGON); glVertex2i(143,113); glVertex2i(226,113); glVertex2i(226,30); glVertex2i(143,30); glEnd(); glFlush(); glutSwapBuffers(); } void keyboard(unsigned char key, int x, int y){ switch (key) { case 27: exit(0); break; } } void mouse(int button, int state, int x, int y){ switch (button) { case GLUT_LEFT_BUTTON: if (state == GLUT_DOWN) { r=(GLfloat)rand()/(RAND_MAX+1.0); g=(GLfloat)rand()/(RAND_MAX+1.0); b=(GLfloat)rand()/(RAND_MAX+1.0); glutPostRedisplay(); } break; } } |
Para compilar e executar o programa preenchimento.c, salve-o juntamente com o arquivo Makefile em um diretório e execute a seguinte seqüência de comandos:
$ make preenchimento
$ preenchimento |
A saída do programa preenchimento é mostrado na Figura 3-1.
Serão descritas aqui apenas as partes do programa que acrescentam conceitos novos em relação aos exemplos anteriores.
GLubyte tux[] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7f, 0xfe, 0x0, 0x0, 0xc4, 0x23, 0x0, 0x1, 0x83, 0x21, 0x80, 0x1, 0x7, 0xe0, 0x80, 0x1, 0x7, 0xf0, 0x80, 0x1, 0x8f, 0xf9, 0x80, 0x0, 0xff, 0xff, 0x0, 0x0, 0x4f, 0xf1, 0x0, 0x0, 0x6f, 0xf1, 0x0, 0x0, 0x2f, 0xf3, 0x0, 0x0, 0x27, 0xe2, 0x0, 0x0, 0x30, 0x66, 0x0, 0x0, 0x1b, 0x1c, 0x0, 0x0, 0xb, 0x88, 0x0, 0x0, 0xb, 0x98, 0x0, 0x0, 0x8, 0x18, 0x0, 0x0, 0xa, 0x90, 0x0, 0x0, 0x8, 0x10, 0x0, 0x0, 0xc, 0x30, 0x0, 0x0, 0x6, 0x60, 0x0, 0x0, 0x3, 0xc0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }; |
Define o vetor tux[], do tipo GLubyte, para representar o padrão de preenchimento de polígonos utilizado neste exemplo.
Existem dois métodos principais para preencher regiões utilizando padrões. O mais comum utiliza texturas, mas não será abordado nesta lição. O outro método consiste em definir um mapa de bits monocromático de 32x32 pixels, representando a máscara para o padrão que se deseja desenhar. O padrão utilizado neste exemplo é mostrado na Figura 3-2.
A máscara de desenho (vetor tux[]) é formada por um conjunto de números representados na forma hexadecimal. Para construir este vetor, toma-se cada linha do mapa de bits de baixo para cima. Cada 8 pixels de uma linha da figura equivalem aos bits componentes de um elemento do vetor. Os bits mais significativos ficam à esquerda e os menos significativos à direita. Seguindo esta receita, então a linha 8 da Figura 3-2, representada pela seqüência de bits 00000000110001000010001100000000, equivalerá à seqüência "0x0, 0xc4, 0x23, 0x0" do array tux[].
glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB); |
A função glutInitDisplayMode() avisa GLUT para utilizar dois buffers no desenho de cenas: um principal e outro auxiliar. Todos os objetos deverão desenhados no buffer auxiliar. Quando a função glutSwapBuffers() for chamada, o buffer auxiliar passa a ser o principal, e o principal toma o lugar do auxiliar. Assim, a imagem gerada é apresentada de uma só vez na tela, evitando cintilações e a visualização do processo de desenho, efeitos indesejáveis principalmente em animações.
glutMouseFunc(mouse); |
Define que função GLUT deverá chamar quando ocorrerem eventos de mouse. Quando o usuário pressiona ou solta uma dos botões do mouse, cada pressionamento ou soltura gera uma chamada de mouse. A função de chamada passada como argumento para glutMouseFunc() deve possuir o seguinte protótipo:
void funcao()
(int button, int state, int x, int y);
void display(void){ int i; glClear(GL_COLOR_BUFFER_BIT); glDisable(GL_POLYGON_STIPPLE); |
As funções glDisable() e glEnable() permitem habilitar diversas habilidades do OpenGL. O parâmetro GL_POLYGON_STIPPLE passado para essa função desabilita o desenho de polígonos utilizando padrões de desenho. Quando GL_POLYGON_STIPPLE é habilitado, OpenGL usa o padrão corrente para desenhar.
glPolygonMode(GL_BACK, GL_LINE); glColor3f(1.0, 0.0, 0.0); glBegin(GL_POLYGON); glVertex2i(30,226); glVertex2i(113,226); glVertex2i(113,143); glVertex2i(30,143); glEnd(); |
Neste trecho, a função glPolygonMode() indica que a parte de trás dos polígonos (GL_BACK) será desenhada apenas com a linha de contorno externo (GL_LINE), de cor vermelha, conforme especificado pela função glColor3f(). As funções glBegin()/glEnd() são usadas agora para iniciar o traçado de um polígono (GL_POLYGON) de coordenadas especificadas pela função glVertex2i(). O resultado é o contorno retangular vermelho mostrado na Figura 3-2.
glPolygonMode(GL_BACK, GL_FILL); glColor3f(0.0, 1.0, 0.0); glBegin(GL_POLYGON); glVertex2i(143,226); glVertex2i(226,226); glVertex2i(226,143); glVertex2i(143,143); glEnd(); |
A função glPolygonMode() indica agora que a parte de trás dos polígonos será desenhada apenas com preenchimento sólido (GL_FULL). A cor de desenho agora é (R, G, B) = (0, 1, 0), de modo que o resultado da execução desse trecho de código é o retângulo verde mostrado na Figura 3-2.
glBegin(GL_POLYGON); glColor3f(1.0, 0.0, 0.0); glVertex2i(30,113); glColor3f(0.0, 1.0, 0.0); glVertex2i(113,113); glColor3f(0.0, 0.0, 1.0); glVertex2i(113,30); glColor3f(1.0, 1.0, 0.0); glVertex2i(30,30); glEnd(); |
Este trecho de código demonstra uma característica peculiar de preenchimento. Como cada vértice é desenhado com uma cor diferente, OpenGL interpola estas cores para compor as tonalidades do interior do polígono, gerando um preenchimento bastante colorido.
glEnable(GL_POLYGON_STIPPLE); glColor3f(1.0, 0.0, 1.0); glPolygonStipple(tux); glBegin(GL_POLYGON); glVertex2i(143,113); glVertex2i(226,113); glVertex2i(226,30); glVertex2i(143,30); glEnd(); |
O preenchimento com padrões é agora habilitado pela função glEnable(). A função glColor3f() define magenta, combinação das tonalidades puras vermelho (R=1) e azul (B=1), como a nova cor de desenho. A função glPolygonStipple() define o novo padrão de preenchimento de polígonos, representado pelo vetor tux[]. Em seguida, o par glBegin()/glEnd() desenha o último polígono, preenchindo com o padrão "tux".
glFlush(); |
A função glFlush() faz com que qualquer comando OpenGL ainda não executado seja executado o mais rápido possível pelo mecanismo de exibição. OpenGL freqüentemente executa comandos aos lotes, de modo a tornar mais eficiente o processo de exibição, principalmente quando os programas são executados via rede. Neste caso, quando os comandos executados um a um, o programa pode se tornar ineficiente, considerando as sobrecargas existentes em um barramento de rede. Caso o programa desenvolvido seja destinado ao uso somente local, a função glFlush() torna-se desnecessária. Entretanto, se o programa é feito para funcionar bem tanto localmente quanto em rede, deve ser incluída uma chamada à função glFlush() no final de cada quadro ou cena.
void mouse(int button, int state, int x, int y){ switch (button) { case GLUT_LEFT_BUTTON: if (state == GLUT_DOWN) { r=(GLfloat)rand()/(RAND_MAX+1.0); g=(GLfloat)rand()/(RAND_MAX+1.0); b=(GLfloat)rand()/(RAND_MAX+1.0); glutPostRedisplay(); } break; } } |
A função de tratamento de eventos de mouse verifica se algum botão é pressionado. Caso o botão esquerdo (GLUT_LEFT_BUTTON) seja pressionado (GLUT_DOWN), serão gerados três valores aleatórios para as variáveis r, g e b, na faixa [0,1]. Quando a função glutPostRedisplay() é executada, a função display é chamada novamente, fazendo com que a janela corrente seja redesenhada e o polígono no canto superior esquerdo dessa janela mude de cor.