Criando uma janela OpenGL

9 12 2007

Embora o OpenGL seja extremamente poderoso no que diz respeito à parte gráfica, não existe em sua estrutura uma única função para controlar o ambiente de janelas. Isso porque os projetistas do OpenGL deciram construir uma biblioteca gráfica, e nada além disso.

Um dos primeiros projetistas desenvolveu uma camada limitada, adicional ao OpenGL chamada glut (OpenGL utilities), que justamente criava a janela e fazia um controle simples do teclado. Embora ele seja prático para o aprendizado, não é adequado para jogos.

Um dos grandes poderes da SDL é justamente preencher esse gap. Podemos usar os recursos da SDL – como o de criação de janelas e controle do mouse, teclado e joystick – para facilmente fazer um jogo completo com OpenGL.

Os atributos do OpenGL

No artigo passado, você aprendeu que todos os subsistemas da SDL são inicializados com SDL_Init. Entretanto, como o OpenGL é uma parte desse subsistema, outra função foi criada para inicializa-lo. Essa função é chamada SDL_GL_SetAttribute. Essa função possui em sua assinatura dois parâmetros. O primeiro, é um inteiro indicando qual atributo será alterado. O segundo, o novo valor do atributo. A lista dos atributos possíveis é:

  • SDL_GL_RED_SIZE: Define o número de bits do componente vermelho no frame buffer. Os valores comumente usados para esse número é o 5 ou manter o default (não usar a função SetAttribute). Existem constantes como essa também para o componente azul e verde, chamadas SDL_GL_GREEN_SIZE, SDL_GL_BLUE_SIZE, respectivamente;
  • SDL_GL_BUFFER_SIZE: Define o tamanho do frame buffer em bits. Esse valor deve ser igual ou superior a soma dos componentes de cor vermelho, verde e azul, descritos no comando anterior. Por exemplo, se você quer 24 bits de cor, com um canal alpha (transparência), o uso dos comandos seria:
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);

//Temos 3x8=24 bits para as cores + 8 bits alpha= 32 bits
SDL_GL_SetAttribute(SDL_GL_BUFFER_SIZE, 32 );
  • SDL_GL_DEPTH_SIZE: Esse atributo define o tamanho do buffer no eixo Z. O hardware gráfico normalmente suporta 16 e 32 bits. Se um valor não suportado for passado, o comando falhará.
  • SDL_GL_ACCUM_RED_SIZE: Define o valor do buffer de acumulação do OpenGL. Como no outro caso, há versões desse comando para as cores verde e azul. Deixaremos esses atributos definidos para zero. Para mais informações sobre os buffers do OpenGL, leia este material.
  • SDL_GL_DOUBLE_BUFFER: Define se o OpenGL deverá tentar ou não usar double buffering. Os valores para esse atributo podem ser 0 ou 1. É importante ressaltar que, quando estivermos trabalhando com OpenGL é aqui que definimos o double buffering, e não nos parâmetros da função SDL_SetVideoMode.

Não é possível alterar esses valores no decorrer do programa.

Além desses parâmetros, o comando glViewport diz ao OpenGL qual a porção da janela que deve ser utilizada. Veremos mais detalhe sobre esse comando no futuro, no artigo sobre sistemas de coordenadas. No próximo exemplo, usaremos toda a área da janela, mas você pode usar esse comando para definir uma área menor, como no caso de uma resolução de cinema vista numa tela quadrangular. Se esse comando for omitido, algumas versões do OpenGL não desenharão absolutamente nada, enquanto outras usarão o tamanho da tela por padrão.

Agora que sabemos o que cada parâmetro faz, que tal alterarmos o programa anterior para criar a nossa janela?

#include "SDL.h"
#include "GL/gl.h"

#include <stdexcept>
#include <iostream>

SDL_Surface* window;

