A Visualização do desenvolvedor para Android 11 já está disponível. Teste e compartilhe seu feedback.

OpenGL ES

O Android inclui compatibilidade com gráficos 2D e 3D de alto desempenho com o Open Graphics Library (OpenGL®), especificamente, a API OpenGL ES. O OpenGL é uma API gráfica de várias plataformas que especifica uma interface de software padrão para hardware de processamento de gráficos 3D. OpenGL ES é uma variação da especificação OpenGL destinada a dispositivos incorporados. O Android é compatível com várias versões da API OpenGL ES:

  • OpenGL ES 1.0 e 1.1: esta especificação da API é compatível com o Android 1.0 e versões posteriores.
  • OpenGL ES 2.0: esta especificação da API é compatível com o Android 2.2 (API nível 8) e versões posteriores.
  • OpenGL ES 3.0: esta especificação da API é compatível com o Android 4.3 (API nível 18) e versões posteriores.
  • OpenGL ES 3.1: esta especificação da API é compatível com o Android 5.0 (API nível 21) e versões posteriores.

Cuidado: a compatibilidade da API OpenGL ES 3.0 em um dispositivo requer a implementação desse pipeline de gráficos fornecido pelo fabricante do dispositivo. Um dispositivo com o Android 4.3 ou versões anteriores pode não ser compatível com a API OpenGL ES 3.0. Para saber mais sobre como verificar qual versão do OpenGL ES é compatível durante a execução, consulte Verificar a versão do OpenGL ES.

Observação: a API específica disponibilizada pelo framework do Android é semelhante à API J2ME JSR239 OpenGL ES, mas não é idêntica. Se você conhece a especificação J2ME JSR239, preste atenção a possíveis variações.

Consulte também

Noções básicas

O Android é compatível com o OpenGL tanto por meio da API do framework quanto do Kit de desenvolvimento nativo (NDK, na sigla em inglês). Este tópico têm como foco as interfaces do framework do Android. Para mais informações sobre o NDK, consulte Android NDK.

Há duas classes básicas no framework do Android que permitem criar e manipular gráficos com a API OpenGL ES: GLSurfaceView e GLSurfaceView.Renderer. Se você quiser usar o OpenGL no seu aplicativo para Android, é importante entender, primeiramente, como implementar essas classes em uma atividade.

GLSurfaceView
Esta classe é uma View em que é possível desenhar e manipular objetos por meio de chamadas da API OpenGL. O funcionamento é semelhante ao de uma SurfaceView. Para usar essa classe, crie uma instância de GLSurfaceView e adicione seu Renderer a ela. No entanto, se você quiser capturar eventos de tela touchscreen, estenda a classe GLSurfaceView para implementar listeners de toque, como mostrado na lição de treinamento do OpenGL, Responder a eventos de toque.
GLSurfaceView.Renderer
Esta interface define os métodos necessários para desenhar gráficos em uma GLSurfaceView. É preciso disponibilizar uma implementação dessa interface como uma classe separada e anexá-la à instância GLSurfaceView usando GLSurfaceView.setRenderer().

A interface GLSurfaceView.Renderer requer a implementação dos seguintes métodos:

  • onSurfaceCreated(): o sistema chama esse método uma vez ao criar a GLSurfaceView. Use esse método para realizar ações que precisam acontecer apenas uma vez, como configurar parâmetros de ambiente do OpenGL ou inicializar objetos gráficos do OpenGL.
  • onDrawFrame(): o sistema chama esse método em cada novo desenho de GLSurfaceView. Use esse método como o ponto de execução principal para desenhar e refazer o desenho de objetos gráficos.
  • onSurfaceChanged(): o sistema chama esse método quando a geometria GLSurfaceView é alterada, incluindo as mudanças no tamanho de GLSurfaceView ou na orientação da tela do dispositivo. Por exemplo, o sistema chama esse método quando o dispositivo muda da orientação retrato para a paisagem. Use esse método para responder a mudanças no contêiner GLSurfaceView.

Pacotes do OpenGL ES

Depois de estabelecer uma visualização de contêiner para o OpenGL ES usando GLSurfaceView e GLSurfaceView.Renderer, você pode começar a chamar APIs do OpenGL por meio das seguintes classes:

Se você quiser começar a criar um app com o OpenGL ES imediatamente, veja a aula Como exibir gráficos com OpenGL ES.

