OpenGL ES

Android 支援透過 Open Graphics Library (OpenGL®) 支援高效能 2D 和 3D 圖形,特別是 OpenGL ES API。OpenGL 是跨平台圖形 API,用於為 3D 圖形處理硬體指定標準軟體介面。OpenGL ES 是適用於嵌入式裝置的 OpenGL 規格變種版本。Android 支援多個 OpenGL ES API 版本:

  • OpenGL ES 2.0 - Android 2.2 (API 級別 8) 以上版本支援此 API 規格。
  • OpenGL ES 3.0 - Android 4.3 (API 級別 18) 以上版本支援此 API 規格。
  • OpenGL ES 3.1 - Android 5.0 (API 級別 21) 以上版本支援此 API 規格。
  • OpenGL ES 3.2 - Android 7.0 (API 級別 24) 以上版本支援此 API 規格。

注意:無論 Android 平台版本為何,除非裝置製造商提供這個圖形管道的實作方式,否則裝置無法支援 OpenGL ES 3.0 API。假如您在資訊清單中指定必須使用 OpenGL ES 3.0,則可確保裝置顯示該版本。如果您指定需要較低的級別版本,但您想要使用 3.0 版功能 (如果有的話),建議您在執行時檢查,瞭解裝置支援哪個 OpenGL 版本。如需操作方法的相關資訊,請參閱「檢查 OpenGL ES 版本」一文。

注意:Android 支援 OpenGL ES 1.0 和 1.1,但這些 API 版本已淘汰,不建議用於現今的應用程式。

注意:Android 架構提供的特定 API 與 J2ME JSR239 OpenGL ES API 類似,但並不相同。如果您熟悉 J2ME JSR239 規格,請發出版本異動通知。

亦請參閱

基本概念

Android 透過其架構 API 和原生開發工具包 (NDK) 支援 OpenGL。本主題著重於 Android 架構介面。如要進一步瞭解 NDK,請參閱 Android NDK

Android 架構提供兩種基礎類別,可讓您使用 OpenGL ES API 建立及操控圖形:GLSurfaceViewGLSurfaceView.Renderer。如果您的目標是在 Android 應用程式中使用 OpenGL,首先應瞭解如何在活動中實作這些類別。

GLSurfaceView
此類別是一種 View,您可以使用 OpenGL API 呼叫繪製及操控物件,其函式與 SurfaceView 類似。如要使用這個類別,請建立 GLSurfaceView 的執行個體,並在其中新增 Renderer。不過,如果您想擷取觸控螢幕事件,建議您擴充 GLSurfaceView 類別以實作觸控事件監聽器,如 OpenGL 訓練課程「回應觸控事件」所示。
GLSurfaceView.Renderer
這個介面定義在 GLSurfaceView 中繪製圖形所需的方法。您必須以獨立類別的形式提供這個介面的實作項目,並使用 GLSurfaceView.setRenderer() 將其附加至 GLSurfaceView 例項。

GLSurfaceView.Renderer 介面需要您實作下列方法:

  • onSurfaceCreated():建立 GLSurfaceView 時,系統會呼叫這個方法一次。使用這個方法可以執行只須發生一次的動作,例如設定 OpenGL 環境參數或初始化 OpenGL 圖形物件。
  • onDrawFrame():每次重新繪製 GLSurfaceView 時,系統會呼叫此方法。請使用這個方法做為繪圖 (和重新繪製) 圖形物件的主要執行點。
  • onSurfaceChanged():當 GLSurfaceView 幾何圖形變更時,系統會呼叫這個方法,包括 GLSurfaceView 的大小或裝置螢幕方向的變化。舉例來說,當裝置從直向改為橫向時,系統會呼叫這個方法。使用這個方法來回應 GLSurfaceView 容器中的變更。

OpenGL ES 套件

使用 GLSurfaceViewGLSurfaceView.Renderer 建立 OpenGL ES 的容器檢視區塊後,即可使用以下類別開始呼叫 OpenGL API:

  • OpenGL ES 2.0 API 類別
    • android.opengl.GLES20 - 這個套件提供 OpenGL ES 2.0 介面,且自 Android 2.2 (API 級別 8) 開始提供。
  • OpenGL ES 3.0/3.1/3.2 API 套件

如要立即開始使用 OpenGL ES 建構應用程式,請按照「使用 OpenGL ES 顯示圖形」類別操作。

