×
Namespaces

Variants
Actions

Archived:Introdução à OpenGL ES 1.0

From Nokia Developer Wiki
Jump to: navigation, search

Archived.pngAquivado: Este artigo foi arquivado, pois o conteúdo não é mais considerado relevante para se criar soluções comerciais atuais. Se você achar que este artigo ainda é importante, inclua o template {{ForArchiveReview|escreva a sua justificativa}}.

Acredita-se que este artigo ainda seja válido no contexto original (quando ele foi escrito)

Article Metadata

Testado com
Aparelho(s): Nokia N95, Nokia N82

Compatibilidade
Plataforma(s): Symbian^1

Artigo
Criado por lpvalente em Lpvalente
Última alteração feita por hamishwillee em 20 Jun 2012


Este artigo apresenta uma introdução ao uso de OpenGL ES [1] no ambiente Symbian OS. Assume-se que o leitor possui alguma familiaridade com a biblioteca OpenGL original [2].

Contents

Introdução

A OpenGL ES 1.0 é baseada na versão 1.3 da OpenGL original, enquanto a versão 1.1 é baseada em OpenGL 1.5. Atualmente, o grupo Khronos [3] é o responsável pelas especificações da OpenGL ES.

Devido à grande variedade de dispositivos móveis disponíveis que poderiam utilizar a OpenGL ES, foi definido um conceito denominado de profile (perfil). Um perfil define um subconjunto de funcionalidades de alguma versão completa da OpenGL, acrescido de propriedades específicas para a versão embarcada, como extensões. Aqui estão alguns perfis:

  • Common Profile: Destinado a dispositivos de entretenimento em massa como telefones celulares, PDAs e consoles, entre outros;
  • Common-Lite Profile: Destinado a dispositivos em que a confiabilidade e segurança são os fatores mais importantes da aplicação. Dessa forma, define um subconjunto mínimo necessário para que seja viável construir esse tipo de aplicação.

Assim como em todas as versões da OpenGL, a versão embarcada também define uma camada que é específica ao sistema onde é executada. Essa camada é definida como EGL e define uma API comum para ser usada com OpenGL ES. A implementação dessa camada, entretanto, depende do fabricante do hardware.

Arquivos necessários

Os arquivos necessários para se desenvolver aplicações para OpenGL ES já estão presentes a partir do SDK S60 2nd Edition FP2. A versão presente nesses SDKs é a 1.0. A versão 1.1 está presente a partir do SDK S60 3rd Edition FP1.

Os arquivos de inclusão são:

#include <GLES/egl.h>
#include <GLES/gl.h>

O arquivo de ligação com a DLL (Carbide.vs) necessário é:

libgles_cm.lib
ws32.lib

O primeiro arquivo corresponde ao perfil Common Profile. O perfil Common-Lite Profile não está disponível nos dispositivos S60. O segundo arquivo é específico para o Symbian OS, correspondente ao servidor de janelas. No caso do Carbide.c++, a extensão .dso deve ser usada para os arquivos de ligação quando a aplicação for compilada para o telefone.

Roteiro – Inicializando a OpenGL ES

Para se inicializar a OpenGL ES, é necessário utilizar a EGL e algumas de suas estruturas. O roteiro é o seguinte:

  1. Recuperar a tela padrão do dispositivo;
  2. Inicializar a OpenGL ES;
  3. Escolher uma configuração para a OpenGL ES;
  4. Criar o contexto OpenGL ES;
  5. Criar uma superfície de desenho;
  6. Fazer com o que o contexto criado em 4 seja o corrente.

O trecho de código a seguir declara uma classe que irá implementar esse roteiro:

#include <e32base.h>
#include <w32std.h>
 
#include "GLES/egl.h"
#include "GLES/gl.h"
 
class CGLRender: public CBase
{
public:
 
// método para se criar uma instância da classe
static CGLRender* NewL (RWindow & aWindow);
 
public:
 
// destrutor
~CGLRender ();
 
// double buffering
void SwapBuffers ();
 
private:
 
// construtor
CGLRender (RWindow& aWindow);
 
// segunda parte do construtor em duas fases
void ConstructL();
 
private:
 
RWindow iWindow;
 
EGLDisplay iEglDisplay;
EGLConfig iEglConfig;
EGLContext iEglContext;
EGLSurface iEglSurface;
};

