Tutorial de SDL – Parte 50 – SDL e OpenGL 2

Continuando nossa série de artigos traduzidos do site lazyfoo, veremos agora como combinar SDL com OpenGL.
Um dos recursos mais  poderosos do SDL é a habilidade de combinar-se com o OpenGL. Aqui vamos criar um exemplo básico com OpenGL usando o modo legado do OpenGL.

//Using SDL, SDL OpenGL, standard IO, and, strings
#include
#include
#include <GL\GLU.h>
#include
#include

Para usar o OpenGL e o SDL, certifique-se de incluir o cabeçalho SDL OpenGL. Também vamos estar usando o GLU (Utilitários do OPenGL) no nosso exemplo.

Também certifique-se de verificar o arquivo readme.txt para ver quais bibliotecas você precisa conectar ao programa.

//Starts up SDL, creates window, and initializes OpenGL
bool init();
//Initializes matrices and clear color
bool initGL();
//Input handler
void handleKeys( unsigned char key, int x, int y );
//Per frame update
void update();
//Renders quad to the screen
void render();
//Frees media and shuts down SDL
void close();
//The window we'll be rendering to
SDL_Window* gWindow = NULL;
//OpenGL context
SDL_GLContext gContext;
//Render flag
bool gRenderQuad = true;

Em termos de novas funções, temos uma função de inicialização dedicada ao OpenGL. Também temos manipuladores de teclas próprios, aleḿ de funções para atualização e renderização para o loop principal.

Também temos um contexto GL. Um contexto OPenGL é somente algo que lida com chamadas OpenGL e você precisa ter para qualquer renderização do OpenGL. Finalmente, temos um flag booleano que alterna a renderização.

bool init()
{
    //Initialization flag
    bool success = true;
    //Initialize SDL
    if( SDL_Init( SDL_INIT_VIDEO ) < 0 )
    {
        printf( "SDL could not initialize! SDL Error: %s\n", SDL_GetError() );
        success = false;
    }
    else
    {
        //Use OpenGL 2.1
        SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, 2 );
        SDL_GL_SetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, 1 );
        //Create window
        gWindow = SDL_CreateWindow( "SDL Tutorial", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN );
        if( gWindow == NULL )
        {
            printf( "Window could not be created! SDL Error: %s\n", SDL_GetError() );
            success = false;
        }

Ao criar uma janela SDL OpenGL, alguns passos  precisam ser seguidos. Antes de criar a janela, precisamos especificar a versão que queremos.  Queremos OpenGL 2.1 assim usamos SDL-GL_SetAttribute para configurar a versão principal para 2 e versão secundária para 1. Depois que a versão foi configurada, crie uma janela OpenGL  passando a flag SDL_WINDOW_OPENGL para SDL_CreateWindow.

        else
        {
            //Create context
            gContext = SDL_GL_CreateContext( gWindow );
            if( gContext == NULL )
            {
                printf( "OpenGL context could not be created! SDL Error: %s\n", SDL_GetError() );
                success = false;
            }
            else
            {
                //Use Vsync
                if( SDL_GL_SetSwapInterval( 1 ) < 0 )
                {
                    printf( "Warning: Unable to set VSync! SDL Error: %s\n", SDL_GetError() );
                }
                //Initialize OpenGL
                if( !initGL() )
                {
                    printf( "Unable to initialize OpenGL!\n" );
                    success = false;
                }
            }
        }
    }
    return success;
}

Depois que a janela ter sido criada com sucesso, chamamos SDL_GL_CreateContext para criar um contexto de renderização do OpenGL. Se isso for concluido com sucesso, ativamos Vsync com SDL_GL_SetSwapInterval.

Depois que a janela SDL OpenGL ter sido criada, inicializamos parametros internos do OpenGL com a função initGL.