Declarar requisitos do OpenGL

Se seu aplicativo usa recursos do OpenGL que não estão disponíveis em todos os dispositivos, é preciso incluir esses requisitos no arquivo AndroidManifest.xml. Estas são as declarações mais comuns de manifesto do OpenGL:

  • Requisitos de versão do OpenGL ES: se seu aplicativo exigir uma versão específica do OpenGL ES, será necessário declarar esse requisito adicionando as configurações a seguir ao seu manifesto, conforme mostrado abaixo.

    Para o OpenGL ES 2.0:

        <!-- Tell the system this app requires OpenGL ES 2.0. -->
        <uses-feature android:glEsVersion="0x00020000" android:required="true" />
        

    A adição dessa declaração faz o Google Play restringir a instalação do seu aplicativo em dispositivos que não são compatíveis com o OpenGL ES 2.0. Se seu aplicativo se destinar exclusivamente a dispositivos compatíveis com o OpenGL ES 3.0, também será possível especificar isso no manifesto:

    Para o OpenGL ES 3.0:

        <!-- Tell the system this app requires OpenGL ES 3.0. -->
        <uses-feature android:glEsVersion="0x00030000" android:required="true" />
        

    Para o OpenGL ES 3.1:

        <!-- Tell the system this app requires OpenGL ES 3.1. -->
        <uses-feature android:glEsVersion="0x00030001" android:required="true" />
        

    Observação: a API OpenGL ES 3.x é compatível com a versão anterior 2.0, o que significa que você pode ser mais flexível na implementação do OpenGL ES no seu aplicativo. Ao declarar a API OpenGL ES 2.0 como requisito no seu manifesto, você poderá usar essa versão da API como padrão, verificar a disponibilidade da API 3.x durante a execução e usar recursos do OpenGL ES 3.x se o dispositivo for compatível. Para mais informações sobre como verificar a versão do OpenGL ES compatível um dispositivo, consulte Verificar a versão do OpenGL ES.

  • Requisitos de compactação de texturas: se seu aplicativo usar formatos de compactação de texturas, será necessário declarar os formatos compatíveis com seu aplicativo no arquivo de manifesto por meio de <supports-gl-texture>. Para mais informações sobre os formatos de compactação de textura disponíveis, consulte Compatibilidade com compactação de texturas.

    A declaração de requisitos de compactação de texturas no manifesto oculta seu aplicativo dos usuários que usam dispositivos não compatíveis com pelo menos um dos tipos de compactação declarados. Para mais informações sobre o funcionamento da filtragem do Google Play para compactação de texturas, consulte a seção Google Play e filtragem de compactação de texturas da documentação <supports-gl-texture>.

Coordenadas de mapeamento para objetos desenhados

Um dos problemas básicos na exibição de gráficos em dispositivos Android é que as telas podem variar em tamanho e formato. O OpenGL presume que o sistema de coordenadas seja quadrado e uniforme e, por padrão, desenha essas coordenadas na sua tela normalmente, como se ela fosse um quadrado perfeito.

Figura 1. Sistema de coordenadas padrão do OpenGL (à esquerda) mapeado em uma tela comum de dispositivo Android (à direita).

A ilustração acima mostra o sistema de coordenadas uniforme presumido para um frame do OpenGL à esquerda e como essas coordenadas são realmente mapeadas em uma tela de dispositivo comum na orientação paisagem à direita. Para resolver esse problema, aplique modos de projeção do OpenGL e visualizações de câmera para transformar as coordenadas, de modo que os objetos gráficos tenham as proporções corretas em qualquer tela.

Para aplicar projeções e visualizações de câmera, crie uma matriz de projeção e uma matriz de visualização de câmera e aplique-as ao pipeline de renderização do OpenGL. A matriz de projeção recalcula as coordenadas de seus gráficos para que eles sejam corretamente mapeados nas telas de dispositivos Android. A matriz de visualização de câmera cria uma transformação que renderiza objetos a partir de uma determinada posição dos olhos.

Projeção e visualização de câmera no OpenGL ES 1.0

