OpenGL ES

Android zapewnia obsługę wysokiej wydajności grafiki 2D i 3D dzięki Open Graphics Library (OpenGL®), a w szczególności z interfejsem OpenGL ES API. OpenGL to wieloplatformowy interfejs API grafiki, który określa standardowy interfejs oprogramowania do obsługi sprzętu do przetwarzania grafiki 3D. OpenGL ES to rodzaj specyfikacji OpenGL przeznaczony do urządzeń wbudowanych. Android obsługuje kilka wersji interfejsu OpenGL ES API:

  • OpenGL ES 2.0 – ta specyfikacja interfejsu API jest obsługiwana przez Androida 2.2 (poziom interfejsu API 8) i nowsze wersje.
  • OpenGL ES 3.0 – ta specyfikacja interfejsu API jest obsługiwana przez Androida 4.3 (poziom API 18) i nowsze wersje.
  • OpenGL ES 3.1 – ta specyfikacja interfejsu API jest obsługiwana przez Androida 5.0 (poziom API 21) i nowsze wersje.
  • OpenGL ES 3.2 – ta specyfikacja interfejsu API jest obsługiwana przez Androida 7.0 (poziom API 24) i nowsze wersje.

Uwaga: bez względu na wersję platformy Androida urządzenie nie może obsługiwać interfejsu OpenGL ES 3.0 API, jeśli producent urządzenia nie zapewni implementacji tego potoku graficznego. Jeśli w pliku manifestu określisz, że wersja OpenGL ES 3.0 jest wymagana, będziesz mieć pewność, że urządzenie będzie zawierać tę wersję. Jeśli określisz, że wymagana jest wersja niższego poziomu, ale chcesz korzystać z funkcji 3.0, jeśli są dostępne, sprawdź w czasie działania, jaka wersja OpenGL obsługuje urządzenie. Więcej informacji na ten temat znajdziesz w sekcji Sprawdzanie wersji OpenGL ES.

Uwaga: Android zapewnia obsługę OpenGL ES 1.0 i 1.1, ale te wersje interfejsu API zostały wycofane i nie powinny być używane przez nowoczesne aplikacje.

Uwaga: interfejs API udostępniany przez platformę Androida jest podobny do interfejsu J2ME JSR239 OpenGL ES API, ale nie jest identyczny. Jeśli znasz specyfikację J2ME JSR239, uważaj na odmiany.

Patrz też

Podstawy

Android obsługuje tryb OpenGL zarówno za pomocą interfejsu API platformy, jak i pakietu Native Development Kit (NDK). Ten temat jest poświęcony interfejsom platformy Androida. Więcej informacji o pakiecie NDK znajdziesz w zestawie Android NDK.

Na platformie Androida są 2 podstawowe klasy, które umożliwiają tworzenie grafiki i manipulowanie nią za pomocą interfejsu OpenGL ES API: GLSurfaceView i GLSurfaceView.Renderer. Jeśli chcesz używać trybu OpenGL w swojej aplikacji na Androida, Twoim pierwszym celem powinna być wiedza o tym, jak wdrożyć te klasy w aktywności.

GLSurfaceView
Ta klasa to View, w której możesz rysować i modyfikować obiekty za pomocą wywołań interfejsu API OpenGL. Jej funkcja jest podobna do funkcji SurfaceView. Aby użyć tej klasy, utwórz instancję GLSurfaceView i dodaj do niej Renderer. Jeśli jednak chcesz rejestrować zdarzenia ekranu dotykowego, musisz rozszerzyć klasę GLSurfaceView, aby zaimplementować detektory dotyku, jak pokazano w lekcji trenowania OpenGL: Reagowanie na zdarzenia dotknięcia.
GLSurfaceView.Renderer
Ten interfejs definiuje metody wymagane do rysowania grafiki w GLSurfaceView. Musisz udostępnić implementację tego interfejsu jako oddzielną klasę i dołączyć ją do instancji GLSurfaceView za pomocą GLSurfaceView.setRenderer().

