Neste artigo, iremos percorrer o código-fonte de um programa simples que usa OpenGL para entender os conceitos básicos relacionados ao tema, de modo a servir de base para aplicações mais complexas.
Para começar, segue abaixo a função main() de nosso programa. Estaremos explicando cada uma das funções dentro dele a seguir:
int main(int argc, char **argv) { glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB); glutInitWindowSize(350,300); glutInitWindowPosition(10,10); glutInit(&argc,argv); glutCreateWindow("3D"); glutDisplayFunc(Desenha); glutReshapeFunc(AlteraTamanhoJanela); glutMouseFunc(GerenciaMouse); glutKeyboardFunc(GerenciaTeclado); glutSpecialFunc(TeclasEspeciais); Inicializa(); glutMainLoop(); }
glutInitDisplayMode
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.
- GLUT_RGBA
- Mascara de bits para selecionar o modo de janela RGBA. Esse é o padrão se nem GLUT_RGBA ou GLUT_INDEX forem especificados.
- GLUT_RGB
- Um apelido para GLUT_RGBA.
- GLUT_INDEX
- Mascara de bits para selecionar o modo de janela com um índice de cor. Isso sobrecarrega GLUT_RGBA se também for especificada.
- GLUT_SINGLE
- Mascara de bits para selecionar uma janela com buffer simples. Esse é o padrão caso nem GLUT_DOUBLE ou GLUT_SINGLE forem especificados.
- GLUT_DOUBLE
- Mascara de bits para selecionar uma janela com buffer duplo. Sobrecarrega GLUT_SINGLE se também for especificado.
- GLUT_ACCUM
- Mascara de bits para selecionar uma janela com buffer de acumulação.
- GLUT_ALPHA
- Mascara de bits para selecionar uma janela com um componente alpha para o buffer de cores.
- GLUT_DEPTH
- Mascara de bits para selecionar uma janela com buffer de profundidade.
- GLUT_STENCIL
- Mascara de bits para selecionar uma janela com um buffer de estêncil.
- GLUT_MULTISAMPLE
- Mascara de bits para selecionar uma janela com suporte a multi amostragem. Se multi amostragem não estiver disponível, uma janela sem esse recursos será exibida. Nota: tanto implementações do lado cliente quanto do lado servidor do OpenGL devem suportar a extensão GLX_SAMPLE_SGIS para que multi amostragem esteja disponível.
- GLUT_STEREO
- Mascara de bits para selecionar uma janela estéreo.
- GLUT_LUMINANCE
- Mascara de bits para selecionar uma janela com um modelo de cores “luminance”. Esse modelo fornece a funcionalidade do modelo de cor RGBA, mas os componentes verde e azul não são mantidos no buffer. Ao invés disso cada componente vermelho do pixel é convertido em um índice entre zero e glutGet (GLUT_WINDOW_COLORMAP_SIZE)-1 e visto em um mapeamento de cores para determinar a cor do pixel na janela. O mapeamento de cor inicial de GLUT_LUMINANCE é inicializado como cinza linear, mas pode ser modificado com rotinas do GLUT.
glutInitWindowPosition, glutInitWindowSize.
void glutInitWindowSize(int width, int height);
void glutInitWindowPosition(int x, int y);
Atribui a posição inicial e o tamanho da janela criada. Os valores são dados em pixels e servem como uma sugestão ao sistema de janela do SO.
glutInit
void glutInit(int *argcp, char **argv);
Inicializa a biblioteca GLUT e negocia a sessão junto com o sistema de janela. Neste processo, glutInit pode provocar a finalização da aplicação GLUT, enviando uma mensagem de erro ao usuário indicando que não pode ser inicializada apropriadamente.
glutCreateWindow
int glutCreateWindow(char *name);
Cria uma janela e associa o nome passado como parâmetro ao nome da janela. O valor retornado é um identificador (número inteiro) da janela.
glutDisplayFunc(Desenho)
void glutDisplayFunc(void (*func)(void));
func é chamada de função display callback. Quando o GLUT determina que o conteúdo da janela precisa ser redesenhado, display callback é chamada.
Desenho()
Abaixo segue um exemplo de uma função de callback para executar um desenho na tela:
void Desenho(void) { glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); glLoadIdentity(); gluLookAt(0,0,5,0,0,0,0,1,0); glBegin(GL_LINES); glVertex3f(-0.5,-0.5,0.0); glVertex3f(0.5,0.0,0.0); glVertex3f(0.0,0.5,0.0); glutSwapBuffers(); glEnd(); glFlush(); }
glutReshapeFunc(AlteraTamanhoJanela)
estabelece a função “AlteraTamanhoJanela” previamente definida como a função callback de alteração do tamanho da janela. Isto é, sempre que a janela é maximizada, minimizada, etc., a função “AlteraTamanhoJanela” é executada para reinicializar o sistema de coordenadas.
void AlteraTamanhoJanela
Abaixo segue um exemplo da função AlteraTamanhoJanela:
void AlteraTamanhoJanela(GLsizei w, GLsizei h) { // Evita a divisao por zero if(h == 0) h = 1; // Especifica as dimensões da Viewport glViewport(0, 0, w, h); // Inicializa o sistema de coordenadas glMatrixMode(GL_PROJECTION); glLoadIdentity(); // Estabelece a janela de seleção (left, right, bottom, top) if (w <= h) gluOrtho2D (0.0f, 250.0f, 0.0f, 250.0f*h/w); else gluOrtho2D (0.0f, 250.0f*w/h, 0.0f, 250.0f); }
Explicação sobre as funções Desenho() e AlteraTamanhoJanela()
Com apenas algumas primitivas simples, tais como pontos, linhas e polígonos, é possível criar estruturas complexas. Em outras palavras, objetos e cenas criadas com OpenGL consistem em simples primitivas gráficas que podem ser combinadas de várias maneiras. Portanto, OpenGL fornece ferramentas para desenhar pontos, linhas e polígonos, que são formados por um ou mais vértices. Neste caso, é necessário passar uma lista de vértices, o que pode ser feito entre duas chamadas de funções OpenGL:
glBegin()
glEnd()
O argumento passado para glBegin() determina qual objeto será desenhado. No exemplo abaixo, para desenhar três pontos pretos foi usada a seguinte sequência de comandos:
glBegin(GL_POINTS); glColor3f(0.0f, 0.0f, 0.0f); glVertex2i(100, 50); glVertex2i(100, 130); glVertex2i(150, 130); glEnd();
Para desenhar outras primitivas, basta trocar GL_POINTS, que exibe um ponto para cada chamada ao comando glVertex, por:
-
GL_LINES: exibe uma linha a cada dois comandos glVertex;
-
GL_LINE_STRIP: exibe uma seqüência de linhas conectando os pontos definidos por glVertex;
-
GL_LINE_LOOP: exibe uma seqüência de linhas conectando os pontos definidos por glVertex e ao final liga o primeiro como último ponto;
-
GL_POLYGON: exibe um polígono convexo preenchido, definido por uma seqüência de chamadas a glVertex;
-
GL_TRIANGLES: exibe um triângulo preenchido a cada três pontos definidos por glVertex;
-
GL_TRIANGLE_STRIP: exibe uma seqüência de triângulos baseados no trio de vértices v0, v1, v2, depois, v2, v1, v3, depois, v2, v3, v4 e assim por diante;
-
GL_TRIANGLE_FAN: exibe uma seqüência de triângulos conectados baseados no trio de vértices v0, v1, v2, depois, v0, v2, v3, depois, v0, v3, v4 e assim por diante;
-
GL_QUADS: exibe um quadrado preenchido conectando cada quatro pontos definidos por glVertex;
-
GL_QUAD_STRIP: exibe uma seqüência de quadriláteros conectados a cada quatro vértices; primeiro v0, v1, v3, v2, depois, v2, v3, v5, v4, depois, v4, v5, v7, v6, e assim por diante.
A função glVertex2i pertence à biblioteca GL e possui dois argumentos inteiros. De maneira análoga, também é possível passar valores de ponto flutuante no lugar de inteiros, e três coordenadas (x,y,z) no lugar de duas usando, por exemplo, as seguintes chamadas às funções OpenGL:
glVertex2d(100.0, 50.0);
glVertex3f(50.0, 50.0, 50.0);
Além disso, para cada vértice é possível definir uma cor diferente. Neste caso, no desenho final é feita uma “interpolação” das cores.
Antes de descrever os parâmetros e comandos da função “AlteraTamanhoJanela”, é necessário revisar alguns conceitos e especificações. Em quase todos ambientes de janelas, o usuário pode alterar o tamanho e dimensões da janela em qualquer momento. Quando isto ocorre, o conteúdo da janela é redesenhado levando em conta as novas dimensões. Normalmente, o esperado é que a escala do desenho seja alterada de maneira que ele fique dentro da janela, independente do tamanho da janela de visualização ou do desenho. Assim, uma janela pequena terá o desenho completo, mas pequeno, e uma janela grande terá o desenho completo e maior.
Apesar do exemplo mostrar um quadrado 2D, o desenho é feito em um espaço de coordenadas 3D. A função glBegin(GL_QUADS);… glEnd(); desenha o quadrado no plano xy em z=0. Portanto, é necessário determinar o tamanho da viewport (janela onde será feito o desenho) e do volume de visualização (parte do universo da aplicação que será mapeada para viewport), pois estes parâmetros influenciam o espaço de coordenadas e a escala do desenhos 2D e 3D na janela.
Sempre que o tamanho da janela é alterado, a viewport e o volume de visualização devem ser redefinidos de acordo com as novas dimensões da janela. Assim, a aparência do desenho não é alterada (por exemplo, um quadrado não vira um retângulo). Como a alteração do tamanho da janela é detectada e gerenciada de maneira diferente em cada ambiente, a biblioteca GLUT fornece a função glutReshapeFunc, descrita anteriormente, que registra a função callback que a GLUT irá chamar sempre que houver esta alteração. A função passada para a glutReshapeFunc deve ter o seguinte protótipo: void AlteraTamanhoJanela(GLsizei w, GLsizei h);. O nome “AlteraTamanhoJanela” foi escolhido porque descreve o que a função faz. Os parâmetros recebidos sempre que o tamanho da janela é alterado são a sua nova largura e a sua nova altura, respectivamente. Esta informação é usada para modificar o mapeamento do sistema de coordenadas desejado para o sistema de coordenadas da tela com a ajuda de duas funções uma OpenGL, glViewport, e uma da biblioteca GLU, gluOrtho2D. Estas e outras funções chamadas na “AlteraTamanhoJanela”, que definem como a viewport é especificada, são descritas a seguir.
-
glViewport(0, 0, w, h); recebe como parâmetro a nova largura e altura da janela. O protótipo desta função é: void glViewport(GLint x, GLint y, GLsizei width, GLsizei height);. Seus parâmetros especificam o canto inferior esquerdo da viewport (x,y) dentro da janela, e a sua largura e altura em pixels (width e height). Geralmente x e y são zero, mas é possível usar a viewport para visualizar mais de uma cena em diferentes áreas da janela. Em outras palavras, a viewport define a área dentro janela, em coordenadas de tela, que OpenGL pode usar para fazer o desenho. O volume de visualização é, então, mapeado para a nova viewport.
-
gluOrtho2D (0.0f, 250.0f*w/h, 0.0f, 250.0f); é usada para determinar que a projeção ortográfica (2D) será utilizada para exibir na tela a imagem 2D que está na janela de seleção definida através dos parâmetros passados para esta função. O protótipo desta função é: void gluOrtho2D(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top);. No sistema de coordenadas cartesianas, os valores left e right especificam os limites mínimo e máximo no eixo X; analogamente, bottom e top especificam os limites mínimo e máximo no eixo Y.
-
glMatrixMode(GL_PROJECTION); e glLoadIdentity(); servem, respectivamente, para avisar a OpenGL que todas as futuras alterações, tais como operações de escala, rotação e translação, irão afetar a “câmera” (ou observador), e para inicializar o sistema de coordenadas antes da execução de qualquer operação de manipulação de matrizes. Sem este comando, cada chamada sucessiva de gluOrtho2D poderia resultar em uma corrupção do volume de visualização. Em outras palavras, a matriz de projeção é onde o volume de visualização, que neste caso é um plano, é definido; a função gluOrtho2D não estabelece realmente o volume de visualização utilizado para fazer o recorte, apenas modifica o volume existente; ela multiplica a matriz que descreve o volume de visualização corrente pela matriz que descreve o novo volume de visualização, cujas coordenadas são recebidas por parâmetro.
-
glMatrixMode(GL_MODELVIEW); avisa a OpenGL que todas as futuras alterações, tais como operações de escala, rotação e translação, irão afetar os modelos da cena, ou em outras palavras, o que é desenhado. A função glLoadIdentity(); chamada em seguida, faz com que a matriz corrente seja inicializada com a matriz identidade (nenhuma transformação é acumulada).
glutMouseFunc
glutMouseFunc
determina a função de controle para comandos do mouse da janela atual.
Uso
void glutMouseFunc(void (*func)(int button, int state,int x, int y));
- func
- A função de controle de comandos do mouse.
void GerenciaMouse(int button, int state, int x, int y) { if(button==GLUT_LEFT_BUTTON) if(state == GLUT_DOWN) { if(angle >= 10) angle -= 5; } if(button==GLUT_RIGHT_BUTTON) if(state==GLUT_DOWN) { if(angle <= 130) angle += 5; } EspecificaParametrosVisualizacao(); glutPostRedisplay(); }
glutKeyboardFunc
glutKeyboardFunc
Determina a função de manipulação de comandos do teclado da janela atual.
Uso
void glutKeyboardFunc(void (*func)(unsigned char key, int x, int y));
- func
- A função de manipulação de comandos do teclado.
void GerenciaTeclado(unsigned char key, int x, int y) { switch(key) { case 'R': case 'r': glColor3f(1.0f,0.0f,0.0f); break; case 'G': case 'g': glColor3f(0.0f,1.0f,0.0f); break; case 'B': case 'b': glColor3f(0.0f,0.0f,1.0f); break; } glutPostRedisplay(); }
glutSpecialFunc
glutSpecialFunc
Determina a função de manipula comandos das teclas especiais do teclado na janela atual.
Uso
void glutSpecialFunc(void (*func)(int key, int x, int y));
- func
- A função que manipulada os comandos.
- GLUT_KEY_F1
- F1 function key.
- GLUT_KEY_F2
- F2 function key.
- GLUT_KEY_F3
- F3 function key.
- GLUT_KEY_F4
- F4 function key.
- GLUT_KEY_F5
- F5 function key.
- GLUT_KEY_F6
- F6 function key.
- GLUT_KEY_F7
- F7 function key.
- GLUT_KEY_F8
- F8 function key.
- GLUT_KEY_F9
- F9 function key.
- GLUT_KEY_F10
- F10 function key.
- GLUT_KEY_F11
- F11 function key.
- GLUT_KEY_F12
- F12 function key.
- GLUT_KEY_LEFT
- Left directional key.
- GLUT_KEY_UP
- Up directional key.
- GLUT_KEY_RIGHT
- Right directional key.
- GLUT_KEY_DOWN
- Down directional key.
- GLUT_KEY_PAGE_UP
- Page up directional key.
- GLUT_KEY_PAGE_DOWN
- Page down directional key.
- GLUT_KEY_HOME
- Home directional key.
- GLUT_KEY_END
- End directional key.
- GLUT_KEY_INSERT
- Inset directional key.
Note que as teclas ESC, Backspace e Delete são geradas como um caracter ASCII.
void TeclasEspeciais(int key, int x, int y) { if(key == GLUT_KEY_UP) { win -= 20; glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(-win,win,-win,win); } if(key == GLUT_KEY_DOWN) { win += 20; glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(-win,win,-win,win); } glutPostRedisplay(); }
Inicializa()
Nesta função são feitas as inicializações OpenGL que devem ser executadas antes do rendering . Muitos estados OpenGL devem ser determinados somente uma vez e não a cada vez que a função “Desenha” é chamada.
glutMainLoop()
é a função que faz com que comece a execução da “máquina de estados” e processa todas as mensagens específicas do sistema operacional, tais como teclas e botões do mouse pressionados, até que o programa termine.