宣告 OpenGL 需求

如果應用程式使用並非所有裝置都可用的 OpenGL 功能,您必須將這些需求加入 AndroidManifest.xml 檔案。以下是最常見的 OpenGL 資訊清單宣告:

  • OpenGL ES 版本需求 - 如果應用程式需要特定版本的 OpenGL ES,您必須在資訊清單中新增下列設定,以宣告該要求,如下所示。

    OpenGL ES 2.0 版本:

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

    新增這項宣告會使 Google Play 禁止在不支援 OpenGL ES 2.0 的裝置上安裝您的應用程式。如果您的應用程式僅適用於支援 OpenGL ES 3.0 的裝置,您也可以在資訊清單中指定這項資訊:

    OpenGL ES 3.0 版本:

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

    OpenGL ES 3.1 版本:

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

    OpenGL ES 3.2 版本:

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

    注意:OpenGL ES 3.x API 與 2.0 API 回溯相容,這表示您可以更靈活地在應用程式中導入 OpenGL ES。在資訊清單中宣告 OpenGL ES 2.0 API 為必要條件,您可以將該 API 版本設為預設、在執行階段檢查是否有 3.x API 的可用性,並在裝置支援時使用 OpenGL ES 3.x 功能。如要進一步瞭解如何檢查裝置支援的 OpenGL ES 版本,請參閱「檢查 OpenGL ES 版本」一文。

  • 紋理壓縮要求 - 如果應用程式使用紋理壓縮格式,您必須使用 <supports-gl-texture> 宣告應用程式支援的格式。如要進一步瞭解可用的紋理壓縮格式,請參閱「紋理壓縮支援」。

    在資訊清單中宣告紋理壓縮要求,如果裝置不支援至少一種宣告的壓縮類型,使用者就無法存取您的應用程式。如要進一步瞭解 Google Play 篩選功能如何處理紋理壓縮,請參閱 <supports-gl-texture> 說明文件的「 Google Play 和紋理壓縮篩選功能」一節。

已繪製物件的對應座標

在 Android 裝置顯示圖像時,其中一個基本問題就是螢幕的大小和形狀可能會有所不同。OpenGL 會假設採用方形、統一座標系統,且根據預設,將這些座標繪製到一般非方形螢幕上,就像是完全方形一樣。

圖 1 預設 OpenGL 座標系統 (左側) 對應至一般 Android 裝置螢幕 (右側)。

上圖為 OpenGL 影格使用的統一座標系統,以及這些座標在右側橫向模式實際對應到一般裝置螢幕的方式。如要解決這個問題,您可以套用 OpenGL 投影模式和相機檢視畫面來轉換座標,讓圖形物件在任何螢幕上都設有正確比例。

如要套用投影與相機檢視畫面,請建立投影矩陣和相機檢視矩陣,並套用至 OpenGL 算繪管道。投影矩陣會重新計算圖形的座標,以便正確對應至 Android 裝置螢幕。攝影機檢視矩陣會建立轉換功能,可算繪特定眼睛位置的物件。

OpenGL ES 2.0 以上版本中的投影與相機檢視畫面

在 ES 2.0 和 3.0 API 中,您必須先將矩陣成員新增至圖形物件的頂點著色器,即可套用投影與相機檢視畫面。新增這個矩陣成員後,即可產生並套用投影與相機檢視矩陣。

  1. 將矩陣新增至頂點著色器 - 為檢視投影矩陣建立變數,並將其加入為著色器位置的調節係數。在下列頂點著色器程式碼範例中,隨附的 uMVPMatrix 成員可讓您將投影和相機檢視矩陣,套用至使用這個著色器的物件座標。

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

    注意:上方範例定義了頂點著色器中的單一轉換矩陣成員,您可在該著色器中套用合併的投影矩陣和相機檢視矩陣。建議您根據應用程式需求,在頂點著色器中定義個別的投影矩陣和攝影機檢視矩陣成員,以便分別變更。

  2. 存取著色器矩陣 - 在頂點著色器中建立掛鉤,以套用投影和相機檢視畫面後,您就可以存取該變數來套用投影和相機檢視矩陣。以下程式碼顯示如何修改 GLSurfaceView.Renderer 實作的 onSurfaceCreated() 方法,以存取上方頂點著色器中定義的矩陣變數。

    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. 建立投影和攝影機檢視矩陣 - 產生投影,並檢視要套用圖形物件的矩陣。以下程式碼範例說明如何修改 GLSurfaceView.Renderer 實作的 onSurfaceCreated()onSurfaceChanged() 方法,以根據裝置螢幕顯示比例建立攝影機檢視矩陣和投影矩陣。

    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. 套用投影和相機檢視矩陣 - 如要套用投影和相機檢視畫面轉換,請將矩陣相乘,然後設為頂點著色器。以下程式碼範例說明如何修改 GLSurfaceView.Renderer 實作的 onDrawFrame() 方法,以結合在上述程式碼中建立的投影矩陣與相機檢視畫面,然後套用至 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
        ...
    }
    

