Lição 1: Minha primeira Janela em OpenGL: Versão GLUT

Muito do código deste tutorial foi escrito originalmente por Jeff Molofee e Fredric Echols e é  uma tradução do OpenGL Tutorial de Jeff Molofee & Neon Helium. Jeff o reescreveu totalmente em Janeiro de 2000. Durante a tradução ao português, a formatação original, com muitas tabelas e figuras mostrando bordas de tabelas para o texto explicativo, foi modificada de forma a facilitar a carga das páginas e a torná-las mais leves.

Muitas partes do código foram adaptadas para utilizarem a API GLUT. Para isso foi muitas vezes utilizado material proveniente do Livro OpenGL SuperBible, que foi programado por Michael Sweet.


O objetivo desta lição é ensiná-lo a criar sua primeira janela em OpenGL  usando técnicas de programação OpenGL e a API de alto nível independente de plataforma GLUT.



Objetivos

O objetivo desta lição é ensiná-lo a criar sua primeira janela em OpenGL e, de lambuja, a compilar um programinha gráfico  em Microsoft C++. A janela que você vai criar pode ter molduras ou ocupar a tela toda, ter qualquer resolução e qualquer profundidade de cores que você queira. O código apresentado aqui foi escrito de forma a poder ser reutilizado em muitas aplicações futuras utilizando OpenGL. A grande maioria dos tutoriais a seguir será baseada no código que vamos ver abaixo.

O código deste tutorial está escrito de uma forma fácil de se entender: Explicamos a parte do código que vai vir e aí vem o código. Em seguida explicamos o que tem de ser programado a seguir e depois apresentmos o código correspondente a essa explicação. Se você copiar somente o código apresentado, você vai poder compilar e sair executando seus programas.

Vamos iniciar o tutorial diretamente escrevendo código. Todos os exemplos daqui para frente são baseados no procedimento de se criar uma aplicação OpenGL com GLUT e Microsoft Visual C++. O código aqui descrito com GLUT é independente de plataforma e pode ser usado sem modificações em Unix/Linux.

Se você quiser ver código para outros sistemas operacionais como BeOS ou outras linguagens que não C,  veja os links no fim dessa página. Para editar programas em Unix/Linux procure pela documentação do XEmacs e do gdb na Internet para saber como usar um bom editor de programas e um bom debugador em Unix.

Criando uma Aplicação em Visual C++