Na API ES 1.0, aplique a projeção e a visualização de câmera criando cada matriz e adicionando-as ao ambiente do OpenGL.

  1. Matriz de projeção: crie uma matriz de projeção usando a geometria da tela do dispositivo para recalcular as coordenadas do objeto, que serão desenhadas com as proporções corretas. O exemplo de código a seguir demonstra como modificar o método onSurfaceChanged() de uma implementação GLSurfaceView.Renderer para criar uma matriz de projeção com base na proporção da tela e aplicá-la ao ambiente de renderização do OpenGL.

    Kotlin

        override fun onSurfaceChanged(gl: GL10, width: Int, height: Int) {
            gl.apply {
                glViewport(0, 0, width, height)
    
                // make adjustments for screen ratio
                val ratio: Float = width.toFloat() / height.toFloat()
    
                glMatrixMode(GL10.GL_PROJECTION)            // set matrix to projection mode
                glLoadIdentity()                            // reset the matrix to its default state
                glFrustumf(-ratio, ratio, -1f, 1f, 3f, 7f)  // apply the projection matrix
            }
        }
        

    Java

        public void onSurfaceChanged(GL10 gl, int width, int height) {
            gl.glViewport(0, 0, width, height);
    
            // make adjustments for screen ratio
            float ratio = (float) width / height;
            gl.glMatrixMode(GL10.GL_PROJECTION);        // set matrix to projection mode
            gl.glLoadIdentity();                        // reset the matrix to its default state
            gl.glFrustumf(-ratio, ratio, -1, 1, 3, 7);  // apply the projection matrix
        }
        
  2. Matriz de transformação de câmera: depois de ajustar o sistema de coordenadas usando uma matriz de projeção, também será necessário aplicar uma visualização de câmera. O exemplo de código a seguir demonstra como modificar o método onDrawFrame() de uma implementação GLSurfaceView.Renderer para aplicar uma visualização modelo e usar o utilitário GLU.gluLookAt() para criar uma transformação de visualização que simula uma posição de câmera.

    Kotlin

        override fun onDrawFrame(gl: GL10) {
            ...
            gl.apply {
                // Set GL_MODELVIEW transformation mode
                glMatrixMode(GL10.GL_MODELVIEW)
                glLoadIdentity()                     // reset the matrix to its default state
            }
    
            // When using GL_MODELVIEW, you must set the camera view
            GLU.gluLookAt(gl, 0f, 0f, -5f, 0f, 0f, 0f, 0f, 1.0f, 0.0f)
            ...
        }
        

    Java

        public void onDrawFrame(GL10 gl) {
            ...
            // Set GL_MODELVIEW transformation mode
            gl.glMatrixMode(GL10.GL_MODELVIEW);
            gl.glLoadIdentity();                      // reset the matrix to its default state
    
            // When using GL_MODELVIEW, you must set the camera view
            GLU.gluLookAt(gl, 0, 0, -5, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
            ...
        }
        

Projeção e visualização de câmera no OpenGL ES 2.0 e versões posteriores

Nas APIs ES 2.0 e 3.0, aplique a projeção e a visualização de câmera adicionando primeiro um membro de matriz aos sombreadores de vértice dos objetos gráficos. Com esse membro de matriz adicionado, você pode gerar e aplicar matrizes de projeção e de visualização de câmera aos objetos.

  1. Adicionar matriz aos sombreadores de vértice: crie uma variável para a matriz de projeção de visualização e inclua-a como um multiplicador da posição do sombreador. No exemplo de código de sombreador de vértice, o membro uMVPMatrix incluído permite aplicar matrizes de projeção e de visualização de câmera às coordenadas de objetos que usam esse sombreador.

    Kotlin

        private val vertexShaderCode =
    
            // This matrix member variable provides a hook to manipulate
            // the coordinates of objects that use this vertex shader.
            "uniform mat4 uMVPMatrix;   \n" +
    
            "attribute vec4 vPosition;  \n" +
            "void main(){               \n" +
            // The matrix must be included as part of gl_Position
            // Note that the uMVPMatrix factor *must be first* in order
            // for the matrix multiplication product to be correct.
            " gl_Position = uMVPMatrix * vPosition; \n" +
    
            "}  \n"
        

    Java

        private final String vertexShaderCode =
    
            // This matrix member variable provides a hook to manipulate
            // the coordinates of objects that use this vertex shader.
            "uniform mat4 uMVPMatrix;   \n" +
    
            "attribute vec4 vPosition;  \n" +
            "void main(){               \n" +
            // The matrix must be included as part of gl_Position
            // Note that the uMVPMatrix factor *must be first* in order
            // for the matrix multiplication product to be correct.
            " gl_Position = uMVPMatrix * vPosition; \n" +
    
            "}  \n";
        

    Observação: o exemplo acima define um único membro da matriz de transformação no sombreador de vértice em que você aplica uma matriz de projeção combinada e uma matriz de visualização de câmera. Dependendo dos requisitos do seu aplicativo, defina uma matriz de projeção e membros da matriz de visualização de câmera separados nos sombreadores de vértice para poder mudá-los de maneira independente.

  2. Acessar a matriz do sombreador: depois de criar um hook nos sombreadores de vértice para aplicar a projeção e a visualização da câmera, você pode acessar essa variável para aplicar matrizes de projeção e de visualização de câmera. O código a seguir mostra como modificar o método onSurfaceCreated() de uma implementação GLSurfaceView.Renderer para acessar a variável de matriz definida no sombreador de vértice acima.

    Kotlin

        override fun onSurfaceCreated(gl: GL10, config: EGLConfig) {
            ...
            muMVPMatrixHandle = GLES20.glGetUniformLocation(program, "uMVPMatrix")
            ...
        }
        

    Java

        public void onSurfaceCreated(GL10 unused, EGLConfig config) {
            ...
            muMVPMatrixHandle = GLES20.glGetUniformLocation(program, "uMVPMatrix");
            ...
        }
        
  3. Criar matrizes de projeção e de visualização de câmera: gere as matrizes de projeção e de visualização a serem aplicadas aos objetos gráficos. O exemplo de código a seguir mostra como modificar os métodos onSurfaceCreated() e onSurfaceChanged() de uma implementação GLSurfaceView.Renderer para criar uma matriz de visualização de câmera e uma matriz de projeção com base na proporção da tela do dispositivo.

    Kotlin

        override fun onSurfaceCreated(gl: GL10, config: EGLConfig) {
            ...
            // Create a camera view matrix
            Matrix.setLookAtM(vMatrix, 0, 0f, 0f, -3f, 0f, 0f, 0f, 0f, 1.0f, 0.0f)
        }
    
        override fun onSurfaceChanged(gl: GL10, width: Int, height: Int) {
            GLES20.glViewport(0, 0, width, height)
    
            val ratio: Float = width.toFloat() / height.toFloat()
    
            // create a projection matrix from device screen geometry
            Matrix.frustumM(projMatrix, 0, -ratio, ratio, -1f, 1f, 3f, 7f)
        }
        

    Java

        public void onSurfaceCreated(GL10 unused, EGLConfig config) {
            ...
            // Create a camera view matrix
            Matrix.setLookAtM(vMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
        }
    
        public void onSurfaceChanged(GL10 unused, int width, int height) {
            GLES20.glViewport(0, 0, width, height);
    
            float ratio = (float) width / height;
    
            // create a projection matrix from device screen geometry
            Matrix.frustumM(projMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
        }
        
  4. Aplicar matrizes de projeção e de visualização de câmera: para aplicar as transformações de projeção e de visualização de câmera, multiplique as matrizes e defina-as no sombreador de vértice. O exemplo de código a seguir mostra como modificar o método onDrawFrame() de uma implementação GLSurfaceView.Renderer para combinar a matriz de projeção e a visualização de câmera criadas no código acima e aplicá-las aos objetos gráficos a serem renderizados pelo OpenGL.

    Kotlin

        override fun onDrawFrame(gl: GL10) {
            ...
            // Combine the projection and camera view matrices
            Matrix.multiplyMM(vPMatrix, 0, projMatrix, 0, vMatrix, 0)
    
            // Apply the combined projection and camera view transformations
            GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, vPMatrix, 0)
    
            // Draw objects
            ...
        }
        

    Java

        public void onDrawFrame(GL10 unused) {
            ...
            // Combine the projection and camera view matrices
            Matrix.multiplyMM(vPMatrix, 0, projMatrix, 0, vMatrix, 0);
    
            // Apply the combined projection and camera view transformations
            GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, vPMatrix, 0);
    
            // Draw objects
            ...
        }
        

Para ver um exemplo completo de como aplicar projeção e visualização de câmera com o OpenGL ES 2.0, veja a aula Como exibir gráficos com OpenGL ES.

Faces e enrolamento de formas

No OpenGL, a face de uma forma é uma superfície definida por três ou mais pontos no espaço tridimensional. Um conjunto de três ou mais pontos tridimensionais (denominados vértices no OpenGL) tem uma face frontal e uma face traseira. Como saber qual é a face frontal e qual é a traseira? Boa pergunta. A resposta tem a ver com o enrolamento, ou seja, a direção em que você define os pontos de uma forma.

Coordenadas nos vértices de um triângulo

Figura 1. Ilustração de uma lista de coordenadas que é traduzida em um desenho no sentido anti-horário.

Nesse exemplo, os pontos do triângulo são definidos na ordem em que são desenhados no sentido anti-horário. A ordem em que essas coordenadas são desenhadas define a direção de enrolamento da forma. Por padrão, no OpenGL, a face desenhada no sentido anti-horário é a frontal. O triângulo mostrado na Figura 1 é definido de modo que você esteja olhando para a face frontal da forma (conforme interpretado pelo OpenGL) e a face traseira esteja do outro lado.

Por que é importante saber qual face de uma forma é a frontal? A resposta tem a ver com um recurso muito usado do OpenGL, denominado seleção de face. A seleção de face é uma opção para o ambiente do OpenGL que permite ao pipeline de renderização ignorar (não calcular ou desenhar) a face traseira de uma forma, economizando tempo, memória e ciclos de processamento:

Kotlin

    gl.apply {
        // enable face culling feature
        glEnable(GL10.GL_CULL_FACE)
        // specify which faces to not draw
        glCullFace(GL10.GL_BACK)
    }
    

Java

    // enable face culling feature
    gl.glEnable(GL10.GL_CULL_FACE);
    // specify which faces to not draw
    gl.glCullFace(GL10.GL_BACK);
    

Se você tentar usar o recurso de seleção de face sem saber quais lados das formas são o frontal e o traseiro, seus gráficos do OpenGL terão uma aparência mais estreita ou poderão sequer ser exibidos. Portanto, sempre defina as coordenadas das formas do OpenGL em um sentido de desenho anti-horário.

Observação: é possível definir um ambiente do OpenGL para tratar a face no sentido horário como a face frontal, mas essa opção requer mais código e provavelmente confundirá os desenvolvedores experientes do OpenGL quando você pedir ajuda. Então, não faça isso.

Versões do OpenGL e compatibilidade com dispositivos

As especificações da API OpenGL ES 1.0 e 1.1 são compatíveis a partir do Android 1.0. A partir do Android 2.2 (API nível 8), o framework é compatível com a especificação da API OpenGL ES 2.0. O OpenGL ES 2.0 é compatível com a maioria dos dispositivos Android e é recomendado para aplicativos novos desenvolvidos com o OpenGL. O OpenGL ES 3.0 é compatível com o Android 4.3 (API nível 18) e versões posteriores em dispositivos que oferecem uma implementação da API OpenGL ES 3.0. Para mais informações sobre o número relativo de dispositivos Android compatíveis com uma determinada versão do OpenGL ES, consulte o painel de versões do OpenGL ES.

A programação de gráficos com a API OpenGL ES 1.0/1.1 é significativamente diferente do uso das versões 2.0 e posteriores. A versão 1.x da API tem mais métodos de conveniência e um pipeline gráfico fixo, enquanto as APIs OpenGL ES 2.0 e 3.0 oferecem um controle mais direto do pipeline por meio de sombreadores do OpenGL. Analise com atenção os requisitos gráficos e escolha a versão da API mais adequada para seu aplicativo. Para mais informações, consulte Escolher uma versão da API OpenGL.

A API OpenGL ES 3.0 oferece mais recursos e um desempenho melhor que a API 2.0 e também é compatível com versões anteriores. Isso significa que você pode programar seu aplicativo segmentando o OpenGL ES 2.0 e incluir condicionalmente os recursos gráficos do OpenGL ES 3.0, se eles estiverem disponíveis. Para mais informações sobre como verificar a disponibilidade da API 3.0, consulte Verificar a versão do OpenGL ES

Compatibilidade com compactação de textura

A compactação de textura pode melhorar significativamente o desempenho do seu aplicativo com OpenGL reduzindo os requisitos de memória e usando a largura de banda da memória de maneira mais eficiente. O framework do Android oferece compatibilidade com o formato de compactação ETC1 como recurso padrão, incluindo uma classe de utilitário ETC1Util e a ferramenta de compactação etc1tool localizada no SDK Android em <sdk>/tools/. Para ver um exemplo de um aplicativo para Android que usa compactação de texturas, consulte a amostra de código CompressedTextureActivity no SDK Android (<sdk>/samples/<version>/ApiDemos/src/com/example/android/apis/graphics/).

Cuidado: o formato ETC1 é compatível com a maioria dos dispositivos Android, mas não há garantias de que ele esteja disponível. Para verificar se o formato ETC1 é compatível com um dispositivo, chame o método ETC1Util.isETC1Supported().

Observação: o formato de compactação de textura ETC1 não é compatível com texturas com transparência (canal Alfa). Se seu aplicativo exigir texturas com transparência, busque outros formatos de compactação disponíveis nos seus dispositivos de destino.

Os formatos de compactação de textura ETC2/EAC são disponibilizados ao usar a API OpenGL ES 3.0. Esse formato de textura oferece excelentes taxas de compactação com alta qualidade visual, e o formato também é compatível com transparência (canal Alfa).

Além dos formatos ETC, os dispositivos Android oferecem uma compatibilidade variada com a compactação de textura baseada nos chipsets de GPU e implementações do OpenGL. Pesquise sobre a compatibilidade com a compactação de textura nos dispositivos que você está segmentando para determinar os tipos de compactação com que seu aplicativo deve ser compatível. Para determinar quais formatos de textura são compatíveis com um determinado dispositivo, consulte o dispositivo e analise os nomes da extensão do OpenGL, que identificam esses formatos e outros recursos do OpenGL compatíveis. Alguns dos formatos de compactação de textura mais comuns são:

  • ATITC (ATC): a compactação de textura ATI (ATITC ou ATC) está disponível em vários dispositivos e é compatível com a compactação de taxa fixa para texturas RGB com e sem um canal Alfa. Esse formato pode ser representado por vários nomes de extensão do OpenGL, por exemplo:
    • GL_AMD_compressed_ATC_texture
    • GL_ATI_texture_compression_atitc
  • PVRTC: a compactação de textura PowerVR (PVRTC) está disponível em vários dispositivos e é compatível com texturas de 2 e 4 bits por pixel com ou sem um canal Alfa. Esse formato é representado pelo seguinte nome de extensão do OpenGL:
    • GL_IMG_texture_compression_pvrtc
  • S3TC (DXTn/DXTC): a compactação de textura S3 (S3TC) tem diversas variações de formato (DXT1 a DXT5) e está disponível de forma menos ampla. O formato é compatível com texturas RGB com canais Alfa de 4 ou 8 bits. Esses formatos são representados pelo seguinte nome de extensão do OpenGL:
    • GL_EXT_texture_compression_s3tc
    Alguns dispositivos são compatíveis somente com a variação do formato DXT1. Essa limitação é representada pelo seguinte nome de extensão do OpenGL:
    • GL_EXT_texture_compression_dxt1
  • 3DC: a compactação de textura 3DC é um formato menos disponível que tem compatibilidade com texturas RGB com um canal Alfa. Esse formato é representado pelo seguinte nome de extensão do OpenGL:
    • GL_AMD_compressed_3DC_texture

Alerta: esses formatos de compactação de textura não são compatíveis com todos os dispositivos. A compatibilidade com esses formatos pode variar de acordo com o fabricante e o dispositivo. Para mais informações sobre como determinar quais são os formatos de compactação de textura em um dispositivo específico, consulte a próxima seção.

Observação: depois de decidir com quais formatos de compactação de textura seu aplicativo será compatível, declare-os no manifesto usando <supports-gl-texture>. Essa declaração permite a filtragem por serviços externos, como o Google Play, para que o app seja instalado apenas em dispositivos compatíveis com os formatos exigidos. Para mais detalhes, consulte as declarações do manifesto do OpenGL.

Determinar extensões do OpenGL

As implementações do OpenGL variam de acordo com o dispositivo Android com relação às extensões compatíveis com a API OpenGL ES. Essas extensões incluem compactações de textura, mas normalmente também incluem outras extensões para o conjunto de atributos do OpenGL.

Para determinar quais formatos de compactação de textura e outras extensões do OpenGL são compatíveis com um dispositivo específico:

  1. Execute o seguinte código nos dispositivos de destino para determinar quais formatos de compactação de textura são compatíveis:

    Kotlin

        var extensions = gl.glGetString(GL10.GL_EXTENSIONS)
        

    Java

        String extensions = gl.glGetString(GL10.GL_EXTENSIONS);
        

    Alerta: os resultados dessa chamada variam de acordo com o modelo do dispositivo. Execute essa chamada em vários dispositivos de destino para determinar quais tipos de compactação aceitos são mais usados.

  2. Veja a saída desse método para determinar quais extensões do OpenGL são compatíveis com o dispositivo.

Pacote de extensões para Android (AEP, na sigla em inglês)

O AEP garante que seu aplicativo seja compatível com um conjunto padronizado de extensões do OpenGL além do conjunto principal descrito na especificação do OpenGL 3.1. O empacotamento dessas extensões incentiva um conjunto consistente de funcionalidades em vários dispositivos, ao mesmo tempo em que permite que os desenvolvedores aproveitem ao máximo a seleção mais recente de dispositivos móveis de GPU.

O AEP também melhora a compatibilidade com imagens, buffers de armazenamento de sombreadores e contadores atômicos nos sombreadores de fragmento.

Para que seu app possa usar o AEP, o manifesto precisa declarar que o AEP é necessário. Além disso, a versão da plataforma precisa ser compatível com o pacote.

Declare o requisito de AEP no manifesto da seguinte forma:

    <uses feature android:name="android.hardware.opengles.aep"
                  android:required="true" />
    

Para verificar se a versão da plataforma é compatível com o AEP, use o método hasSystemFeature(String), transmitindo FEATURE_OPENGLES_EXTENSION_PACK como argumento. O snippet de código a seguir mostra como fazer isso:

Kotlin

    var deviceSupportsAEP: Boolean =
            packageManager.hasSystemFeature(PackageManager.FEATURE_OPENGLES_EXTENSION_PACK)
    

Java

    boolean deviceSupportsAEP = getPackageManager().hasSystemFeature
         (PackageManager.FEATURE_OPENGLES_EXTENSION_PACK);
    

Se o método retornar verdadeiro, o AEP será compatível.

Para mais informações sobre o AEP, visite a página relacionada no Registro Khronos do OpenGL ES (em inglês).

Verificar a versão do OpenGL ES

Existem várias versões do OpenGL ES disponíveis em dispositivos Android. Você pode especificar a versão mínima da API exigida pelo aplicativo no manifesto, mas também pode usar os recursos de uma API mais recente ao mesmo tempo. Por exemplo, a API OpenGL ES 3.0 é compatível com a versão 2.0 da API. Portanto, programe seu aplicativo de forma que ele use os recursos do OpenGL ES 3.0, mas recorra à API 2.0 se a API 3.0 não estiver disponível.

Antes de usar os recursos do OpenGL ES de uma versão posterior à mínima exigida no manifesto do aplicativo, é necessário verificar a versão da API disponível no dispositivo. Isso pode ser feito de duas maneiras:

  1. Tente criar o contexto do OpenGL ES de nível superior (EGLContext) e verifique o resultado.
  2. Crie um contexto do OpenGL ES mínimo compatível e verifique o valor da versão.

O exemplo de código a seguir mostra como verificar a versão do OpenGL ES disponível criando um EGLContext e verificando o resultado. O exemplo a seguir mostra como verificar a versão do OpenGL ES 3.0:

Kotlin

    private const val EGL_CONTEXT_CLIENT_VERSION = 0x3098
    private const val glVersion = 3.0
    private class ContextFactory : GLSurfaceView.EGLContextFactory {

        override fun createContext(egl: EGL10, display: EGLDisplay, eglConfig: EGLConfig): EGLContext {

            Log.w(TAG, "creating OpenGL ES $glVersion context")
            return egl.eglCreateContext(
                    display,
                    eglConfig,
                    EGL10.EGL_NO_CONTEXT,
                    intArrayOf(EGL_CONTEXT_CLIENT_VERSION, glVersion.toInt(), EGL10.EGL_NONE)
            ) // returns null if 3.0 is not supported
        }
    }
    

Java

    private static double glVersion = 3.0;

    private static class ContextFactory implements GLSurfaceView.EGLContextFactory {

      private static int EGL_CONTEXT_CLIENT_VERSION = 0x3098;

      public EGLContext createContext(
              EGL10 egl, EGLDisplay display, EGLConfig eglConfig) {

          Log.w(TAG, "creating OpenGL ES " + glVersion + " context");
          int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, (int) glVersion,
                  EGL10.EGL_NONE };
          // attempt to create a OpenGL ES 3.0 context
          EGLContext context = egl.eglCreateContext(
                  display, eglConfig, EGL10.EGL_NO_CONTEXT, attrib_list);
          return context; // returns null if 3.0 is not supported;
      }
    }
    