如需使用 OpenGL ES 2.0 套用投影和相機檢視畫面的完整範例,請參閱「使用 OpenGL ES 顯示圖形」類別。

形狀錶面和眨眼

在 OpenGL 中,形狀的表面是由三維空間中的三個或更多點定義的介面。一組三個以上的 3D 點 (在 OpenGL 中稱為頂點) 具有一個正面和一個背面。如何辨別哪個臉孔為正面和背面?問得好。答案與曲面或定義形狀點的方向有關。

三角形的頂點座標

圖 1 插圖:座標清單會轉譯成逆時針繪製順序。

在這個範例中,三角形的點會按照逆時針方向繪製。這些座標的繪製順序會定義形狀的繞行方向。根據預設,在 OpenGL 中,逆時針繪製的臉孔為正面。圖 1 中所顯示的三角形定義了您查看形狀的正面 (依 OpenGL 解讀),另一側則是背面。

為何知道形狀是正面的那面?答案是透過 OpenGL 常用的功能,稱為臉部縮減。臉部縮減是 OpenGL 環境的一個選項,可讓轉譯管道忽略 (不計算或繪製) 形狀的後面,進而節省時間、記憶體和處理週期:

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

如果在不知道形狀的正面和背面,嘗試使用臉部縮減功能時,OpenGL 圖形看起來會比較淡,或甚至完全無法顯示。因此,請務必依照逆時針繪製順序定義 OpenGL 形狀的座標。

注意:您可以設定 OpenGL 環境將逆時針視為前端,但這麼做需要較多的程式碼,而且當您要求 OpenGL 開發人員協助時,可能會感到混淆。那就別這樣

OpenGL 版本和裝置相容性

自 Android 1.0 起,系統便開始支援 OpenGL ES 1.0 和 1.1 API 規格。使用 OpenGL ES 1.0/1.1 API 進行圖形程式設計,與使用 2.0 以上版本有明顯差異。從 Android 2.2 (API 級別 8) 開始的所有 Android 裝置都支援 OpenGL ES 2.0,如果使用 OpenGL ES 開發新應用程式,則建議使用最早的版本。在提供 OpenGL ES 3.0 API 實作的裝置上,Android 4.3 (API 級別 18) 以上版本支援 OpenGL ES 3.0。如要進一步瞭解支援特定 OpenGL ES 版本的 Android 裝置相對數量,請參閱 OpenGL ES 版本資訊主頁

建議您仔細考量圖像需求,並選擇最適合應用程式的 API 版本。詳情請參閱「選擇 OpenGL API 版本」一文。

OpenGL ES 3.0 API 提供比 2.0 API 更多的功能和更優異的效能,而且具有回溯相容性。也就是說,您可以編寫指定 OpenGL ES 2.0 的應用程式,並有條件地加入 OpenGL ES 3.0 圖形功能 (如果有的話)。如要進一步瞭解如何檢查 3.0 API 的可用性,請參閱「檢查 OpenGL ES 版本」一文

支援紋理壓縮

紋理壓縮能夠減少記憶體需求並提升記憶體頻寬的效率,進而大幅提高 OpenGL 應用程式的效能。Android 架構以標準功能的形式支援 ETC1 壓縮格式,包括 ETC1Util 公用程式類別和 etc1tool 壓縮工具 (位於 Android SDK 中的 <sdk>/tools/)。如需使用紋理壓縮的 Android 應用程式範例,請參閱 Android SDK 中的 CompressedTextureActivity 程式碼範例 (<sdk>/samples/<version>/ApiDemos/src/com/example/android/apis/graphics/)。

所有支援 OpenGL ES 2.0 以上版本的 Android 裝置都支援 ETC1 格式。