Em Visual C++, inicie invocando o ambiente de desenvolvimento Visual C++ e crie um novo projeto Win32-console (não uma aplicação console/DOS e também não uma aplicação Win32 pura->você não poder usar printf() para debugar). Feito isto, você vai passar para a etapa seguinte que á a da linkagem das bibliotecas do OpenGL. No Visual C++, vá para Project -> Settings -> Link -> "Object/Library Modules". Lá, antes da linha contendo kernel32.lib adicione OpenGL32.lib (ou OpenGL.lib, caso você queira usar a implementação da Silicon Graphics e não a da Microsoft, lembrando de baixar a biblioteca do site da Silicon Graphics (http://reality.sgi.com/) e copiá-la para windows\system.), GLu32.lib e GLaux.lib.

Feito isto, clique em OK. Você está pronto para digitar codigo...



Estrutura geral de um Programa OpenGL

Podemos imaginar um programa OpenGL como dividido em várias seções. Cada seção é representada por um conjunto de funções, invocadas ou do programa principal ou de outra seção.

Seção 1: Declaração de Variáveis Gobais e Inicialização
GL-Includes...
GL-Variaveis...

Seção 2: Inicialização de OpenGL
int InitGL(GLvoid) {....}

Seção 3: Desenhar a Cena OpenGL
void redesenhaMundo(void)     {....}
void mostraMundo(void)        {....}

Seção 4:  Redimensionar a Janela e o Desenho
void redimensionaJanela(int width, int height)  {....}

Seção 5: Controle de Mensagens

void tecla(unsigned char tecla, int x, int y)   {....}
void teclas_de_seta ( int tecla, int x, int y ) {....}


Seção 6: Função Principal em OpenGL

int main(int  argc,    char *argv[]) 
  {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
    glutInitWindowSize(640, 480);
    glutInitWindowPosition(0, 0);
    window = glutCreateWindow("Título da Janela");
    glutReshapeFunc(redimensionaJanela);
    glutDisplayFunc(redesenhaMundo);
    glutKeyboardFunc(tecla);
    glutSpecialFunc(teclas_de_seta);
    glutIdleFunc(mostraMundo);
    InitGL( 640, 480);
    glutMainLoop();
    return (0);
  }




Declaração de Variáveis Gobais e Inicialização

As três primeiras linhas incluem os headerfiles que são responsáveis pelos protótipos das funções contidas nas bibliotecas que você acabou de instruir o linkador para adicionar ao código-objeto.

#include <stdlib.h>                             // incluimos stdlib para compatibilidade com
                                                // microsoft windows. Em Unix esta linha eh 
                                                // sem efeito porque stdlib eh incluido por
                                                // default sempre. Se voce esquecer esta linha
                                                // em windows, seu compilador vai dizer que
                                                // nao conhece a funcao exit()...

#include <stdio.h>                              // Incluimos stdio para podermos usar printf()
                                                // para debugar alguma coisa que queiramos. 
                                                // Mais tarde vamos usar fscanf e fgets para
                                                // ler dados. Se voce esta usando uma versao
                                                // antiga de Borland C++ (ou Turbo C) ou mesmo
                                                // outro compilador C p/windows fora de padrao 
                                                // ANSI, voce provavelmente tera de incluir 
                                                // um headerfile obsoleto chamado conio.h aqui 

#include <GL/glut.h>                            // Aqui sao incluidas TODAS as definicoes 
                                                // para tanto o OpenGL como o GLUT compilarem com
                                                // sucesso em QUALQUER sistema operacional. 
                                                // NUNCA inclua windows.h ou outro headerfile 
                                                // especifico a windows em seus programas, que 
                                                // voce estara produzindo codigo que so compila
                                                // nesta plataforma. O glut.h resolve a independencia
                                                // de plataforma de todo codigo OpenGL de uma forma
                                                // extremamente elegante: a versao de glut.h para 
                                                // cada plataforma inclue os headerfiles especificos
                                                // para aquela plataforma.
                                                
#define ESCAPE 27                               /* ascii code for the escape key */
Na próxima seção de código listaremos as variáveis globais. Nesta altura do campeonato precisamos apenas de três. window vai guardar o handle da janela aberta e Width e Height são variáveis globais que guardam o tamanho atual da viewport (que pode ser a janela, se estivermos em modo janela, ou a tela, se estivermos em modo tela cheia) . São importantes para calcularmos o redimensionamento dos objetos que vamos desenhar, todas vez que a viewport muda de tamanho.
int             window;                         /* The number of our GLUT window */

int             Width;                          /* Width of window */
int             Height;                         /* Height of window */



Inicialização de OpenGL

Na próxima seção de código faremos todas as definições necessárias ao OpenGL. Nós iremos definir qual a cor usada para limpar a tela, qual profundidade de cores terá a janela, habilitar sombreamento suavizado, etc. Esta rotina será chamada até que a janela OpenGL seja criada e retorna um valor, com o qual não vamos nos preocupar no momento, pois a nossa inicialização é simples e nada deverá dar errado.

int InitGL(GLvoid)                                                              // All Setup For OpenGL Goes Here
{
        glShadeModel(GL_SMOOTH);                                                // Habilita sombreamento suavizado
                                                                                // Mistura cores de forma suave me um polígono
A próxima linha define a cor de limpeza da janela. Os valores de cores em OpenGL possuem uma escla de 0.0f a 1.0f, indicando 1.0f o valor mais forte. A função glClearColor possue 4 parâmetros: componente vermelha, componente verde, componente azul e valor alfa.  Em uma aula posterior veremos a teori adas cores e será explicado o significado extao destes valores. Por enquanto setaremos um fundo preto, colocando todos os valores em zero.
        glClearColor(0.0f, 0.0f, 0.0f, 0.0f);                                   // Fundo negro


The next three lines have to do with the Depth Buffer. Think of the depth buffer as layers into the screen. The depth buffer keeps track of how deep objects are into the screen. We won't really be using the depth buffer in this program, but just about every OpenGL program that draws on the screen in 3D will use the depth buffer. It sorts out which object to draw first so that a square you drew behind a circle doesn't end up on top of the circle. The depth buffer is a very important part of OpenGL.
 

        glClearDepth(1.0f);                                                     // Inicialização do Depth Buffer
        glEnable(GL_DEPTH_TEST);                                                // Habilita teste de profundidade
        glDepthFunc(GL_LEQUAL);                                                 // Define qual teste de profundidade vai ser feito
Definimos agora que a perspectiva a ser calculada é a mais bonitinha possível, definida através da constante GL_NICEST. É meio lenta, mas vamos usar mesmo assim.
        glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);                      // Calculos de perspectiva legaizinhos
        return TRUE;                                                            // Fazemos de conta que sempre dá tudo certo retornando OK
}


Desenhar o Mundo OpenGL

Esta função é onde o código de desenho será colocado. Tudo o que você planeja mostrar deve ser chamado de dentro desta rotina, sendo que todos os tutoriais posteriores a este vão apenas adicionar ou modificar código dentro desta função, sempre que se quiser mostrar algo.

int redesenhaMundo(GLvoid)                                       // É aqui que desenhamos TUDO
{
     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);         // Limpe a tela e o buffer
     glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);            // Indique que os dois lados de qualquer superfície devem ser representados.    
     glLoadIdentity();                                           // Resete a corrente Modelview Matrix

     /* eh AQUI que você vai chamar todas as funcoes que 
        desenham algo */

     glutSwapBuffers();                                          // Troque os buffers. Isto ainda não possui nenhum efeito.
                                                                 // Quando você começar a desenhar vai ver que no modo double-buffer, 
                                                                 // você desenha no buffe rescondido e depois mostra o desenho pronto.   

     /* glutSwapBuffers performs a buffer swap on the layer in use for the current window.
        Specifically, glutSwapBuffers promotes the contents of the back
        buffer of the layer in use of the current window to become the contents
        of the front buffer. The contents of the back buffer then become
        undefined. The update typically takes place during the vertical retrace of
        the monitor, rather than immediately after glutSwapBuffers is called.

        An implicit glFlush is done by glutSwapBuffers before it returns.
        Subsequent OpenGL commands can be issued immediately after calling
        glutSwapBuffers, but are not executed until the buffer exchange is
        completed. */
}
/*===========================================================*/
/* Funcao que chama redraw.                                                                      */
/* Endereco passado como a Idle Function, funcao que         */
/* o laco do GLUT chama quando nao esta fazendo outra coisa. */
void 
mostraMundo(void)
/*===========================================================*/
{

        redesenhaMundo();

}