Se o método createContext() mostrado acima retornar nulo, seu código deverá criar um contexto do OpenGL ES 2.0 e voltar a usar somente essa API.

O exemplo de código a seguir mostra como verificar a versão do OpenGL ES criando um contexto mínimo compatível e verificando a string da versão:

Kotlin

    // Create a minimum supported OpenGL ES context, then check:
    gl.glGetString(GL10.GL_VERSION).also {
        Log.w(TAG, "Version: $it")
    }
     // The version format is displayed as: "OpenGL ES <major>.<minor>"
     // followed by optional content provided by the implementation.
    

Java

    // Create a minimum supported OpenGL ES context, then check:
    String version = gl.glGetString(GL10.GL_VERSION);
    Log.w(TAG, "Version: " + version );
    // The version format is displayed as: "OpenGL ES <major>.<minor>"
    // followed by optional content provided by the implementation.
    

Com essa abordagem, se você descobrir que o dispositivo é compatível com uma versão posterior de API, será necessário destruir o contexto mínimo do OpenGL ES e criar um novo contexto com a versão da API mais recente disponível.

Escolher uma versão da API OpenGL

A versão 1.0 da API OpenGL ES (e as extensões 1.1) e as versões 2.0 e 3.0 oferecem interfaces gráficas de alto desempenho para a criação de jogos 3D, visualizações e interfaces do usuário. A programação de gráficos para OpenGL ES 2.0 e 3.0 é muito semelhante, sendo que a versão 3.0 representa um superconjunto da API 2.0 com recursos extras. A programação da API OpenGL ES 1.0/1.1, em comparação ao OpenGL ES 2.0 e ao 3.0, difere significativamente. Por isso, os desenvolvedores devem considerar com atenção os seguintes fatores antes de iniciar o desenvolvimento com essas APIs:

  • Desempenho: em geral, o OpenGL ES 2.0 e o 3.0 oferecem um desempenho gráfico mais rápido do que as APIs ES 1.0/1.1. No entanto, a diferença de desempenho poderá variar de acordo com o dispositivo Android em que o aplicativo OpenGL estiver sendo executado, devido a diferenças na implementação do pipeline de gráficos do OpenGL ES feita pelo do fabricante do hardware.
  • Compatibilidade com dispositivos: os desenvolvedores devem considerar os tipos de dispositivos, versões do Android e versões do OpenGL ES disponíveis para os clientes. Para mais informações sobre a compatibilidade do OpenGL nos dispositivos, consulte a seção Versões do OpenGL e compatibilidade com dispositivos.
  • Conveniência de programação: a API OpenGL ES 1.0/1.1 oferece um pipeline de funções fixas e funções de conveniência que não estão disponíveis nas APIs OpenGL ES 2.0 ou 3.0. Os desenvolvedores sem experiência com o OpenGL ES podem achar a programação da versão 1.0/1.1 mais rápida e conveniente.
  • Controle de elementos gráficos: as APIs OpenGL ES 2.0 e 3.0 oferecem um nível mais alto de controle, disponibilizando um pipeline totalmente programável por meio de sombreadores. Com um controle mais direto do pipeline de processamento gráfico, os desenvolvedores podem criar efeitos que seriam muito difíceis de gerar usando a API 1.0/1.1.
  • Compatibilidade com texturas: a API OpenGL ES 3.0 oferece a melhor compatibilidade com a compactação de texturas porque ela garante a disponibilidade do formato de compactação ETC2, que aceita transparência. As implementações das APIs 1.x e 2.0 geralmente incluem compatibilidade com ETC1. No entanto, esse formato de textura não é compatível com transparência e, portanto, é necessário oferecer recursos em outros formatos de compactação compatíveis com os dispositivos segmentados. Para mais informações, consulte Compatibilidade com compactação de textura.

Embora desempenho, compatibilidade, conveniência, controle e outros fatores influenciem sua decisão, escolha uma versão da API OpenGL de acordo com o que você acredita ser a melhor experiência para seus usuários.