Interfejs GLSurfaceView.Renderer wymaga zaimplementowania tych metod:

  • onSurfaceCreated(): system wywołuje tę metodę raz podczas tworzenia obiektu GLSurfaceView. Użyj tej metody, aby wykonać działania, które muszą wystąpić tylko raz, takie jak ustawienie parametrów środowiska OpenGL lub inicjowanie obiektów graficznych OpenGL.
  • onDrawFrame(): system wywołuje tę metodę przy każdym odświeżeniu GLSurfaceView. Używaj tej metody jako podstawowego punktu wykonywania przy rysowaniu (i ponownym rysowaniu) obiektów graficznych.
  • onSurfaceChanged(): system wywołuje tę metodę, gdy zmieni się geometria GLSurfaceView, w tym zmiana rozmiaru elementu GLSurfaceView lub orientacji ekranu urządzenia. Na przykład system wywołuje tę metodę, gdy orientacja urządzenia zmieni się z pionowej na poziomą. Użyj tej metody, aby reagować na zmiany w kontenerze GLSurfaceView.

Pakiety OpenGL ES

Po skonfigurowaniu widoku kontenera dla OpenGL ES za pomocą GLSurfaceView i GLSurfaceView.Renderer możesz zacząć wywoływać interfejsy API OpenGL za pomocą tych klas:

  • Klasa interfejsu OpenGL ES 2.0 API
    • android.opengl.GLES20 – ten pakiet udostępnia interfejs OpenGL ES 2.0 i jest dostępny od Androida 2.2 (poziom API 8).
  • Pakiety interfejsów API OpenGL ES 3.0/3.1/3.2

Jeśli chcesz od razu rozpocząć tworzenie aplikacji w trybie OpenGL ES, zapoznaj się z kursem Wyświetlanie grafiki w trybie OpenGL ES.

Deklarowanie wymagań dotyczących OpenGL

Jeśli Twoja aplikacja korzysta z funkcji OpenGL, które nie są dostępne na wszystkich urządzeniach, musisz uwzględnić te wymagania w pliku AndroidManifest.xml. Oto najczęstsze deklaracje w pliku manifestu OpenGL:

  • Wymagania dotyczące wersji OpenGL ES – jeśli aplikacja wymaga konkretnej wersji OpenGL ES, musisz zadeklarować to wymaganie, dodając do pliku manifestu poniższe ustawienia, jak pokazano poniżej.

    W przypadku OpenGL ES 2.0:

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

    Dodanie tej deklaracji spowoduje, że Google Play ograniczy możliwość instalowania Twojej aplikacji na urządzeniach, które nie obsługują OpenGL ES 2.0. Jeśli aplikacja jest przeznaczona wyłącznie na urządzenia obsługujące OpenGL ES 3.0, możesz też określić to w pliku manifestu:

    W przypadku OpenGL ES 3.0:

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

    W przypadku OpenGL ES 3.1:

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

    W przypadku OpenGL ES 3.2:

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

    Uwaga: interfejs API OpenGL ES 3.x jest zgodny wstecznie z interfejsem API 2.0, dzięki czemu możesz elastyczniej wdrażać go w swojej aplikacji. Jeśli deklarujesz interfejs API OpenGL ES 2.0 jako wymagany w pliku manifestu, możesz używać tej wersji jako domyślnej, sprawdzać dostępność interfejsu API 3.x w czasie działania i używać funkcji OpenGL ES 3.x, jeśli urządzenie go obsługuje. Więcej informacji o sprawdzaniu wersji OpenGL ES obsługiwanej przez urządzenie znajdziesz w artykule Sprawdzanie wersji OpenGL ES.

  • Wymagania dotyczące kompresji tekstur – jeśli Twoja aplikacja korzysta z formatów kompresji tekstur, formaty obsługiwane przez nią musisz zadeklarować w pliku manifestu za pomocą <supports-gl-texture>. Więcej informacji o dostępnych formatach kompresji tekstur znajdziesz w artykule Obsługa kompresji tekstur.

    Zadeklarowanie w pliku manifestu wymagań dotyczących kompresji tekstur powoduje ukrycie aplikacji przed użytkownikami urządzeń, które nie obsługują co najmniej jednego z zadeklarowanych typów kompresji. Więcej informacji o tym, jak działa filtrowanie w Google Play przy kompresowaniu tekstur, znajdziesz w sekcji dotyczącej filtrowania w Google Play i filtrowania tekstur w dokumentacji <supports-gl-texture>.