Redimensionar a Janela e o Desenho

A tarefa da próxima seção é a de redimensionar a cena em OpenGL de acordo com as modificações de tamanho sofridas pela janela. Esta rotina será chamada pelo menos uma vez para inicializar a sua perspectiva. A cena será sempre redimensionada de acordo com a largura e altura da janela.

/*===================================================== */
/* redimensionaJanela() - Resize the window...          */
void
redimensionaJanela(int width,  /* I - Width of window   */
                   int height) /* I - Height of window  */
/*===================================================== */
  {
    /* Save the new width and height */
    Width  = width;
    Height = height;

    /* Reset the viewport... */
    glViewport(0, 0, width, height);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();         // Reset The Modelview Matrix

    }


Controle de Mensagens

Aqui as mensagens de janela emitidas quando pressionamos uma tecla enquanto o cursor está sobre a janela OpenGL são tratadas. Temos duas funções: uma que é invocada por GLUT sempre que uma tecla normal (ASCII até 127) é teclada e outra que é invocada sempre que uma tecla especial (teclas de função, setas, page-up, etc) é teclada. GLUT sempre invoca essas funções passando-lhes três parâmetros:


A primeira função abaixo nos vamos regitrar como a função chamada quando apertamos uma tecla comum. A única coisa que essa função vai fazer por enquanto é terminar o programa caso alguém tecle ESCAPE.

/*==========================================================*/
/*      The function called whenever a key is pressed.      */
void 
tecla(unsigned char tecla, int x, int y) 
/*==========================================================*/
{
  switch ( tecla ) {
    case 27:    /* When Escape Is Pressed...                */
                /* shut down our window                     */
                glutDestroyWindow(window); 
                /* exit the program...normal termination.   */
                exit(0); 
                break;          // Ready For Next Case
Quando queremos terminar um programa GLUT precisamos apenas chamar a função glutDestroyWindow que a janela será destruída de uma maneira elagante. A seguir chamamos exit(0) para terminar o programa em condição 0 - sem erros..

    default:                   // Now Wrap It Up
                break;
  }

}