O campo iEglDisplay representa o dispositivo físico do dispositivo. O campo iEglConfig representa a configuração da OpenGL ES. O campo iEglContext representa o contexto da OpenGL ES e iEglSurface representa uma superfície de desenho.

Recuperar a tela padrão do dispositivo

Para se recuperar a tela, pode ser usado este trecho de código:

// recuperar dispositivo físico padrão
iEglDisplay = eglGetDisplay ( EGL_DEFAULT_DISPLAY);
 
if (iEglDisplay == EGL_NO_DISPLAY)
User::Panic ( _L( "Unable to find a suitable EGLDisplay"), 0);

A constante EGL_DEFAULT_DISPLAY é usada para se referenciar a tela padrão do aparelho (muitas vezes só existe uma). Caso tenha ocorrido algum erro, a função irá retornar EGL_NO_DISPLAY.

Inicializar a OpenGL ES

Após recuperar a tela do dispositivo, é necessário inicializar a OpenGL ES através deste comando:

// inicialização do OpenGL ES	
if (!eglInitialize (iEglDisplay, 0, 0) )
User::Panic (_L("Unable to initialize EGL"), 0);

Além do objeto correspondente à tela, os outros dois parâmetros aceitos pela função servem para que a EGL informa qual é a sua versão. Por exemplo:

EGLint major, minor;
 
eglInitialize (iEglDisplay, & major, &minor);

Se a versão fosse 1.0, major conteria 1 e minor conteria 0.

Escolher uma configuração para a OpenGL ES

A seguir, é necessário escolher a configuração mínima que a aplicação deve usar. Para isso, é necessário usar este comando:

EGLint numConfigs;
 
if (!eglChooseConfig (iEglDisplay, attribList, &iEglConfig, 1, &numConfigs) )
User::Panic (_L("Unable to choose EGL config"), 0);

O parâmetro attribList representa uma lista de atributos que a aplicação deseja usar. O parâmetro iEglConfig corresponde a uma lista de configurações disponíveis no sistema, que são compatíveis com a descrição desejada. O tamanho máximo dessa lista é determinado no quarto parâmetro (que no caso pede para retornar apenas 1). O número total de configurações que são compatíveis com a descrição desejada é retornado através do parâmetro numConfigs.

A lista de atributos desejados para a aplicação é definida como um array de pares de inteiros, na forma de (atributo, valor). A EGL define constantes para todos os atributos possíveis de serem escolhidos. Por exemplo, dois deles que são comumente determinados são o modo de cores e o tamanho do z-buffer:

// lista de atributos desejados para a configuração da OpenGL
EGLint attribList [] =
{
EGL_BUFFER_SIZE, 0,
EGL_DEPTH_SIZE, 15,
EGL_NONE
};
 
 
// determinação da quantidade de bits por pixel, dependendo do modo
// de vídeo do aparelho
switch (iWindow.DisplayMode() )
{
case EColor4K:
attribList [1] = 12;
break;
 
case EColor64K:
attribList [1] = 16;
break;
 
case EColor16M:
attribList [1] = 24;
break;
 
default:
attribList [1] = 32;
}

A constante EGL_BUFFER_SIZE corresponde ao modo de cores (RGBA, em bits) e EGL_DEPTH_SIZE corresponde ao tamanho do z-buffer (bits). A lista de atributos deve ser terminada com EGL_NONE. A parte final do exemplo consulta o modo de vídeo, a partir de um objeto RWindow.

Criar o contexto OpenGL ES

O contexto da OpenGL ES é criado desta forma:

// criar contexto OpenGL
iEglContext = eglCreateContext (iEglDisplay, iEglConfig, EGL_NO_CONTEXT, 0);
 
if (iEglContext == 0)
User::Panic (_L("Unable to create EGL context"), 0);

O terceiro parâmetro serve para indicar um outro contexto a ser usado para se compartilhar objetos de textura, se desejado. No caso, EGL_NO_CONTEXT é usado para se indicar que não há compartilhamento. O último parâmetro representa uma lista de atributos a ser mapeada para o contexto. No exemplo, não existe essa lista.

Ativar o contexto criado

Para que o contexto possa ser usado efetivamente, é necessário torná-lo ativo. Na OpenGL ES, apenas um contexto pode estar ativo por vez. O trecho a seguir realiza essa operação:

// usar o contexto	
eglMakeCurrent (iEglDisplay, iEglSurface, iEglSurface, iEglContext);

A partir desse momento, todas as operações realizadas com OpenGL ES afetam esse contexto.

Finalização da OpenGL ES

Após utilizar a OpenGL ES, é necessário liberar os recursos. Essa parte é muito importante, especialmente por se tratar de dispositivos com poucos recursos.

CGLRender::~CGLRender()
{
eglMakeCurrent (iEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglDestroySurface (iEglDisplay, iEglSurface);
eglDestroyContext (iEglDisplay, iEglContext);
eglTerminate (iEglDisplay);
}

A primeira linha faz com que nenhum contexto esteja ativo. Depois, são destruídas a superfície de desenho e o contexto. A última linha finaliza a OpenGL ES.

Algumas diferenças entre OpenGL e OpenGL ES

A OpenGL ES utiliza double-buffering por padrão. Para trocar os buffers, deve-se proceder como no trecho seguinte:

void CGLRender::SwapBuffers ()
{
eglSwapBuffers (iEglDisplay, iEglSurface);
}

Outro exemplo, em OpenGL ES não é possível utilizar o modo imediato para desenhar. Ou seja, código como este a seguir, não existe em OpenGL ES:

glBegin (GL_TRIANGLES);
glVertex3f (0,1,0);
glVertex3f (-1,0,0);
glVertex3f (1,0,0);
glEnd();

Para se desenhar primitivas em OpenGL ES, é necessário utilizar vertex arrays. Dessa forma, a seguir têm-se um exemplo de como desenhar um triângulo em OpenGL ES:

const GLbyte KVertices []=
{
0,1,0,
-1,0,0,
1,0,0
};
 
...
 
glEnableClientState (GL_VERTEX_ARRAY);
glVertexPointer (3, GL_BYTE, 0, KVertices);
glDrawArrays (GL_TRIANGLES, 0, 3);

Devido ao fato de muitos dispositivos não possuírem FPUs ("floating point units") nem hardware gráfico dedicado, os perfis da OpenGL ES definem funções que aceitam parâmetros como ponto-fixo. Em particular, o perfil Common-Lite Profile não aceita ponto flutuante.

Matemática em ponto-fixo é uma técnica para se simular números de ponto flutuante utilizando números inteiros. Assim, define-se uma faixa de bits para representar a parte inteira, e outra faixa para representar a parte fracionária. A OpenGL ES trabalha com ponto-fixo 16:16, ou seja, um inteiro de 32 bits é dividido em 16 bits para parte inteira e 16 para a parte fracionária.

Devido também à essa limitação do ponto-flutuante, o tipo double foi removido da OpenGL ES. Exemplo de uso da função de translação em ponto-fixo:

glTranslatex (20 << 16, 0, 0, 1 << 16);
 
// equivalente a
glTranslatef (20.0f, 0.0f, 0.0f, 1.0f);

As funções equivalentes em ponto-fixo possuem o sufixo 'x'. Em particular, foi acrescido o tipo GLfixed para representar os números codificados em ponto-fixo.

Algumas outras diferenças:

  • As variações de funções que aceitam vários parâmetros, de vários tipos (ex: glVertex3{fsidv}) não existem mais, à exceção de algumas (como glColor4{fx}, por exemplo);
  • Somente o modo RGBA pode ser usado;
  • O único modo de desenho disponível para as primitivas é o sólido, ou seja, os modos de desenho em wireframe e pontos não podem usados;
  • Não é possível consultar estados dinâmicos da OpenGL na versão 1.0. Como exemplo de estado dinâmico, a cor atual. Entretanto, estados estáticos podem ser consultados.
  • Não existe a biblioteca GLU ("OpenGL Utility Library"). Entretanto, é possível encontrar na rede implementações das funções disponíveis na GLU original, convertidas para OpenGL ES.
  • As primitivas GL_QUADS, GL_QUAD_STRIP e GL_POLYGON não existem em OpenGL ES.

Particularidades e dicas para OpenGL ES

  • Em relação ao Symbian OS, não se deve usar o método eglSwapBuffers dentro do método Draw() da classe que representa a View. O desenho deve ser feito por uma outra classe, sendo atualizado por um temporizador ou objeto ativo.
  • É recomendado que se use o modo em tela cheia ("fullscreen") em aplicações OpenGL ES. Caso isso não seja feito, pode acontecer de a aplicação trabalhar apenas com meia tela (no telefone), ou problema parecido. Para se usar o modo em tela cheia, pode-se usar o método ApplicationRect() da classe de appUI, ao se criar a classe de view.
  • Utilizar números em ponto-fixo para especificar geometria, devido às limitações dos dispositivos em relação a ponto flutuante;
  • Evitar muitas trocas de estado. Por exemplo, alterar várias chamadas entre desenho de polígonos com textura e polígonos sem textura. O ideal, no caso, é otimizar a troca de estados ao agrupar os polígonos com e sem textura;
  • Habilitar o backface culling sempre que possível;
  • Desabilitar correção de perspectiva para objetos distantes (porque o efeito será pouco perceptível);
  • Desabilitar ou reduzir outros efeitos que não possam ser percebidos facilmente (mas que que consumam processamento);
  • Não utilizar iluminação, se possível;
  • Minimizar quantidade de texturas usadas na aplicação. Se possível, tentar agrupar imagens em uma única textura, de modo a minimizar a troca;
  • Tentar submeter grandes quantidades de polígonos em uma única chamada, em vez de fazer muitas chamadas com quantidades pequenas de polígonos.

Anexo: A classe completa

Aqui está o arquivo .cpp de implementação.

#include "CGLRender.h"
 
//_____________________________________________________________________________
 
CGLRender::CGLRender (RWindow & aWindow)
: iWindow (aWindow)
{}
 
//_____________________________________________________________________________
 
CGLRender::~CGLRender()
{
eglMakeCurrent (iEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglDestroySurface (iEglDisplay, iEglSurface);
eglDestroyContext (iEglDisplay, iEglContext);
eglTerminate (iEglDisplay);
}
 
//_____________________________________________________________________________
 
CGLRender* CGLRender::NewL (RWindow & aWindow)
{
CGLRender* instance = new (ELeave) CGLRender (aWindow);
CleanupStack::PushL (instance);
 
instance->ConstructL();
CleanupStack::Pop();
 
return instance;
}
 
//_____________________________________________________________________________
 
void CGLRender::ConstructL()
{
// lista de atributos
EGLint attribList [] =
{
EGL_BUFFER_SIZE, 0,
EGL_DEPTH_SIZE, 15,
EGL_NONE
};
 
 
// recuperar modo de cores do aparelho
switch (iWindow.DisplayMode() )
{
case EColor4K:
attribList [1] = 12;
break;
 
case EColor64K:
attribList [1] = 16;
break;
 
case EColor16M:
attribList [1] = 24;
break;
 
default:
attribList [1] = 32;
}
 
// passo 1
iEglDisplay = eglGetDisplay (EGL_DEFAULT_DISPLAY);
 
if (iEglDisplay == EGL_NO_DISPLAY)
User::Panic (_L("Unable to find a suitable EGLDisplay"), 0);
 
// passo 2
if (!eglInitialize (iEglDisplay, 0, 0) )
User::Panic (_L("Unable to initialize EGL"), 0);
 
// passo 3
EGLint numConfigs;
 
if (!eglChooseConfig (iEglDisplay, attribList, &iEglConfig, 1, &numConfigs) )
User::Panic (_L("Unable to choose EGL config"), 0);
 
// passo 4
iEglContext = eglCreateContext (iEglDisplay, iEglConfig, EGL_NO_CONTEXT, 0);
 
if (iEglContext == 0)
User::Panic (_L("Unable to create EGL context"), 0);
 
// passo 5
iEglSurface = eglCreateWindowSurface (iEglDisplay, iEglConfig, &iWindow, 0);
 
if (iEglSurface == NULL)
User::Panic (_L("Unable to create EGL surface"), 0);
 
// passo 6
eglMakeCurrent (iEglDisplay, iEglSurface, iEglSurface, iEglContext);
}
 
//_____________________________________________________________________________
 
void CGLRender::EnforceContext ()
{
eglMakeCurrent (iEglDisplay, iEglSurface, iEglSurface, iEglContext);
}
 
//_____________________________________________________________________________
 
void CGLRender::SwapBuffers ()
{
eglSwapBuffers (iEglDisplay, iEglSurface);
}
This page was last modified on 20 June 2012, at 03:04.
199 page views in the last 30 days.