Współrzędne mapowania rysowanych obiektów

Jednym z podstawowych problemów z wyświetlaniem grafiki na urządzeniach z Androidem jest to, że ekrany mogą mieć różne rozmiary i kształty. Tryb OpenGL zakłada kwadratowy, jednolity układ współrzędnych i domyślnie rysuje te współrzędne na ekranie, który zwykle nie jest kwadratowy, jakby był idealnie kwadratem.

Rysunek 1. Domyślny układ współrzędnych OpenGL (po lewej) zmapowany na typowy ekran urządzenia z Androidem (po prawej).

Na ilustracji powyżej przyjęto jednolity układ współrzędnych przyjmowany dla ramki OpenGL po lewej stronie oraz sposób, w jaki te współrzędne są mapowane na typowy ekran urządzenia w orientacji poziomej po prawej. Aby rozwiązać ten problem, można zastosować tryby projekcji OpenGL i widoki z kamery do przekształcenia współrzędnych, aby uzyskać właściwe proporcje obiektów graficznych na każdym wyświetlaczu.

Aby zastosować widoki projekcji i kamery, należy utworzyć macierz projekcji oraz macierz obrazu z kamery, a potem zastosować je do potoku renderowania OpenGL. Macierz projekcji ponownie oblicza współrzędne grafiki, aby prawidłowo mapowały się na ekrany urządzeń z Androidem. Matryca widoku kamery tworzy transformację, która renderuje obiekty z określonej pozycji oczu.

Projekcja i widok z kamery w trybie OpenGL ES 2.0 lub nowszym

W interfejsach API ES 2.0 i 3.0 projekcję i widok kamery stosuje się, dodając najpierw element macierzy do cieniowania wierzchołków obiektów graficznych. Po dodaniu tego elementu macierzy możesz generować i stosować do obiektów macierze obrazu i wyświetlania z kamery.

  1. Dodaj macierz do cieniowania wierzchołków – utwórz zmienną dla macierzy projekcji widoku i dodaj ją jako mnożnik pozycji cieniowania. W poniższym przykładowym kodzie cieniowania wierzchołków dołączony element uMVPMatrix umożliwia zastosowanie macierzy projekcji i wyświetlania kamery do współrzędnych obiektów korzystających z tego programu.

    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";
    

    Uwaga: przykład powyżej definiuje jeden element macierzy transformacji w cieniowaniu wierzchołków, do którego zastosujesz połączoną macierz projekcji i matrycę widoku kamery. W zależności od wymagań aplikacji możesz zdefiniować w cieniach wierzchołków osobne macierze projekcji i elementy macierzy do wyświetlania kamer, dzięki czemu będziesz mieć możliwość ich niezależnego modyfikowania.

  2. Dostęp do macierzy cieniowania – po utworzeniu punktu zaczepienia w cieniowaniu wierzchołków w celu zastosowania obrazu z kamery i odwzorowania możesz uzyskać dostęp do tej zmiennej, aby zastosować macierze obrazu i odwzorowania. Poniższy kod pokazuje, jak zmienić metodę onSurfaceCreated() w implementacji GLSurfaceView.Renderer, aby uzyskać dostęp do zmiennej macierzy zdefiniowanej w cieniowaniu wierzchołków powyżej.

    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. Tworzenie macierzy obrazu i odwzorowania – wygeneruj macierze obrazu i obrazu do zastosowania w obiektach graficznych. Poniższy przykładowy kod pokazuje, jak zmodyfikować metody onSurfaceCreated() i onSurfaceChanged() w implementacji GLSurfaceView.Renderer, aby utworzyć macierz obrazu z kamery i matrycę projekcji na podstawie współczynnika proporcji ekranu urządzenia.

    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. Stosowanie macierzy do odwzorowania i obrazu z kamery – aby zastosować przekształcenia związane z rzutem i widokiem kamery, pomnóż przez siebie macierze i umieść je w cieniu wierzchołków. Poniższy przykładowy kod pokazuje, jak zmodyfikować metodę onDrawFrame() w implementacji GLSurfaceView.Renderer, aby połączyć macierz projekcji i widok kamery utworzony w powyższym kodzie, a następnie zastosować ją do obiektów graficznych przeznaczonych do renderowania przez 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
        ...
    }
    

