OpenGL ES

Android は、Open Graphics Library(OpenGL®)、特に OpenGL ES API で高性能な 2D と 3D のグラフィックをサポートしています。OpenGL は、3D グラフィック処理ハードウェアの標準ソフトウェア インターフェースを指定する、クロス プラットフォームのグラフィック API です。OpenGL ES は組み込みデバイス向けの OpenGL 仕様の一種です。Android は OpenGL ES API のいくつかのバージョンをサポートしています。

  • OpenGL ES 1.0 と 1.1 - この API 仕様は Android 1.0 以降でサポートされています。
  • OpenGL ES 2.0 - この API 仕様は Android 2.2(API レベル 8)以降でサポートされています。
  • OpenGL ES 3.0 - この API 仕様は Android 4.3(API レベル 18)以降でサポートされています。
  • OpenGL ES 3.1 - この API 仕様は Android 5.0(API レベル 21)以降でサポートされています。

注意: デバイスで OpenGL ES 3.0 API をサポートするには、デバイスの製造元が提供する、このグラフィック パイプラインの実装が必要です。Android 4.3 以前を搭載しているデバイスでは、OpenGL ES 3.0 API がサポートされていない可能性があります。サポートされている OpenGL ES のバージョンを実行時に確認する方法については、OpenGL ES バージョンの確認をご覧ください。

注: Android フレームワークが提供する特定の API は J2ME JSR239 OpenGL ES API に似ていますが、まったく同じというわけではありません。J2ME JSR239 仕様に慣れている場合、バリエーションの違いに注意してください。

参照

基本情報

Android は、フレームワーク API と Native Development Kit(NDK)の両方を通じて OpenGL をサポートしています。このトピックでは、Android フレームワーク インターフェースに焦点を当てて説明します。NDK の詳細については、Android NDK をご覧ください。

Android フレームワークには、OpenGL ES API でグラフィックを作成し操作するための基本クラスが 2 つあります(GLSurfaceViewGLSurfaceView.Renderer)。Android アプリケーションで OpenGL を使用することを目的としている場合は、アクティビティでこれらのクラスを実装する方法を理解することが最初の目標です。

GLSurfaceView
このクラスは、OpenGL API 呼び出しを使用してオブジェクトを描画または操作できる View です。機能面で SurfaceView に似ています。このクラスを使用するには、GLSurfaceView のインスタンスを作成し、Renderer を追加します。ただし、タッチ スクリーン イベントをキャプチャする場合は、OpenGL トレーニング レッスンのタップイベントへの応答に示すように、GLSurfaceView クラスを拡張してタッチリスナーを実装する必要があります。
GLSurfaceView.Renderer
このインターフェースは、GLSurfaceView でグラフィックを描画するために必要なメソッドを定義します。このインターフェースの実装を別個のクラスとして指定し、GLSurfaceView.setRenderer() を使用して GLSurfaceView インスタンスにアタッチする必要があります。

GLSurfaceView.Renderer インターフェースでは、次のメソッドを実装する必要があります。

  • onSurfaceCreated(): GLSurfaceView の作成時、システムがこのメソッドを 1 回呼び出します。このメソッドは、OpenGL 環境パラメータの設定や OpenGL グラフィック オブジェクトの初期化など、1 回だけ実行する必要があるアクションを実行するために使用します。
  • onDrawFrame(): GLSurfaceView が再描画されるごとに、システムがこのメソッドを呼び出します。このメソッドは、グラフィック オブジェクトの描画と再描画の主要な実行ポイントとして使用します。
  • onSurfaceChanged(): GLSurfaceView のサイズの変更またはデバイス画面の向きの変更を含め、GLSurfaceView ジオメトリが変更されると、システムがこのメソッドを呼び出します。たとえば、デバイスが縦向きから横向きに変更されたときに、このメソッドが呼び出されます。このメソッドは、GLSurfaceView コンテナの変更に対応するために使用します。

OpenGL ES パッケージ

