Home OpenGL A máquina de estados OpenGL
No artigo anterior, vimos a história da OpenGL e alguns de seus conceitos mais importantes. Um dos aspectos mais ressaltados foi o fato da OpenGL se comportar como uma grande máquina de estados. Nesse artigo, vamos explorar melhor essa máquina, ler seus valores, definir valores e identificar possíveis situações de erro.
Ativando e desativando capacidades
A máquina de estados da OpenGL controla uma série de capacidades de desenho. Não é de se estranhar que a OpenGL possua, portanto, uma série igualmente grande de funções para questionar o seu valor, a maior parte começada com glGet.
Não é o propósito desse artigo mostrar todas elas. Algumas capacidades precisam de um entendimento muito grande, seja de computação gráfica ou da própria API, para serem compreendidas e serão explicadas futuramente. Entretanto, é bom conhecer desde cedo as principais funções disponíveis.
Três funções configuram-se entre as mais importantes na hora de ligar ou desligar capacidades. São elas:
glEnable(glEnum cap): Liga uma das mais de quarenta capacidades disponíveis na OpenGL. A capacidade desejada é passada como parâmetro. Por padrão, a maior parte das capacidades da OpenGL está inativa. Elas devem ser ativadas quando desejado, mas é bom lembrar que algumas são implementadas através de operações que podem ser pesadas e custar preciosos FPS. A lista completa das opções disponíveis pode ser encontrada aqui.
glDisable(glEnum cap): Obviamente, faz o contrário do glEnable. Ele desliga uma capacidade.
GLboolean glIsEnabled(GLEnum cap): É usada para questionar a OpenGL se uma determinada capacidade está ativa ou não. Essa função retorna GL_TRUE se a capacidade estiver ativa ou GL_FALSE, se não estiver. É importante testar o retorno da função com essas constantes, pois não há garantias de que seu valor poderá ser convertido implicitamente para true e false.
A capacidade passa a valer para todos os comandos após a sua ativação, e dura enquanto estiver ativada. Exemplos de capacidades são anti-aliasing (que suaviza bordas de figuras), alpha-blending (que permite efeitos de transparência, por exemplo), teste de profundidade, etc.
Verificando estados numéricos
Além das capacidades, a OpenGL também possui uma série de parâmetros numéricos e booleanos e, consequentemente, funções para questioná-los. Essas funções são:
Em todas elas recebem em pname o nome do parâmetro que você deseja questionar e em pname um array que seja grande o suficiente para receber o valor de retorno. Você pode obter qualquer parâmetro usando qualquer função, entretanto, as seguintes regras de conversão irão se aplicar:
Se o parâmetro for float ou int, e você o solicitar como boolean, a função retornará GL_FALSE se o parâmetro for 0, ou GL_TRUE, caso contrário;
Se o parâmetro for float e você requisitá-lo como um int, a função retornará o valor inteiro mais próximo. Os valores booleanos continuam retornando GL_TRUE e GL_FALSE;
Se você solicitar double, qualquer um dos valores acima será convertido para double.
Outro detalhe importante é que a função glFloatv geralmente arredonda o parâmetro para o inteiro mais próximo, mesmo que não haja conversão. Alguns casos são exceções a essa regra, como as cores e normais, mas não estranhe se o valor requisitado para outros parâmetros não for exatamente igual ao passado.
Requisitando Strings
Você pode descobrir detalhes da implementação da OpenGL em Runtime usando a seguinte função:
const GLbyte *glGetString(GLenum name)
A string retornada será um array de bytes terminados em 0 (ou ", se você se sente mais confortável assim). Um dos seguintes valores pode ser questionado a função:
GL_VENDOR: Indica o nome da empresa que implementou a OpenGL em uso. Por exemplo, se o fabricante for a NVIDIA, você irá receber um texto escrito "NVIDIA Corporation". Geralmente o nome do fabricante não muda de hardware para hardware;
GL_RENDERER: Indica o hardware que está sendo usado. Por exemplo, para mim ele retorna "GeForce 6800 GT/PCI/SSE2″. Novamente, esse valor pode não mudar de versão para versão da mesma placa de vídeo;
GL_VERSION: Contém a versão da OpenGL implementada. O formato retornado é major.minor.release, possivelmente seguida por informações adicionais do vendedor. No meu caso, a versão é: 2.1.1
GL_EXTENSIONS: Mostra uma lista delimitada por espaços de todas as extensões da OpenGL disponíveis. A lista pode ser bastante longa.
Que tal imprimir esses valores em sua casa? Você pode fazer isso rodando este exemplo. Se você estiver rodando no Windows, o resultado sairá num arquivo chamado stdout.txt. No linux, o resultado sai direto no console.
Pode parecer uma boa idéia usar esses valores para decidir quais capacidades da OpenGL serão ativadas ou não. Por exemplo, você poderia procurar por "NVIDIA" no texto para ativar certas configurações, caso a placa esteja lá. Embora isso seja sugestivo, essa é uma prática não recomendada. O ideal é munir o seu jogo com uma tela de configurações, onde o usuário pode definir alguns desses parâmetros de maneira controlada.
Dando dicas à OpenGL
Alguns comandos na OpenGL podem variar um pouco de implementação para implementação (ou driver para driver). Para esses casos, existem comandos que apenas dão dicas à OpenGL de como ele deve balancear a relação entre velocidade e qualidade. A diferença entre uma dica e uma capacidade é que o hardware não é obrigado a seguir a dica se ele não se julgar capaz.
A função usada para passar essas dicas à OpenGL é a:
As dicas (hints) podem assumir 3 valores:
GL_NICEST: Prioriza qualidade em relação à performance;
GL_FASTEST: Prioriza performance em relação à velocidade;
GL_DONT_CARE: Indica que você não se importa e deixa essa decisão para à OpenGL escolher o que achar melhor.
Podemos encontrar os seguintes parâmetros, que serão afetados por elas:
GL_POINT_SMOOTH_HINT, GL_LINE_SMOOTH_HINT, GL_POLYGON_SMOOTH_HINT: Específica a qualidade da amostra a ser utilizada durante uma operação de suavisação de serrilhado (anti-aliasing);
GL_FOG_HINT: Indica a qualidade do cálculo de neblina. A opção GL_FASTEST usa o cálculo através dos vértices, enquanto GL_NICEST calcula por pixel;
GL_PERSPECTIVE_CORRECTION_HINT: Ajusta a qualidade da interpolação de texturas e definições de cores.
Vale a pena lembrar novamente que as dicas são totalmente dependentes da implementação da OpenGL e, portanto, podem ser completamente ignoradas.
Quando uma função da OpenGL dá erro, seu comportamento é ignorado e um código de erro é definido. Você deve ter observado que nenhuma das funções acima retorna qualquer tipo de valor então, como ler esse código de erro? Através da função:
Essa função retorna o primeiro valor do primeiro erro ocorrido desde a última chamada à glGetError, ou desde o início da aplicação. Sempre que glGetError for chamado, o código de erro é retornado e a OpenGL volta a indicar GL_NO_ERROR. Além de GL_NO_ERROR, a função pode retornar um dos seguintes valores:
GL_INVALID_ENUM: Retorna quando um valor inválido é passado num parâmetro que aceite um GL_ENUM. Você pode notar que várias das funções acima aceitam um GLenum, mas nem todos os valores possíveis do GLenum são aceitos em todas as funções;
GL_INVALID_VALUE: Esse erro é retornado quando você colocou um parâmetro numérico acima do limite permitido;
GL_INVALID_OPERATION: Ocorre quando uma combinação de enum e valor resulta numa condição inválida ou inexistente. É um erro mais sutil que os anteriores e pode ser difícil de se encontrar;
GL_STACK_OVERFLOW: A OpenGL tem várias pilhas que podem ser manipuladas. Esse erro ocorre quando uma dessas pilhas é estourada. Veremos mais detalhes sobre após estudarmos as primitivas da OpenGL;
GL_STACK_UNDERFLOW: Esse erro é o oposto do anterior. Se, eventualmente, você desempilhar mais vezes do que empilhou, esse erro é retornado.
GL_OUT_OF_MEMORY: Falta de memória. Do contrário dos outros erros, a OpenGL pode alterar seu estado quando isso ocorre. Na verdade, isso é tão drástico que, se detectado, é melhor você tentar finalizar a sua aplicação da maneira mais delicada possível;
GL_TABLE_TOO_LARGE: Ocorre quando você tenta usar uma tabela que é maior do que a sua OpenGL é capaz de gerenciar. Geralmente esse erro está relacionado a subset de imagens da OpenGL, que não é assim tão usado em jogos.
Várias funções aceitam um argumento do tipo GLenum. Um GLenum nada mais é do que uma variável numérica, com diversos defines especificando diversos valores para ela (portanto, uma enumeração simplificada). Algumas funções aceitam apenas um pequeno subset dessa enumeração, mesmo recebendo como argumento um GL_enum. Fique atento a esse detalhe para não receber erros de GL_INVALID_ENUM;
Assim como a SDL a OpenGL também tem seus tipos de dados independentes de plataforma, tais como GLboolean, GLdouble, GLint, GLbyte e GLfloat;
Existem mais funções para questionar estados, uma relação completa pode ser obtida aqui. Muitas dessas funções (tal como glGetMaterial) serão discutidas em seus respectivos tópicos;
20/09/2010 09:14:28 |201.41.121.xxx| Zero - Moleza pros leitores
Olá, estou lendo os artigos numa paulada só e seria mais prático se houvesse, no fim do artigo, um atalho para o artigo seguinte.
Obrigado e abraços, o blog está de parabéns..
20/09/2010 12:13:06 | Vinícius Godoy de Mendonça
Sim, é uma das coisas que está no meu "to-do" list.
No topo do artigo, clique ali no nome da categoria (OpenGL), que vc vai para a lista completa de artigos. Não é tão prático como o link, mas quebra um galho.
Obrigado pelo elogio, estamos fazendo o possível para esse portal se tornar uma grande referência na área.