Pełny przykład zastosowania obrazu i obrazu z kamery za pomocą OpenGL ES 2.0 znajdziesz w artykule na temat wyświetlania grafiki w OpenGL ES.

Kształtuj twarze i kręcisz się

W trybie OpenGL ściana kształtu jest powierzchnią zdefiniowaną przez co najmniej trzy punkty w trójwymiarowej przestrzeni. Zestaw trójwymiarowych lub więcej trójwymiarowych punktów (nazywanych w trybie OpenGL wierzchołkami) ma przednią stronę i ścianę tylną. Skąd wiadomo, która tarcza jest przednią, a która z tyłu? Dobre pytanie. Wynika to z przewijania, czyli kierunku, w którym definiujesz punkty kształtu.

Współrzędne są współrzędne
przy wierzchołkach trójkąta

Rysunek 1. Ilustracja listy współrzędnych, która przekłada się na kolejność rysowania w lewo.

W tym przykładzie punkty trójkąta są zdefiniowane w kolejności, w której są rysowane w kierunku przeciwnym do ruchu wskazówek zegara. Kolejność rysowania współrzędnych określa kierunek winięcia kształtu. W trybie OpenGL domyślnie twarz jest narysowana w lewo. Trójkąt widoczny na Rysunku 1 jest zdefiniowany tak, aby patrzeć na przednią płaszczyznę kształtu (interpretowaną przez OpenGL), a drugą stroną jest tylna ściana.

Dlaczego ważne jest, aby wiedzieć, która ściana ma kształt przedniej strony? Wynika to z popularnej funkcji OpenGL, tzw. rozpoznawania twarzy. Selekcjonowanie twarzy to opcja w środowisku OpenGL, która umożliwia potokowi renderowania ignorowanie (nie obliczanie ani rysowanie) tylnej płaszczyzny kształtu, co oszczędza czas, pamięć i cykle przetwarzania:

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);

Jeśli spróbujesz użyć funkcji zaznaczania twarzy, nie wiedząc, które boki stanowią przód i tył, grafika OpenGL będzie trochę cienka lub w ogóle się nie pojawi. Dlatego zawsze definiuj współrzędne kształtów OpenGL w kolejności rysowania w kierunku przeciwnym do ruchu wskazówek zegara.

Uwaga: możliwe jest skonfigurowanie środowiska OpenGL w taki sposób, aby traktowano tarczę w prawo jako tarczę przednią, ale wymaga to dodatkowego kodu i może zdezorientować doświadczonych programistów OpenGL, gdy poprosisz o pomoc. Nie rób tego.

Wersje OpenGL i zgodność urządzeń

Specyfikacje interfejsu API OpenGL ES 1.0 i 1.1 są obsługiwane od Androida 1.0. Programowanie graficzne za pomocą interfejsu API OpenGL ES 1.0/1.1 znacznie różni się od korzystania z wersji 2.0 i nowszych. Język OpenGL ES 2.0 jest obsługiwany na wszystkich urządzeniach z Androidem w wersji od 2.2 (poziom interfejsu API 8) i jest najstarszą wersją zalecana dla nowych aplikacji tworzonych na platformie OpenGL ES. Interfejs OpenGL ES 3.0 jest obsługiwany z Androidem 4.3 (poziom interfejsu API 18) lub nowszym na urządzeniach z implementacją interfejsu OpenGL ES 3.0 API. Informacje o względnej liczbie urządzeń z Androidem, które obsługują daną wersję OpenGL ES, znajdziesz w panelu wersji OpenGL ES.

Uważnie zapoznaj się z wymaganiami dotyczącymi grafiki i wybierz wersję interfejsu API, która najlepiej sprawdzi się w Twojej aplikacji. Więcej informacji znajdziesz w artykule o wybieraniu wersji interfejsu OpenGL API.

Interfejs OpenGL ES 3.0 API zapewnia dodatkowe funkcje i większą wydajność niż interfejs API 2.0. Jest też zgodny wstecznie. Oznacza to, że możesz zapisać ustawienia kierowania aplikacji na OpenGL ES 2.0 i warunkowo włączyć funkcje graficzne OpenGL ES 3.0, jeśli są dostępne. Więcej informacji o sprawdzaniu dostępności interfejsu API 3.0 znajdziesz w artykule Sprawdzanie wersji OpenGL ES.