注意:ETC1 紋理壓縮格式不支援具有透明度 (Alpha 通道) 的紋理。如果應用程式需要透明的紋理,您應調查目標裝置可用的其他紋理壓縮格式。使用 ETC1 轉譯 Alpha 管道紋理的方法,是繫結兩個 ETC1 紋理物件:第一項處理色彩資料,第二項與 Alpha 管道資料,然後合併片段著色器中兩個紋理的值。

確保使用 OpenGL ES 3.0 API 時,可採用 ETC2/EAC 紋理壓縮格式。這種紋理格式具備絕佳的壓縮率,提供高品質的視覺品質,且格式也支援透明度 (Alpha 通道)。

除了 ETC 格式之外,Android 裝置也根據 GPU 晶片組和 OpenGL 實作項目,支援多種紋理壓縮。您應調查目標裝置上的紋理壓縮支援情況,以判斷應用程式應支援哪些壓縮類型。如要判斷特定裝置支援的紋理格式,您必須查詢裝置並查看 OpenGL 擴充功能名稱,進而識別裝置支援的紋理壓縮格式 (以及其他 OpenGL 功能)。一些常見的紋理壓縮格式如下:

  • 可調整的可擴充紋理壓縮 (ASTC) - 用於取代前述格式的紋理壓縮格式。這種格式支援各種區塊大小,因此相較於先前格式更有彈性。
    • GL_KHR_texture_compression_astc_ldr
    • GL_KHR_texture_compression_astc_hdr(高動態範圍)
  • S3TC (DXTn/DXTC) - S3 紋理壓縮 (S3TC) 有多種格式變化版本 (DXT1 到 DXT5),且較不普遍。這種格式支援採用 4 位元 Alpha 或 8 位元 Alpha 管道的 RGB 紋理。這些格式會以下列 OpenGL 擴充功能名稱表示:
    • GL_EXT_texture_compression_s3tc
    部分裝置只支援 DXT1 格式變化版本,這項有限的支援情形會以下列 OpenGL 擴充功能名稱表示:
    • GL_EXT_texture_compression_dxt1

下列紋理壓縮格式被視為舊版格式,因此不建議用於新的應用程式:

  • ATITC (ATC) - ATI 紋理壓縮 (ATITC 或 ATC) 適用於多種裝置,可在無論是否有 Alpha 通道的情況下,對 RGB 紋理採用固定速率壓縮功能。這種格式可能會以多個 OpenGL 擴充功能名稱表示,例如:
    • GL_AMD_compressed_ATC_texture
    • GL_ATI_texture_compression_atitc
  • PVRTC - PowerVR 紋理壓縮 (PVRTC) 適用於多種裝置,並支援 2 位元和 4 位元每個像素紋理 (無論是否有 Alpha 通道)。這種格式會以下列 OpenGL 擴充功能名稱表示:
    • GL_IMG_texture_compression_pvrtc
  • 3DC:3DC 紋理壓縮 (3DC) 是較不普遍使用的格式,可支援透過 Alpha 通道的 RGB 紋理。這種格式會以下列 OpenGL 擴充功能名稱表示:
    • GL_AMD_compressed_3DC_texture

警告:部分裝置不支援這些紋理壓縮格式。支援的格式會因製造商和裝置而異。如要瞭解如何判斷特定裝置上的紋理壓縮格式,請參閱下一節。

注意:決定應用程式支援的紋理壓縮格式後,請務必在資訊清單中使用 <supports-gl-texture> 宣告這些格式。使用這個宣告即可依外部服務 (例如 Google Play) 進行篩選,讓應用程式只安裝在支援應用程式所需格式的裝置上。詳情請參閱 OpenGL 資訊清單宣告

判斷 OpenGL 擴充功能

OpenGL 的實作方式會因 Android 裝置支援的 OpenGL ES API 擴充功能而異。這些擴充功能包括紋理壓縮,但通常也包含 OpenGL 功能集的其他擴充功能。

特定裝置支援哪些紋理壓縮格式和其他 OpenGL 擴充功能:

  1. 在目標裝置上執行以下程式碼,判斷系統支援的紋理壓縮格式:

    Kotlin

    var extensions = gl.glGetString(GL10.GL_EXTENSIONS)
    

    Java

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

    警告:本次呼叫的結果因裝置型號而異!您必須在多個目標裝置上執行這個呼叫,判斷通常支援的壓縮類型。

  2. 查看這個方法的輸出內容,判斷裝置支援哪些 OpenGL 擴充功能。

