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.


Ações

Informações

12 respostas

9 12 2007
Diogo_RBG

Tô acompanhando… não pare !

“SDL_GL_SwapBuffers() garante que os comandos de pintura foram enviados para o hardware”
Sempre me perguntei o que será feita do SDL_Surface *window. Quando uso OpenGL eu não manipulo mais o buffer diretamente. A pergunta seria: Não posso ou não devo ?!

9 12 2007
vinigodoy

Até onde eu li, não deve. O ideal é não misturar as coisas.

Mas seria mesmo legal poder dar um blit direto. Isso evitaria ter que ficar trocando para o modo glOrtho sempre que quiséssemos desenhar um HUD.

12 12 2007
Guedes

Gostei do código, limpo e enxuto, parabéns.
Eu não sei usar OpenGL, que tipo de literatura complementar você sugere, incluindo calculo?

12 12 2007
vinigodoy

Oi Guedes,

Obrigado pelas correções. Apliquei todas no código. Removi do seu comment pq não tinham muito o que discutir eram pequenos erros mesmo.

Se você não se importar de ler em inglês, creio que um dos melhores materiais gratuitos é o próprio manual oficial (The Red Book):
http://www.glprogramming.com/red/

Ele trata de uma versão antiga do OpenGL, mas é um excelente ponto de partida. E, mesmo lá, você vai encontrar muita coisa desde o básico até o avançado.

Ainda em inglês, se você prefere material mais focado em código e tutoriais, não deixe de consultar o site do Nehe e a página do Nate Robins (o homem por trás do Glut):
http://nehe.gamedev.net/
http://www.xmission.com/~nate/tutors.html

Agora, um livro em português, nacional e interessante é o OpenGL Uma Abordagem Prática e Objetiva, pela editora Novatec, escrito por Marcelo Cohen e Isabela Harb Manssour. Não é um material gratuito.

12 12 2007
Guedes

Obrigado pela indicação, essas fontes eu já conhecia, meu problema é calculo, fico louco ao tentar entender o que são esses (0.0f,0.5f), (-0.5f, -0.5f) e (0.5f, -0.5f). Tem alguma indicação para calculo?

12 12 2007
vinigodoy

Seu problema então é mesmo a matemática?
Em inglês tem o 3D Math Primer. Além de muito bom, tem exemplos em C++.

Esses floats aí são coordenadas cartesianas. Por padrão, o OpenGL te dá uma área que vai de -1 até +1 nos eixos x e y.

No Brasil, os bons livros são os de geometria analítica, álgebra vetorial e álgebra linear. Mas são mais difíceis de ler, por se tratar de matemática pura. A grande vantagem é que existem em qualquer biblioteca.

O RedBook tem muita matemática. Além disso, alguns livros de OpenGL também trazem essa matemática básica. Mas nada como um livro específico. ;)

23 12 2007
Uzumaki Naruto

Olha, to acompanhando os tutoriais, mas deu um errinho aqui, na linha 60:

glViewPort(0,0, width, height);

O Code::Blocks acusou que glViewPort não foi declarada, tem certeza que glViewPort está em GL.h (ou SDL.h)?

De qualquer forma eu tirei essa função e rodou normalmente ;)

[],s

23 12 2007
Uzumaki Naruto

Ah, corrigi o problema, é que o p de glViewPort é minúsculo, no seu código tá maiúsculo. Só isso!

23 12 2007
vinigodoy

Sim, certeza absoluta!!! Muito estranho seu gl.h não ter a glViewPort. É uma das funções mais básicas e elementares do OpenGL. Você pode até mesmo conferir aqui na documentação oficial do OpenGL 1.1:

http://www.opengl.org/documentation/specs/man_pages/hardcopy/GL/html/

Entre no menu gl (que representa o gl.h) e procure por ViewPort.

Eu sinceramente não sei porque isso ocorreu na sua máquina. Vou dar uma pesquisada e posto a dica aqui.

23 12 2007
vinigodoy

Opa! Tem razão. Já corrigi ali no artigo. Obrigado por avisar! ;)

21 02 2008
MarceloJava

BLz, esse rodou sem problema estou acompanhando….

21 02 2008
vinigodoy

Beleza! :)

Deixe um comentário