bool initGL()
{
    bool success = true;
    GLenum error = GL_NO_ERROR;
    //Initialize Projection Matrix
    glMatrixMode( GL_PROJECTION );
    glLoadIdentity();
    //Check for error
    error = glGetError();
    if( error != GL_NO_ERROR )
    {
        printf( "Error initializing OpenGL! %s\n", gluErrorString( error ) );
        success = false;
    }
    //Initialize Modelview Matrix
    glMatrixMode( GL_MODELVIEW );
    glLoadIdentity();
    //Check for error
    error = glGetError();
    if( error != GL_NO_ERROR )
    {
        printf( "Error initializing OpenGL! %s\n", gluErrorString( error ) );
        success = false;
    }

Aqui temos a primeira parte do código OpenGL. Se esperava saber tudo sobre OpenGL ao fim desse artigo, isso não vai ser possível. OpenGL é um oceano em termos de tamanho e complexidade e não há como cobrir tudo em um único artigo. O que estamos fazendo aqui é aprender como usar OpenGL com o SDL.

Primeiro inicializamos a matriz de projeção que controla a perspectiva no OpenGL. Aqui fazemos isso pela configuração da matriz identidade. Verificamos se ocorreu algum erro e imprimos o resultado no terminal. Então fazemos o mesmo para a matriz de visualizaação que controla como os objetos renderizados são visualizados e posicionados.

    //Initialize clear color
    glClearColor( 0.f, 0.f, 0.f, 1.f );
    //Check for error
    error = glGetError();
    if( error != GL_NO_ERROR )
    {
        printf( "Error initializing OpenGL! %s\n", gluErrorString( error ) );
        success = false;
    }
    return success;
}

Por último, configuramos clear color, que é a cor usada para preencher a tela quando chamamos glClear.

Não vamos entrar em detalhes sobre como essas funções funcionam pois nossa principal preocupação é saber como SDL trabalha com OpenGL. Se quiser mais detalhes, pode dar uma olhada na documentação do OpenGL 2.1.

void handleKeys( unsigned char key, int x, int y )
{
    //Toggle quad
    if( key == 'q' )
    {
        gRenderQuad = !gRenderQuad;
    }
}
void update()
{
    //No per frame update needed
}

Aqui temos nossos manipuladores de entrada de teclas e de atualização. O manipulador de entrada de tecla alterna a flag de renderização e o manipulador de atualização está aqui apenas por questão de compatibilidade.

void render()
{
    //Clear color buffer
    glClear( GL_COLOR_BUFFER_BIT );
    //Render quad
    if( gRenderQuad )
    {
        glBegin( GL_QUADS );
            glVertex2f( -0.5f, -0.5f );
            glVertex2f( 0.5f, -0.5f );
            glVertex2f( 0.5f, 0.5f );
            glVertex2f( -0.5f, 0.5f );
        glEnd();
    }
}

Aqui no renderizador limpamos a tela e se a renderização está ativada, renderizamos uma quadrado.

O importante de se saber é o OpenGL usa coordenadas normalizadas. Isso significa que elas estão no intervalos -1 a 1:
imagem
Isso significa que nosso quadrado é maior na largura que na altura. Para poder termos uma sistema de coordenadas igual o do SDL, iremos ter que configurar a matrix de projeção para uma perspectiva ortográfica.

        //Enable text input
        SDL_StartTextInput();
        //While application is running
        while( !quit )
        {
            //Handle events on queue
            while( SDL_PollEvent( &e ) != 0 )
            {
                //User requests quit
                if( e.type == SDL_QUIT )
                {
                    quit = true;
                }
                //Handle keypress with current mouse position
                else if( e.type == SDL_TEXTINPUT )
                {
                    int x = 0, y = 0;
                    SDL_GetMouseState( &x, &y );
                    handleKeys( e.text.text[ 0 ], x, y );
                }
            }
            //Render quad
            render();
            //Update screen
            SDL_GL_SwapWindow( gWindow );
        }
        //Disable text input
        SDL_StopTextInput();

Aqui temos o loop principal fazendo uso das funções do loop principal. Uma coisa importante para saber é que quando vocÊ usar a renderização do OpenGL não pode usar a renderização do SDL para superfícies, texturas e outros elementos.  Como pode ver, usamos SDl_Gl_SwapWindow para atualizar a janela.

Nesse artigo vimos por alto os detalhes da renderização do OpenGL, mas se você quiser mais detalhes existem tutoriais sobre OpenGL para isso. Alguns tutoriais usam FreeGLUT, mas deve ser fácil portar o código para SDL 2 para  criar a janela e lidar com eventos. Nesse artigo usamos o antigo OpenGL versão 2.1 por ser mais fácil de usar. Você pode não querer usar esta versão, mas pense assim: muitos dos algoritmos gráficos básicos foram desenvolvidos nos anos 70. O que mudou é temos diferentes ferramentas para renderiza-los, mas os conceitos centrais não mudaram. Se você entender esses conceitos básicos, irá perceber que OpenGL 2, OpenGL 3, OpenGL 4 e mesmo Direct3D tem mais semalhanças do que doferenças. O importante é saber como sistemas de renderização baseados em geometria funcionam e sujar mãos para obter mais experiência.

Baixe os arquivos de mídia e do código fonte para esse artigo aqui.