Obsługa kompresji tekstur

Kompresja tekstur może znacznie zwiększyć wydajność aplikacji OpenGL, zmniejszając wymagania dotyczące pamięci i efektywniej wykorzystując przepustowość pamięci. Platforma Androida obsługuje w standardowych funkcjach format kompresji ETC1, w tym klasę narzędziową ETC1Util i narzędzie do kompresji etc1tool (w pakiecie Android SDK na <sdk>/tools/). Przykład aplikacji na Androida, która korzysta z kompresji tekstur, znajdziesz w przykładzie kodu CompressedTextureActivity w pakiecie Android SDK (<sdk>/samples/<version>/ApiDemos/src/com/example/android/apis/graphics/).

Format ETC1 jest obsługiwany na wszystkich urządzeniach z Androidem, które obsługują platformę OpenGL ES 2.0 lub nowszą wersję.

Uwaga: format kompresji tekstur ETC1 nie obsługuje tekstur z przezroczystością (kanał alfa). Jeśli Twoja aplikacja wymaga tekstur z przezroczystością, sprawdź inne formaty kompresji tekstur dostępne na urządzeniach docelowych. Metoda renderowania tekstur kanału alfa za pomocą ETC1 polega na wiązaniu dwóch obiektów tekstury ETC1: pierwszego z danymi o kolorze, a drugiego z danymi kanału alfa, a następnie połączeniem wartości z 2 tekstur w cieniowaniu fragmentów.

Gwarantujemy dostępność formatów kompresji tekstur ETC2/EAC, gdy używany jest interfejs API OpenGL ES 3.0. Ten format tekstury zapewnia doskonałe współczynniki kompresji i wysoką jakość obrazu. Obsługuje też przezroczystość (kanał alfa).

Oprócz formatów ETC urządzenia z Androidem oferują zróżnicowane obsługę kompresji tekstur w zależności od układów GPU i implementacji OpenGL. Warto sprawdzić obsługę kompresji tekstur na urządzeniach, na które kierujesz reklamy, aby określić, jakie typy kompresji powinna obsługiwać aplikacja. Aby określić, jakie formaty tekstur są obsługiwane na danym urządzeniu, wyślij zapytanie do urządzenia i sprawdź nazwy rozszerzeń OpenGL, które określają, jakie formaty kompresji tekstur (i inne funkcje OpenGL) są obsługiwane przez urządzenie. Oto niektóre z powszechnie obsługiwanych formatów kompresji tekstur:

  • Adaptable Scalable Texture Identification (ASTC) – format kompresji tekstur zaprojektowany do zastąpienia wcześniejszych formatów. Większa elastyczność w porównaniu z poprzednimi formatami dzięki obsłudze różnych rozmiarów bloków.
    • GL_KHR_texture_compression_astc_ldr
    • GL_KHR_texture_compression_astc_hdr(wysoki zakres dynamiczny)
  • S3TC (DXTn/DXTC) – kompresja tekstur S3 (S3TC) występuje w kilku formatach (od DXT1 do DXT5) i jest rzadsza. Ten format obsługuje tekstury RGB z 4-bitowymi kanałami alfa lub 8-bitowymi kanałami alfa. Te formaty są reprezentowane przez tę nazwę rozszerzenia OpenGL:
    • GL_EXT_texture_compression_s3tc
    Niektóre urządzenia obsługują tylko wersję formatu DXT1. Taka ograniczona obsługa jest oznaczona przez tę nazwę rozszerzenia OpenGL:
    • GL_EXT_texture_compression_dxt1