A segunda função será  registrada para ser invocada sempre que uma tecla especial for apertada enquanto o cursor estiver sobre a janela OpenGL. No momento vamos fazer esta função mudar entre os modos tela cheia (fullscreen) e em janela (windowed). Se você der uma olhada no exemplo windows-only, você vai ver que fazer isto sem uma API como o GLUT é extremamente complicado. Com GLUT é muito fácil.

/*==========================================================*/
/*  The function called whenever a special key is pressed.  */
void 
teclas_de_seta ( int tecla, int x, int y )  
/*==========================================================*/
{
  switch ( tecla ) {
    case GLUT_KEY_UP:                   // Quando a seta para cima é teclada...
      glutFullScreen ( );               // Vá para o modo tela cheia...
      break;
    case GLUT_KEY_DOWN:                 // Quando a seta para baixo for teclada...
      glutReshapeWindow ( 640, 480 );   // Vá para modo em janela de 640 por 480
      break;
    default:
      printf("Teclaram: %d\n", tecla);  // ...para ajudar você a debugar...       
      break;
  }
}
As funções glutFullScreen para modo tela cheia e glutReshapeWindow para mudar o tamanho de uma janela (e ir para modo janela caso não se esteja nele) facilitam enormemente a vida do programador porque você não tem de se preocupar com nada, apenas invocá-las e GLUT vai se ocupar, sem perda de performance, de todos os aspectos dependentes de plataforma. As constantes como GLUT_KEY_UP estão definidas em glut.h. Edite o headerfile e veja quais existem.



Função Principal em OpenGL

Aqui é o ponto de entrada da aplicação. Aqui são chamadas as rotinas de criação de janela, tratamento de mensagens e interação humana. A estrutura gerakl de uma função main() para um programa usando a API GLUT será sempre a estrutura que está aí embaixo. Provavelmente você nunca mais mexerá neste programa, por mais complexos que sejam os mundos virtuais ou programas gráficos que você vai fazer usando OpenGL e GLUT.

A código abaixo possui links para as manpages das funções GLUT usadas. Depois que você pegar o jeito da estrutura geral do código, de uma olhada nas manpages simplesmente clicando na função.

Observe duas coisas importantes:

/*==========================================================*/
/*      'main()' - Open a window and display some text.     */
int                /* O - Exit status                       */
main(int  argc,    /* I - Number of command-line arguments  */
     char *argv[]) /* I - Command-line arguments            */
/*==========================================================*/
    {
        /* Initialize GLUT state - glut will take any command line arguments that pertain to it or 
           X Windows - look at its documentation at http://reality.sgi.com/mjk/spec3/spec3.html */  
        glutInit(&argc, argv);
        /*      Select type of Display mode:   
                Double buffer 
                RGBA color
                Alpha components supported 
                Depth buffer */  
        glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
        
        glutInitWindowSize(640, 480); /* get a 640 x 480 window */

        /*      The window starts at the upper left corner of the screen        */
        /*      Aparentemente esta funcao so faz sentido no X-Windows. Em       */
        /*      MS-Windows a janela aparece sempre aa esquerda em cima de       */
        /*      qualquer forma.                                                 */
        glutInitWindowPosition(0, 0);  

        /* Abrimos aqui uma janela, Open a window                               */ 
        /* A variavel window guarda o numero da janela. Pode ser util.          */
        window = glutCreateWindow("ine5341 Computação Gráfica UFSC");

        /*  Register the function called when our window is resized.            */
        glutReshapeFunc(redimensionaJanela);

        /*      Register the function to do all our OpenGL drawing.             */
        glutDisplayFunc(redesenhaMundo);

        /* Register the function called when the keyboard is pressed.           */
        glutKeyboardFunc(tecla);

        /* Resgitramos a funcao que é chamada quando uma tecla especial         */
        /* foi teclada, como setas e teclas de funcao.                          */
        glutSpecialFunc(teclas_de_seta);
                
        /* Funcao chamada sempre que o Loop do GLUT esta ocioso                 */
        /* Esta funcao eh responsavel pelos efeitos de animacao.                */
        glutIdleFunc(mostraMundo);

        /* Chamamos a funcao que realiza o resto das inicializacoes.            */
        /* Esta funcao nos mesmos programamos para concentrar os comandos de    */
        /* inicializacao em um unico lugar e facilitar a nossa vida.            */
        InitGL( 640, 480);

        /* Iniciamos a maquina de processamento de eventos do GLUT.             */  
        glutMainLoop();
        return (0);
    }