GLSurfaceViewGLSurfaceView.Renderer を使用して OpenGL ES のコンテナビューを設定したら、次のクラスを使用して OpenGL API の呼び出しを開始できます。

  • OpenGL ES 1.0 / 1.1 API パッケージ
  • OpenGL ES 2.0 API クラス
    • android.opengl.GLES20 - このパッケージは OpenGL ES 2.0 へのインターフェースを提供し、Android 2.2(API レベル 8)以降で使用できます。
  • OpenGL ES 3.0/3.1 API パッケージ
    • android.opengl - このパッケージは OpenGL ES 3.0/3.1 クラスへのインターフェースを提供します。バージョン 3.0 は Android 4.3(API レベル 18)以降で使用できます。バージョン 3.1 は Android 5.0(API レベル 21)以降で使用できます。

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.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> を使用して、アプリケーションがサポートしている形式をマニフェスト ファイルで宣言する必要があります。利用可能なテクスチャ圧縮形式の詳細については、テクスチャ圧縮のサポートをご覧ください。

    テクスチャ圧縮の要件をマニフェストで宣言すると、宣言した圧縮タイプの少なくとも 1 つをサポートしていないデバイスを持つユーザーから、アプリケーションが見えなくなります。テクスチャ圧縮に対する Google Play のフィルタリング方法の詳細については、<supports-gl-texture> ドキュメントのGoogle Play とテクスチャ圧縮のフィルタリング セクションをご覧ください。

描画オブジェクトの座標のマッピング

Android デバイスでグラフィックを表示する際の基本的な問題の 1 つは、画面のサイズと形状が異なる可能性があることです。OpenGL は正方形の均一な座標系を前提としており、デフォルトでは、通常は正方形でない画面上に、完全な正方形であるかのようにこれらの座標を描画します。

図 1. 標準的な Android デバイスの画面上にマッピングされたデフォルトの OpenGL 座標系(右)。

上の図は、OpenGL フレームで想定される均一な座標系(左)と、これらの座標が実際に横向きの標準的なデバイス画面にどのようにマッピングされるか(右)を示しています。この問題を解決するには、OpenGL 投影モードとカメラビューを適用して座標を変換し、グラフィック オブジェクトの縦横比が正しくなるようにします。

投影ビューとカメラビューを適用するには、投影行列とカメラビュー行列を作成して、OpenGL レンダリング パイプラインに適用します。投影行列は、Android デバイスの画面に正しくマッピングされるように、グラフィックの座標を再計算します。カメラビュー行列は、特定の視点からオブジェクトをレンダリングする変換を作成します。

OpenGL ES 1.0 での投影とカメラビュー