Android 擴充功能套件 (AEP)

AEP 可確保應用程式支援比 OpenGL 3.1 規格所述的核心集,更支援多種 OpenGL 擴充功能組合。將這些擴充功能封裝在一起後,就能讓各裝置享有一致的功能組合,同時讓開發人員充分利用最新的行動 GPU 裝置。

AEP 還改善了對片段著色器中圖片、著色器儲存空間緩衝區和不可分割計數器的支援功能。

如要讓應用程式使用 AEP,應用程式的資訊清單必須宣告需要 AEP。此外,平台版本必須支援這項功能。

AEP 中指定的所有額外功能都包含在基本 OpenGL ES 3.2 規格中。如果您的應用程式需要 OpenGL ES 3.2,則不需要 AEP。

在資訊清單中宣告 AEP 要求,如下所示:

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

如要驗證平台版本是否支援 AEP,請使用 hasSystemFeature(String) 方法,傳入 FEATURE_OPENGLES_EXTENSION_PACK 做為引數。以下程式碼片段說明如何執行這項作業:

Kotlin

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

Java

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

如果這個方法傳回 True,表示支援 AEP。

如要進一步瞭解 AEP,請前往 Khronos OpenGL ES Registry 參閱其頁面。

檢查 OpenGL ES 版本

Android 裝置提供數種版本的 OpenGL ES。您可以在資訊清單中指定應用程式所需的最低 API 版本,不過您也可以同時使用新版 API 的功能。舉例來說,OpenGL ES 3.0 API 與 2.0 版的 API 回溯相容,因此,建議您編寫應用程式,使其使用 OpenGL ES 3.0 功能,但如果 3.0 API 無法使用,則改回使用 2.0 API。

使用 OpenGL ES 功能時,如果其版本高於應用程式資訊清單中所需的最低版本,應用程式應檢查該裝置上可用的 API 版本。您可以選擇以下其中一種方法:

  1. 嘗試建立較高層級的 OpenGL ES 情境 (EGLContext) 並檢查結果。
  2. 建立支援的最低 OpenGL ES 情境,並檢查版本值。

下列程式碼範例說明如何透過建立 EGLContext 並檢查結果,檢查可用的 OpenGL ES 版本。以下範例說明如何檢查 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;
  }
}

如果上述 createContext() 方法傳回空值,程式碼應改為建立 OpenGL ES 2.0 內容,並改回只使用該 API。

下列程式碼範例說明如何先建立最低支援的結構定義,然後檢查版本字串,以檢查 OpenGL ES 版本:

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.

使用這個方法時,如果您發現裝置支援較高層級的 API 版本,就必須刪除最低 OpenGL ES 結構定義,並使用較可用的 API 版本建立新內容。

選擇 OpenGL API 版本

OpenGL ES 2.0 和 3.0 版都會提供高效能圖形介面,可用於建立 3D 遊戲、視覺化呈現和使用者介面。OpenGL ES 2.0 和 3.0 的圖形傳播大致類似,其中 3.0 版代表具有額外功能的 2.0 API 超集。與 OpenGL ES 2.0 和 3.0 相比,OpenGL ES 1.0/1.1 API 的程式設計差異很大,因此不建議新應用程式使用。開發人員在使用這些 API 著手開發之前,應審慎考量下列因素:

  • 裝置相容性 - 開發人員應考量客戶可用的裝置類型、Android 版本和 OpenGL ES 版本。如要進一步瞭解不同裝置的 OpenGL 相容性,請參閱「OpenGL 版本和裝置相容性」一節。
  • 紋理支援 - OpenGL ES 3.0 API 最適合支援紋理壓縮,因為 OpenGL ES 3.0 API 可確保採用 ETC2 壓縮格式,支援透明度。2.0 API 實作項目可支援 ETC1,但這個紋理格式不支援透明度。如要使用壓縮的紋理執行透明度,您必須使用兩種 ETC1 紋理 (在顏色和 Alpha 分割之間),或以您指定的裝置支援的其他壓縮格式提供資源。詳情請參閱紋理壓縮支援

雖然相容性和紋理支援可能會影響您的決定,但建議您根據認為可為使用者提供最佳體驗的 OpenGL API 版本。