Uint32 createFlags(bool fullscreen)
{
    //Iniciamos com a OpenGL e paleta de hardware
    Uint32 flags = SDL_OPENGL | SDL_HWPALETTE;

    if (fullscreen)
        flags |= SDL_FULLSCREEN;

    const SDL_VideoInfo* info = SDL_GetVideoInfo();

    //Criarmos uma superfície de hardware,
    //se este estiver disponível
    if (info->hw_available)
        flags |= SDL_HWSURFACE;
    else
        flags |= SDL_SWSURFACE;

    //Aceleração por hardware?
    if(info -> blit_hw)
        flags |= SDL_HWACCEL;

    return flags;
}

int setupOpenGL(int bpp, bool fullscreen)
{
    //Atributos do opengl
    SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
    SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, bpp);
    SDL_GL_SetAttribute( SDL_GL_ACCUM_RED_SIZE, 0);
    SDL_GL_SetAttribute( SDL_GL_ACCUM_GREEN_SIZE, 0);
    SDL_GL_SetAttribute( SDL_GL_ACCUM_BLUE_SIZE, 0);
    SDL_GL_SetAttribute( SDL_GL_ACCUM_ALPHA_SIZE, 0);

    return createFlags(fullscreen);
}

void setup(int width, int height, int bpp, bool fullscreen)
{
    //Inicializamos o subsistema de video.
    if (SDL_Init(SDL_INIT_VIDEO) == -1)
        throw std::runtime_error(SDL_GetError());

    //Tentamos criar a janela
    window = SDL_SetVideoMode(width, height, bpp,
    setupOpenGL(bpp, fullscreen));

    //Sem sucesso? Lançamos uma exceção com o erro.
    if (window == NULL)
        throw std::runtime_error(SDL_GetError());
     //Dizemos para o OpenGL usar toda a tela
     glViewport(0,0, width, height);
    //Configuramos a função de des-inicialização
    atexit (SDL_Quit);
}

/** Espera o usuário pressionar o x da janela. */
void processEvents()
{
    SDL_Event event;

    while (SDL_PollEvent(&event) != 0)
    {
        switch (event.type)
        {
            case SDL_QUIT:
                exit(0); //Fechamos a apliação
                break;
        }
    }
}

int main(int argc,char* argv[])
{
    try
    {
        setup(640, 480, 8, false); //Faz a mágica acontecer

        //Desenhamos um triângulo
        while (true)
        {
            processEvents();
            glClearColor(0.0, 0.0, 0.0, 0.0);
            glClear(GL_COLOR_BUFFER_BIT);

            glBegin(GL_TRIANGLES);
                glColor3f(1,0,0); glVertex2f(0.0f,0.5f);
                glColor3f(0,1,0); glVertex2f(-0.5f, -0.5f);
                glColor3f(0,0,1); glVertex2f(0.5f, -0.5f);
            glEnd();
            SDL_GL_SwapBuffers();
        }
    }
    catch (std::exception &e)
    {
        std::cout << "Error: " << e.what();
        exit(1);
    }
}

Se você conseguiu rodar esse programa corretamente, deve ter obtido uma janela como essa:

Janela SDL/OpenGL

 

Algumas notas sobre o programa

Nesse programa, usamos a função SDL_GetVideoInfo() para obter informações a respeito do hardware do jogador antes de desenhar a janela. Isso mostra mais um dos poderes da SDL e porque é uma boa idéia utiliza-la juntamente com o OpenGL. Essa função retorna um objeto do tipo SDL_VideoInfo, capaz informar se existe hardware, que tipo de aceleração ele possui e até mesmo, como está configurada a resolução do monitor.Também usamos a função SDL_GL_SwapBuffers(). Essa função garante que os comandos de pintura foram enviados para o hardware e que os buffers serão trocados, exibindo a imagem para o usuário.

Algumas funções de pintura do OpenGL foram utilizadas para desenhar o triângulo colorido. Não se preocupe com esse trecho de código agora, pois entrarei em mais detalhes sobre ele no futuro. Ele só foi inserido para demonstrar que a janela criada é realmente uma janela OpenGL.