Manpages

glutInit

glutInit will initialize the GLUT library and negotiate a session with the window system. During this process, glutInit may cause the
termination of the GLUT program with an error message to the user if GLUT cannot be properly initialized.  Examples of this situation include the failure to connect to the window system, the lack of window system support for OpenGL, and invalid command line options.

glutInit also processes command line options, but the specific options parse are window system dependent.

glutInitWindowPosition

Windows created by glutCreateWindow will be requested to be created with the current initial window position and size.

The initial value of the initial window position GLUT state is -1 and -1. If either the X or Y component to the initial window position is negative, the actual window position is left to the window system to determine. The initial value of the initial window size GLUT state is 300 by 300. The initial window size components must be greater than zero.

The intent of the initial window position and size values is to provide a suggestion to the window system for a window's initial size and
position. The window system is not obligated to use this information. Therefore, GLUT programs should not assume the window was created at the specified size or position. A GLUT program should use the window's reshape callback to determine the true size of the window. If you would like your GLUT program to default to starting at a given screen location and at a given size, but you would also like to let the user override these defaults via a command line argument (such as -geometry for X11), call glutInitWindowSize and glutInitWindowPosition  before your call to glutInit.

glutCreateWindow

glutCreateWindow creates a top-level window. The name will be provided to the window system as the window's name. The
intent is that the window system will label the window with the name. Implicitly, the current window is set to the newly created window.  Each created window has a unique associated OpenGL context. State changes to a window's associated OpenGL context can be done immediately after the window is created.

The display state of a window is initially for the window to be shown. But the window's display state is not actually acted upon until
glutMainLoop is entered. This means until glutMainLoop is called, rendering to a created window is ineffective because the window can not yet be displayed.  The value returned is a unique small integer identifier for the window. The range of allocated identifiers starts at one. This window identifier can be used when calling glutSetWindow.

The proper X Windows Inter-Client Communication Conventions Manual (ICCCM) top-level properties are established. The WM_COMMAND property that lists the command line used to invoke the GLUT program is only established for the first window created.

glutReshapeFunc

glutReshapeFunc sets the reshape callback for the current window. The reshape callback is triggered when a window is reshaped. A reshape callback is also triggered immediately before a window's first display callback after a window is created or whenever an overlay for the window is established. The width and height parameters of the callback specify the new window size in pixels. Before the callback, the current window is set to the window that has been reshaped.

If a reshape callback is not registered for a window or NULL is passed to glutReshapeFunc (to deregister a previously registered callback), the default reshape callback is used. This default callback will simply call glViewport(0,0,width,height) on the normal plane (and on the overlay if one exists).

If an overlay is established for the window, a single reshape callback is generated. It is the callback's responsibility to update both the normal plane and overlay for the window (changing the layer in use as necessary).

When a top-level window is reshaped, subwindows are not reshaped. It is up to the GLUT program to manage the size and positions of subwindows within a top-level window. Still, reshape callbacks will be triggered for subwindows when their size is changed using glutReshapeWindow.

glutDisplayFunc

glutDisplayFunc sets the display callback for the current window. When GLUT determines that the normal plane for the window needs to be redisplayed, the display callback for the window is called. Before the callback, the current window is set to the window needing to be redisplayed and (if no overlay display callback is registered) the layer in use is set to the normal plane. The display callback is called with no parameters. The entire normal plane region should be redisplayed in response to the callback (this includes ancillary buffers if your program depends on their state).

GLUT determines when the display callback should be triggered based on the window's redisplay state. The redisplay state for a window can be either set explicitly by calling glutPostRedisplay or implicitly as the result of window damage reported by the window system. Multiple posted redisplays for a window are coalesced by GLUT to minimize the number of display callbacks called.