Poniższe formaty kompresji tekstur są uważane za starsze i nie zalecamy ich stosowania w nowych aplikacjach:

  • ATITC (ATC) – kompresja tekstur ATI (ATITC lub ATC) jest dostępna na wielu urządzeniach i obsługuje kompresję stałą dla tekstur RGB z kanałem alfa i bez niego. Ten format może być reprezentowany przez kilka nazw rozszerzeń OpenGL, na przykład:
    • GL_AMD_compressed_ATC_texture
    • GL_ATI_texture_compression_atitc
  • PVRTC – kompresja tekstur PowerVR (PVRTC) jest dostępna na wielu urządzeniach i obsługuje tekstury 2-bitowe i 4-bitowe na piksel z kanałem alfa lub bez niego. Ten format jest reprezentowany przez tę nazwę rozszerzenia OpenGL:
    • GL_IMG_texture_compression_pvrtc
  • 3DC – kompresja tekstur 3DC to rzadszy format, który obsługuje tekstury RGB z kanałem alfa. Ten format jest reprezentowany przez taką nazwę rozszerzenia OpenGL:
    • GL_AMD_compressed_3DC_texture

Uwaga: te formaty kompresji tekstur nie są obsługiwane na wszystkich urządzeniach. Obsługa tych formatów zależy od producenta i urządzenia. W następnej sekcji znajdziesz informacje o tym, jak sprawdzić, jakie formaty kompresji tekstur są używane na poszczególnych urządzeniach.

Uwaga: gdy zdecydujesz, które formaty kompresji tekstur obsługuje Twoja aplikacja, zadeklaruj je w pliku manifestu za pomocą elementu <supports-gl-texture> . Użycie tej deklaracji umożliwia filtrowanie według usług zewnętrznych, takich jak Google Play, co sprawia, że aplikacja jest instalowana tylko na urządzeniach, które obsługują formaty wymagane przez Twoją aplikację. Szczegółowe informacje znajdziesz w sekcji Deklaracje pliku manifestu OpenGL.

Określanie rozszerzeń OpenGL

Implementacje interfejsu OpenGL różnią się w zależności od urządzenia z Androidem pod względem obsługiwanych rozszerzeń do interfejsu OpenGL ES API. Te rozszerzenia obejmują kompresje tekstur, ale zwykle obejmują też inne rozszerzenia do zestawu funkcji OpenGL.

Aby określić, jakie formaty kompresji tekstur i inne rozszerzenia OpenGL są obsługiwane na danym urządzeniu:

  1. Aby sprawdzić, które formaty kompresji tekstur są obsługiwane, na urządzeniach docelowych uruchom ten kod:

    Kotlin

    var extensions = gl.glGetString(GL10.GL_EXTENSIONS)
    

    Java

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

    Ostrzeżenie: wyniki tego wywołania różnią się w zależności od modelu urządzenia. Aby sprawdzić, jakie typy kompresji są zwykle obsługiwane, musisz uruchomić to wywołanie na kilku urządzeniach docelowych.

  2. Sprawdź dane wyjściowe tej metody, aby określić, jakie rozszerzenia OpenGL są obsługiwane na urządzeniu.

Pakiet rozszerzeń na Androida (AEP)

AEP zapewnia, że aplikacja obsługuje ustandaryzowany zestaw rozszerzeń OpenGL powyżej i poza zestawem podstawowym opisanym w specyfikacji OpenGL 3.1. Połączenie tych rozszerzeń zapewnia spójny zestaw funkcji na różnych urządzeniach, a deweloperom pozwala w pełni korzystać z najnowszych mobilnych GPU.

AEP ulepsza też obsługę obrazów, buforów pamięci do cieniowania i liczników atomowych w cieniowaniu fragmentów.

Aby aplikacja mogła korzystać z interfejsu AEP, w jej pliku manifestu musi być ona zadeklarowana. Ponadto wersja platformy musi ją obsługiwać.

Wszystkie dodatkowe funkcje określone w AEP są zawarte w podstawowej specyfikacji OpenGL ES 3.2. Jeśli aplikacja wymaga trybu OpenGL ES 3.2, nie jest wymagane korzystanie z AEP.

Zadeklaruj w pliku manifestu wymagania AEP:

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

Aby sprawdzić, czy wersja platformy obsługuje AEP, użyj metody hasSystemFeature(String), podając jako argument FEATURE_OPENGLES_EXTENSION_PACK. Poniższy fragment kodu pokazuje, jak to zrobić:

Kotlin

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

Java

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

Jeśli metoda zwraca wartość „true” (prawda), funkcja AEP jest obsługiwana.