ES 1.0 API では、各行列を作成して OpenGL 環境に追加しすることで、投影とカメラビューを適用します。

  1. 投影行列 - 正しい縦横比で描画されるようにオブジェクトの座標を再計算するために、デバイス画面のジオメトリを使用して投影行列を作成します。次のコード例は、GLSurfaceView.Renderer 実装の onSurfaceChanged() メソッドを変更して、画面のアスペクト比に基づいて投影行列を作成し、それを 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. カメラ変換行列 - 投影行列を使用して座標系を調整したら、カメラビューも適用する必要があります。次のコード例は、GLSurfaceView.Renderer 実装の onDrawFrame() メソッドを変更してモデルビューを適用し、GLU.gluLookAt() ユーティリティを使用して、カメラ位置をシミュレートするビュー変換を作成する方法を示しています。

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

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 で、図形の面とは、3 次元空間内の 3 つ以上の点によって定義される面のことです。3 つ以上の 3 次元点のセット(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 バージョンとデバイスの互換性

OpenGL ES 1.0 と 1.1 の API 仕様は、Android 1.0 以降でサポートされています。Android 2.2(API レベル 8)以降、このフレームワークは OpenGL ES 2.0 API 仕様をサポートしています。OpenGL ES 2.0 はほとんどの Android デバイスでサポートされており、OpenGL で開発される新しいアプリケーションに推奨されています。OpenGL ES 3.0 は、OpenGL ES 3.0 API の実装を提供するデバイスの、Android 4.3(API レベル 18)以降でサポートされています。特定のバージョンの OpenGL ES をサポートする Android 搭載デバイスの相対数については、OpenGL ES バージョン ダッシュボードをご覧ください。

OpenGL ES 1.0 / 1.1 API を使用したグラフィック プログラミングは、2.0 以降のバージョンを使用した場合とは大きく異なります。API の 1.x バージョンには便利なメソッドと固定グラフィック パイプラインがあり、OpenGL ES 2.0 と 3.0 API では OpenGL シェーダーを使用してパイプラインを直接的に制御できます。グラフィックの要件を慎重に検討し、アプリケーションに最適な 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 フレームワークは、ETC1Util ユーティリティ クラスと etc1tool 圧縮ツール(<sdk>/tools/ の Android SDK にあります)を含め、ETC1 圧縮形式を標準機能としてサポートしています。テクスチャ圧縮を使用する Android アプリケーションの例については、Android SDK(<sdk>/samples/<version>/ApiDemos/src/com/example/android/apis/graphics/)の CompressedTextureActivity コードサンプルをご覧ください。

注意: ETC1 形式はほとんどの Android デバイスでサポートされていますが、利用できるという保証はありません。ETC1 形式がデバイスでサポートされているかどうかを確認するには、ETC1Util.isETC1Supported() メソッドを呼び出します。

注: ETC1 テクスチャ圧縮形式は、透明度(アルファ チャンネル)を持つテクスチャには対応していません。アプリケーションで透明度を持つテクスチャが必要な場合は、対象のデバイスで利用できる他のテクスチャ圧縮形式を調べる必要があります。

ETC2/EAC テクスチャ圧縮形式は、OpenGL ES 3.0 API を使用する場合に利用できることが保証されています。このテクスチャ形式は、高画質で優れた圧縮率を実現します。また、透明度(アルファ チャンネル)もサポートしています。

ETC 形式以外にも、Android デバイスは、GPU チップセットと OpenGL 実装に基づいてさまざまなテクスチャ圧縮をサポートしています。対象とするデバイスでのテクスチャ圧縮のサポート状況を調べて、アプリケーションでサポートする圧縮タイプを決定する必要があります。特定のデバイスでサポートされているテクスチャ形式を判断するには、デバイスに照会して、デバイスでサポートされているテクスチャ圧縮形式(と、他の OpenGL 機能)を識別する OpenGL 拡張機能名を確認する必要があります。一般的にサポートされているテクスチャ圧縮形式は次のとおりです。

  • ATITC(ATC) - ATI テクスチャ圧縮(ATITC または ATC)はさまざまなデバイスで利用でき、アルファ チャンネルの有無にかかわらず RGB テクスチャの固定レート圧縮をサポートします。この形式は、次のように複数の OpenGL 拡張機能名で表される可能性があります。
    • GL_AMD_compressed_ATC_texture
    • GL_ATI_texture_compression_atitc
  • PVRTC - PowerVR テクスチャ圧縮(PVRTC)はさまざまなデバイスで利用でき、アルファ チャンネルの有無にかかわらずピクセルあたり 2 ビットと 4 ビットのテクスチャをサポートします。この形式は、次の OpenGL 拡張機能名で表されます。
    • GL_IMG_texture_compression_pvrtc
  • S3TC(DXTn/DXTC) - S3 テクスチャ圧縮(S3TC)には形式のバリエーションが複数あり(DXT1 から DXT5)、あまり広く利用できません。この形式は、4 ビットのアルファ チャンネルまたは 8 ビットのアルファ チャンネルを持つ RGB テクスチャをサポートします。これらの形式は、次の OpenGL 拡張機能名で表されます。
    • GL_EXT_texture_compression_s3tc
    一部のデバイスでは、DXT1 形式のバリエーションのみがサポートされています。この限定的なサポートは、次の OpenGL 拡張機能名で表されます。
    • GL_EXT_texture_compression_dxt1
  • 3DC - 3DC テクスチャ圧縮(3DC)は、アルファ チャンネルを持つ RGB テクスチャをサポートする、あまり広く利用できない形式です。この形式は、次の OpenGL 拡張機能名で表されます。
    • GL_AMD_compressed_3DC_texture

警告: これらのテクスチャ圧縮形式は、すべてのデバイスでサポートされているわけではありません。これらの形式のサポート状況は、製造元またはデバイスによって異なる場合があります。特定のデバイスでのテクスチャ圧縮形式を決定する方法については、次のセクションをご覧ください。

注: アプリケーションでサポートするテクスチャ圧縮形式を決定したら、<supports-gl-texture> を使用してマニフェストで宣言します。この宣言を使用すると Google Play などの外部サービスによるフィルタリングが有効になり、アプリに必要な形式をサポートするデバイスにのみ、アプリがインストールされます。詳細については、OpenGL マニフェストの宣言をご覧ください。

OpenGL 拡張機能の決定

OpenGL の実装は、サポートされている OpenGL ES API の拡張機能に関して、Android デバイスによって異なります。これらの拡張機能にはテクスチャ圧縮が含まれますが、通常は 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 要件を次のように宣言します。

    <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 レジストリにあるページをご覧ください。

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 のバージョンを確認する必要があります。これには次の 2 つの方法があります。

  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() メソッドが null を返す場合、コードは代わりに 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 1.0 API バージョン(と 1.1 拡張機能)、バージョン 2.0、バージョン 3.0 はすべて、3D ゲーム、可視化、ユーザー インターフェースを作成するための高性能なグラフィック インターフェースを提供します。OpenGL ES 2.0 と 3.0 のグラフィック プログラミングはほぼ同じです。バージョン 3.0 は 2.0 API のスーパーセットであり、追加の機能を備えています。OpenGL ES 1.0 / 1.1 API のプログラミングは、OpenGL ES 2.0 と 3.0 の場合とは大きく異なるため、デベロッパーはこれらの API を使用して開発を始める前に、次の要素を慎重に考慮する必要があります。

  • パフォーマンス - 一般に、OpenGL ES 2.0 と 3.0 は、ES 1.0 / 1.1 API よりも高速なグラフィック パフォーマンスを提供します。ただし、ハードウェアの製造元による OpenGL ES グラフィック パイプラインの実装の違いから、OpenGL アプリケーションを実行している Android デバイスによってパフォーマンスの違いが生じる場合があります。
  • デバイスの互換性 - デベロッパーは、ユーザーが利用できるデバイスの種類、Android のバージョン、OpenGL ES のバージョンを考慮する必要があります。OpenGL の互換性の詳細については、OpenGL バージョンとデバイスの互換性のセクションをご覧ください。
  • コーディングの利便性 - OpenGL ES 1.0 / 1.1 API は、OpenGL ES 2.0 または 3.0 API では利用できない固定関数パイプラインと便利な関数を提供します。OpenGL ES を初めて使用するデベロッパーは、バージョン 1.0 / 1.1 のコーディングがより高速かつ便利であることに気付くことでしょう。
  • グラフィックの制御 - OpenGL ES 2.0 と 3.0 API は、シェーダーを使用して完全にプログラム可能なパイプラインを提供することで、より高度な制御を提供します。グラフィック処理パイプラインをより直接的に制御することで、デベロッパーは、1.0 / 1.1 API を使用して生成するのは難しい効果を作成できます。
  • テクスチャのサポート - OpenGL ES 3.0 API は、透明度をサポートする ETC2 圧縮形式を利用できるため、テクスチャ圧縮に最適です。通常 1.x と 2.0 の API 実装には ETC1 のサポートが含まれていますが、このテクスチャ形式は透明度をサポートしていないため、通常は対象とするデバイスでサポートされている他の圧縮形式でリソースを提供する必要があります。詳細については、テクスチャ圧縮のサポートをご覧ください。

パフォーマンス、互換性、利便性、制御などの要素が判断に影響する場合がありますが、優れたユーザー エクスペリエンスになるように考慮して OpenGL API のバージョンを選択する必要があります。