When an overlay is established for a window, but there is no overlay display callback registered, the display callback is used for redisplaying both the overlay and normal plane (that is, it will be called if either the redisplay state or overlay redisplay state is set). In this case, the layer in use is not implicitly changed on entry to the display callback.

See glutOverlayDisplayFunc to understand how distinct callbacks for the overlay and normal plane of a window may be established.

When a window is created, no display callback exists for the window. It is the responsibility of the programmer to install a display callback for the window before the window is shown. A display callback must be registered for any window that is shown. If a window becomes displayed without a display callback being registered, a fatal error occurs. Passing NULL to glutDisplayFunc is illegal as of GLUT 3.0; there is no way to ``deregister'' a display callback (though another callback routine can always be registered).

Upon return from the display callback, the normal damaged state of the window (returned by calling glutLayerGet(GLUT_NORMAL_DAMAGED) is cleared. If there is no overlay display callback registered the overlay damaged state of the window (returned by calling glutLayerGet(GLUT_OVERLAY_DAMAGED) is also cleared.

glutKeyboardFunc

glutKeyboardFunc sets the keyboard callback for the current window. When a user types into the window, each key press generating an ASCII character will generate a keyboard callback. The key callback parameter is the generated ASCII character. The state of modifier keys such as Shift cannot be determined directly; their only effect will be on the returned ASCII data. The x and y callback parameters indicate the mouse location in window relative coordinates when the key was pressed. When a new window is created, no keyboard callback is initially registered, and ASCII key strokes in the window are ignored. Passing NULL to glutKeyboardFunc disables the generation of keyboard callbacks.

During a keyboard callback, glutGetModifiers may be called to determine the state of modifier keys when the keystroke generating the callback occurred.

Use glutSpecialFunc for a means to detect non-ASCII key strokes.

glutSpecialFunc

glutSpecialFunc sets the special keyboard callback for the current window. The special keyboard callback is triggered when keyboard
function or directional keys are pressed. The key callback parameter is a GLUT_KEY_* constant for the special key pressed. The x and y callback parameters indicate the mouse in window relative coordinates when the key was pressed. When a new window is created, no special callback is initially registered and special key strokes in the window are ignored. Passing NULL to glutSpecialFunc disables the generation of special callbacks.

During a special callback, glutGetModifiers may be called to determine the state of modifier keys when the keystroke generating the
callback occurred.

glutIdleFunc

glutIdleFunc sets the global idle callback to be func so a GLUT program can perform background processing tasks or continuous
animation when window system events are not being received. If enabled, the idle callback is continuously called when events are not
being received. The callback routine has no parameters. The current window and current menu will not be changed before the idle callback. Programs with multiple windows and/or menus should explicitly set the current window and/or current menu and not rely on its current setting.

The amount of computation and rendering done in an idle callback should be minimized to avoid affecting the program's interactive
response. In general, not more than a single frame of rendering should be done in an idle callback.

Passing NULL to glutIdleFunc disables the generation of the idle callback.

EXAMPLE: A typical idle callback to animate a window might look like:

 void
  idle(void)
  {
    time += 0.05;
    glutSetWindow(window);
    glutPostRedisplay();
  }

Notice how the idle callback does not do any actual drawing; it only advances the time scene state global variable.  That
is left to the window's display callback which will be triggered by the call to glutPostRedisplay.

If you use the idle callback for animation, you should be sure to stop rendering when the window is not visible.  This is easy to set up
with a visibility callback.  For example:

  void
  visible(int vis)
  {
    if (vis == GLUT_VISIBLE)
     glutIdleFunc(idle);
    else
     glutIdleFunc(NULL);
  }

If you do use the idle callback for animation, one thing you should not do is setup the idle callback before calling glutMainLoop.  It is much better to use the visibility callback to install idle callback when the window first becomes visible on the screen.

glutMainLoop

glutMainLoop enters the GLUT event processing loop. This routine should be called at most once in a GLUT program. Once called, this routine will never return. It will call as necessary any callbacks that have been registered.



Links para Código Fonte

Organizado e escrito originalmente por Jeff Molofee (NeHe)
Adapatado para a Língua Portuguesa por Aldo von Wangenheim (UFSC)