Więcej informacji o AEP znajdziesz w rejestrze Khronos OpenGL ES.

Sprawdzanie wersji OpenGL ES

Na urządzeniach z Androidem dostępnych jest kilka wersji OpenGL ES. W pliku manifestu możesz określić minimalną wersję interfejsu API wymaganą przez aplikację, ale możesz też jednocześnie korzystać z funkcji nowszych interfejsów API. Na przykład interfejs API OpenGL ES 3.0 jest zgodny wstecznie z wersją 2.0 interfejsu API, możesz więc napisać aplikację w taki sposób, aby wykorzystywała funkcje OpenGL ES 3.0, ale jeśli interfejs API 3.0 nie jest dostępny, przełącza się na interfejs OpenGL ES 3.0.

Zanim skorzystasz z funkcji OpenGL ES w wersji wyższej niż wymagana w pliku manifestu aplikacji, aplikacja powinna sprawdzić wersję interfejsu API dostępną na urządzeniu. Możesz to zrobić na 2 sposoby:

  1. Spróbuj utworzyć kontekst OpenGL ES wyższego poziomu (EGLContext) i sprawdź wynik.
  2. Utwórz minimalny obsługiwany kontekst OpenGL ES i sprawdź wartość wersji.

Poniższy przykładowy kod pokazuje, jak sprawdzić dostępną wersję OpenGL ES przez utworzenie EGLContext i sprawdzenie wyniku. Ten przykład pokazuje, jak sprawdzić wersję 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;
  }
}

Jeśli powyższa metoda createContext() zwraca wartość null, Twój kod powinien utworzyć kontekst OpenGL ES 2.0 i wrócić do używania tylko tego interfejsu API.

Poniższy przykładowy kod ilustruje, jak sprawdzić wersję OpenGL ES przez utworzenie najpierw minimalnego obsługiwanego kontekstu, a potem sprawdzenie ciągu wersji:

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.

Jeśli okaże się, że urządzenie obsługuje wersję interfejsu API wyższego poziomu, musisz zniszczyć minimalny kontekst OpenGL ES i utworzyć nowy kontekst z wyższą dostępną wersją interfejsu API.

Wybieranie wersji interfejsu OpenGL API

Interfejsy OpenGL ES w wersjach 2.0 i 3.0 zapewniają wydajne interfejsy graficzne do tworzenia gier 3D, wizualizacji i interfejsów użytkownika. Programowanie grafiki dla OpenGL ES 2.0 i 3.0 jest w dużej mierze podobne – wersja 3.0 reprezentuje superzbiór interfejsu API 2.0 z dodatkowymi funkcjami. Programowanie interfejsu OpenGL ES 1.0/1.1 API względem OpenGL ES 2.0 i 3.0 znacznie się różni i nie jest zalecane dla nowych aplikacji. Zanim deweloperzy zaczną korzystać z tych interfejsów API, powinni uważnie wziąć pod uwagę te czynniki:

  • Zgodność urządzeń – deweloperzy powinni wziąć pod uwagę typy urządzeń, wersje Androida i wersje OpenGL ES dostępne dla klientów. Więcej informacji o zgodności OpenGL na różnych urządzeniach znajdziesz w sekcji Wersje OpenGL i zgodność urządzeń.
  • Obsługa tekstur – interfejs OpenGL ES 3.0 API ma najlepszą obsługę kompresji tekstur, ponieważ gwarantuje dostępność formatu kompresji ETC2, który obsługuje przejrzystość. Implementacje interfejsu API w wersji 2.0 obsługują ETC1, ale ten format tekstury nie obsługuje przezroczystości. Aby zaimplementować przezroczystość ze skompresowanymi teksturami, musisz użyć 2 tekstur ETC1 (podzielonych między kolor i alfa) lub udostępnić zasoby w innych formatach kompresji obsługiwanych przez urządzenia, na które kierujesz reklamy. Więcej informacji znajdziesz w artykule na temat obsługi kompresji tekstur.

Chociaż zgodność i obsługa tekstur mogą wpływać na Twoją decyzję, wybierz wersję interfejsu OpenGL API, kierując się tym, co Twoim zdaniem zapewni użytkownikom najlepsze wrażenia.