OpenGL ES概述

OpenGL ES

Android包含支持高性能2D和3D圖形繪制開源庫(OpenGL),尤其是OpenGL ES API. OpenGL是一個跨平臺的圖形API,它為硬件處理3D圖形指定了一個標準的軟件接口。OpenGL ES是一種用于嵌入式設備的OpenGL規(guī)范。Android支持多個版本的OpenGL ES接口:

  • OpenGL ES 1.0和1.1 - Android 1.0或更高版本所支持的API規(guī)范。
  • OpenGL ES 2.0 - Android 2.2(API 8)或更高版本所支持的API規(guī)范。
  • OpenGL ES 3.0 - Android 4.3(API 18)或更高版本所支持的API規(guī)范。
  • OpenGL ES 3.1 - Android 5.0(API 21)或更高版本所支持的API規(guī)范。

注意:設備支持OpenGL ES 3.0 API需要設備制造商提供圖形管道的實現(xiàn)。有些運行Android 4.3或更高版本的設備可能不支持OpenGL ES 3.0 API。獲取關于運行時檢查設備所支持的OpenGL版本的信息,請繼續(xù)閱讀下文。

基礎(The Basics)

Android框架API和原生開發(fā)工具(NDK)都支持OpenGL。本文重點講Android框架關于OpenGL的接口。獲取更多關于NDK的信息,參看Android NDK

Android framework有兩個基礎類可以讓你通過OpenGL ES API創(chuàng)建和操作圖形:GLSurfaceView 和GLSurfaceView.Renderer。如果想在你的Android應用程序中使用OpenGL,了解如何在Activity中實現(xiàn)這些類是你的第一個目標。

GLSurfaceView

這是一個可以使用OpenGL API方法來繪制和操作對象的View,功能類似SurfaceView。你可以通過創(chuàng)建GLSurfaceView的實例并向其添加渲染器Renderer來使用這個類。但是,如果你想捕獲觸屏事件,你得將繼承GLSurfaceView類并實現(xiàn)觸摸監(jiān)聽器。OpenGL實現(xiàn)監(jiān)聽器的示例,Responding to Touch Events.

GLSurfaceView.Renderer

這個接口定義了在GLSurfaceView上繪圖所需的方法。你得在一個單獨的類提供接口的實現(xiàn),并調用GLSurfaceView.setRenderer()將其綁定到GLSurfaceView實例中。
GLSurfaceView.Renderer接口需要實現(xiàn)如下方法:

  • onSurfaceCreated():當創(chuàng)建GLSurfaceView時,系統(tǒng)立即調用這個方法。通過這個方法實現(xiàn)一些需要馬上處理的事,如設置OpenGL的環(huán)境參數(shù)或初始化OpenGL繪圖對象。

  • onDrawFrame(): GLSurfaceView每次重繪時系統(tǒng)就調用這方法。使用此方法作為繪制(和重繪)圖形對象的主要執(zhí)行步驟。

  • onSurfaceChanged():當GLSurfaceView發(fā)生幾何改變時調用這方法,包括GLSurfaceView的大小或設備屏幕方向的改變。比如,當設備從豎直方向變成水平方向時會調用這方法。在GLSurfaceView容器中使用該方法來響應改變。

OpenGL ES包(OpenGL ES packages)

一旦使用GLSurfaceView和GLSurfaceView.Renderer為OpenGL ES創(chuàng)建了視圖容器,你就可以開始使用如下的類調用OpenGL API:

  • OpenGL ES 1.0/1.1 API Packages

    • android.opengl - 這個包為OpenGL ES1.0/1.1提供了一個比javax.microedition.khronos包更好性能靜態(tài)接口類。
      • GLES10
      • GLES10Ext
      • GLES11
      • GLES11Ext
    • javax.microedition.khronos.opengles - 這個類提供了OpenGL ES 1.0/1.1的標準實現(xiàn)。
      • GL10
      • GL10Ext
      • GL11
      • GL11Ext
      • GL11ExtensionPack
  • OpenGL ES 2.0 API Class

    • android.opengl.GLES20 - 這個包為OpenGL ES 2.0提供了一些接口,Android 2.2(API 8)或更高版支持。
  • OpenGL ES 3.0/3.1 API Packages

    • android.opengl - 這個包為OpenGL ES 3.0/3.1提供了一些接口類,其中3.0版本從Android4.3(API 18)開始支持。3.1版本從Android 5.0(API 21)開始支持。

聲明OpenGL的條件(Declaring OpenGL Requirements)

如果你想在應用程序中使用并非所有設備都有效的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" />

為OpenGL ES 3.0

<!-- Tell the system this app requires OpenGL ES 3.1. -->
<uses-feature android:glEsVersion="0x00030001" 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的實現(xiàn)可以更靈活。通過在清單文件中聲明OpenGL ES 2.0 API條件后,你可以默認使用這個API版本,同時可以在程序運行時檢查設備是否支持Open GL ES 3.x的特性,如果支持就使用OpenGL ES 3.0的特性。

  • 紋理壓縮條件 - 如果你的應用程序使用了紋理壓縮格式,你得在清單文件中使用<supports-gl-texture>supports-gl-texture>聲明應用程序所支持壓縮的格式。獲取更多關于紋理壓縮格式的信息,請查看本篇下文的Texture compression support.

繪制對象的映射坐標(Mapping Coordinates For Drawn Objects)

Android設備顯示圖形的一個問題是屏幕會顯示不同的尺寸和形狀。OpenGL假設一個正方形在默認的統(tǒng)一坐標系中,將正方形的坐標繪制到典型的非方形屏幕上,并假使它是完全正方形的。

這里寫圖片描述

圖1.左邊是默認的OpenGL坐標系統(tǒng),右邊用來映射的典型設備屏幕。

上圖左邊顯示OpenGL假設的統(tǒng)一坐標,右邊顯示這些坐標如何真正的映射到水平放置的設備上。為了解決這個問題,你可以應用OpenGL的投影模式和相機視圖轉換坐標來使你的圖形對象在任何顯示器上都具有正確的比例。

為了應用投影和相機視圖,你可以創(chuàng)建一個投影矩陣和相機矩陣,并將它們應用到OpenGL的渲染管道中。投影矩陣通過重新計算圖形的坐標來實現(xiàn)Android屏幕設備的正確顯示。相機視圖矩陣創(chuàng)建一個轉換器并根據(jù)眼睛所看到的位置渲染對象。

OpenGL ES 1.0 的投影和相機視圖(Projection and camera view in OpenGL ES 1.0)

在ES 1.0 API中,你可以通過創(chuàng)建投影矩陣和相機視圖矩陣并將它們添加到OpenGL環(huán)境中來應用投影和相機。

1.投影矩陣 - 使用設備屏幕的幾何形狀創(chuàng)建投影矩陣,以便重新計算并以正確比例繪制對象坐標。下面的示例代碼展示如何修改GLSurfaceView.Renderer實現(xiàn)的onSurfaceChanged()方法,實現(xiàn)根據(jù)屏幕的寬高比創(chuàng)建投影矩陣,并將其應用于OpenGL渲染環(huán)境中。

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.相機轉換矩陣 - 一旦使用投影矩陣調整好坐標系統(tǒng)后,你還得應用相機視圖。下面的示例代碼展示如何修改 GLSurfaceView.Renderer實現(xiàn)的onDrawFrame()方法,以便應用一個視圖模型和使用GLU.gluLookAt()工具創(chuàng)建模擬相機位置的視圖轉換器。

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.添加矩陣到頂點著色器 - 為投影矩陣創(chuàng)建一個作為著色器位置乘數(shù)的變量。下面是頂點著色器的示例代碼,其中包括允許讓你應用投影和相機視圖矩陣到使用著色器的對象坐標上的uMVPMatrix成員。

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

Note:上面的示例在定點著色器中定義了一個單一的轉換矩陣成員,可以將其結合投影矩陣和相機視圖矩陣應用。取決于你的應用程序所需,你可能想單獨地在頂點著色器中聲明投影矩陣和相機視圖矩陣成員,以便可以獨立的改變它們。

2.訪問著色器矩陣 - 在頂點著色器創(chuàng)建一個hook(一個變量)之后,你就可以訪問這個變量來應用投影和相機視圖矩陣。下面的示例代碼展示如何修改 GLSurfaceView.Renderer實現(xiàn)的onSurfaceCreated()方法,來訪問如上定義在頂點著色器中矩陣變量。

public void onSurfaceCreated(GL10 unused, EGLConfig config) {
    ...
    muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
    ...
}

3.創(chuàng)建投影和相機視圖矩陣 - 生成投影和應用于圖形對象的視圖矩陣。下面的示例代碼展示如何修改 GLSurfaceView.Renderer實現(xiàn)的onSurfaceCreated()方法和onSurfaceChanged()方法,實現(xiàn)創(chuàng)建相機視圖矩陣和基于設備屏幕寬高比的投影矩陣。

public void onSurfaceCreated(GL10 unused, EGLConfig config) {
    ...
    // Create a camera view matrix
    Matrix.setLookAtM(mVMatrix, 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(mProjMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
}

4.應用投影和相機視圖矩陣 - 為了應用投影和相機視圖轉換,將兩個矩陣相乘,然后將它們設置給頂點著色器。下面的示例代碼展示如何修GLSurfaceView.Renderer實現(xiàn)的onDrawFrame()方法,來實現(xiàn)結合投影矩陣和上述代碼所創(chuàng)建的相機視圖,然后將它們應用于OpenGL渲染的圖形對象。

public void onDrawFrame(GL10 unused) {
    ...
    // Combine the projection and camera view matrices
    Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mVMatrix, 0);

    // Apply the combined projection and camera view transformations
    GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMVPMatrix, 0);

    // Draw objects
    ...
}

獲取OpenGL ES 2.0應用投影和相機視圖的完整代碼,參看: Displaying Graphics with OpenGL ES類。

形狀面和環(huán)繞(Shape Faces and Winding)

在OpenGL中,形狀的表面由三維空間的三個點或以上定義而成。一組三個或以上的三維點(在OpenGL中稱為頂點)具有正面和背面。如何知道哪一面是正面哪一面是背面呢?這是一個不錯的問題。答案不得不用環(huán)繞或你定義形狀的點的方向來回答。

這里寫圖片描述

圖1.圖文的坐標列表按逆時針繪制。

在例子中,三角形的點被定義在一個逆時針方向繪制的順序中。繪制這些坐標的順序定義了形狀的環(huán)繞方向。在OpenGL中,默認逆時針繪制的面是正面。圖1你所看到的OpenGL渲染出來的形狀是正面,其他面是背面。

為什么了解哪一面是正面如此重要呢?這個回答得用OpenGL常用的特性--面剔除來解釋。面剔除是OpenGL環(huán)境允許你忽略圖形背面(不需要計算和繪制背面)渲染管道,節(jié)省時間、內存還有處理周期的選項。

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

如果你在不知道正面背面的情況下就使用面剔除的特性,OpenGL圖形看起來可能會變形,也可能全部不顯示。所以,總是以逆時針繪制順序定義你的OpenGL形狀的坐標。

Note:將OpenGL環(huán)境的順時針方向作為正面也是存在的,但是這么做會導致需要額外更多的代碼,以及你在需要尋求幫助的時候會讓你搞混OpenGL的開發(fā)。所以不推薦這么做。

OpenGL的版本和設備兼容性

Android從1.0就支持OpenGL ES 1.0和1.1 API規(guī)范。從Android 2.2(API 8)開始,框架支持OpenGL ES 2.0 API規(guī)范。OpenGL ES 2.0支持大多數(shù)Android設備,所以比較推薦使用這版本開發(fā)應用程序。Android 4.3(API 18)或更高版本支持OpenGL ES 3.0 API。獲取關于Android設備支持指定版本的OpenGL ES的信息,請參閱OpenGL ES版本儀表板

使用OpenGL ES 1.0/1.1 API與使用2.0或更高版本API開發(fā)圖形程序是非常不一樣的。1.x版本的API有更多簡便的方法和穩(wěn)定的圖形管道,而OpenGL ES 2.0和3.0 API提供更多使用OpenGL取色器直接控制管道的方法。你應該仔細考慮圖形的需求,然后選擇最適合應用程序的API版本。

OpenGL ES 3.0 API 相比2.0 API,提供了更多特性以及更好的性能表現(xiàn),同時也向后兼容。這意味著你可以編寫針對OpenGL ES 2.0的應用程序,并有條件地包括OpenGL ES 3.0圖形特性(如果可用的話)。

支持紋理壓縮(Texture compression support)

紋理壓縮通過減少內存的使用和提高內存帶寬使用效益來提升OpenGL應用程序的性能。Android框架提供ETC1壓縮格式作為標準的特性,包括ETC1Util工具類和etc1tool壓縮工具(存放于本地Android SDK的tool/目錄下)。

注意:ETC1格式雖然是大多數(shù)Android設備支持的格式,但是并不能保證它是有效的。通過調用 ETC1Util.isETC1Supported()方法來檢查應用程序是否支持ETC1.

Note:ETC1紋理壓縮格式不支持透明度紋理的壓縮。如果你的應用程序有用到透明度的部分,你應該檢查設備是否有其他的紋理壓縮格式。

ETC2/EAC紋理壓縮格式在OpenGL ES 3.0 API中就能保證它的有效性。這種格式提供更高視覺質量的壓縮比率,同時也支持壓縮透明度。

在ETC之后,Android設備基于GPU芯片和OpenGL的實現(xiàn)實現(xiàn)了支持各種紋理壓縮格式。你應該檢索設備所支持的紋理壓縮類型以便決定選出應用程序應該使用的壓縮類型。為了決定你設備所支持的格式,您必須查詢設備并查看OpenGL擴展名,這些名稱標識設備支持哪些紋理壓縮格式(以及其他OpenGL功能)。一些常見支持的紋理壓縮格式如下:

  • ATITC (ATC) - ATI紋理壓縮 (ATITC or ATC) 普遍設備有效,在沒有透明度通道的條件下,支持穩(wěn)定的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 to DXT5) ,不太普遍. 支持有透明度管道的4位或8位紋理壓縮。OpenGL擴展名表示如下:

  • GL_EXT_texture_compression_s3tc

    一些設備只支持 DXT1格式,OpenGL擴展名表示如下:

  • GL_EXT_texture_compression_dxt1

3DC - 3DC 紋理壓縮 (3DC) 不太普遍,支持有透明度管道的RGB紋理. OpenGL擴展名表示如下:

  • GL_AMD_compressed_3DC_texture

注意:紋理壓縮格式并非所有設備都支持。支持這些壓縮格式因手機廠商而異。獲取關于如何在特殊設備上決定使用哪種紋理壓縮格式,請繼續(xù)閱讀下文。

Note:一旦決定了你的應用程序所要支持的紋理壓縮格式,確保在清單文件中使用<<supports-gl-texture>supports-gl-texture>來聲明它們。使用這些聲明并通過外部服務如Google Play來啟動過濾工作,以便app只安裝到你聲明且被支持的設備上。

決定OpenGL的擴展名(Determing OpenGL entensions)

OpenGL的實現(xiàn)在Android設備上支持的OpenGL ES API的擴展方面有所不同。這些擴展包含紋理壓縮,也包含其他OpenGL特性設置的擴展。

要確定特定設備上支持什么紋理壓縮格式和其他OpenGL擴展:

1.在目標設備上運行以下代碼以確定支持哪些紋理壓縮格式:

String extensions = javax.microedition.khronos.opengles.GL10.glGetString(
        GL10.GL_EXTENSIONS);

注意:這個返回結果因設備而異。你得運行在多種設備上以便獲取哪種壓縮類型是最常見的。

2.回顧方法返回的結果以便確定設備支持哪種OpenGL擴展名。

Android擴展包(AEP)

AEP確保你的應用程序支持一套標準化的OpenGL擴展以及一套上下文所描述的OpenGL 3.1規(guī)范。建議將一些功能一致的擴展集成到一起來跨平臺使用,這樣可以讓開發(fā)者充分使用移動設備的GPU。

AEP還改進了對圖像,著色器存儲緩沖區(qū)和片段著色器中的原子計數(shù)器的支持。

為了app使用AEP,你得在應用程序的清單文件添加一些AEP需要的條件。另外,還要對應的平臺版本支持對它的使用。

清單文件所需對AEP的聲明如下:

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

將FEATURE_OPENGLES_EXTENSION_PACK作為參數(shù)傳入hasSystemFeature(String)方法,來確認平臺是否支持AEP。如下代碼片段的演示:

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不能使用,再繼續(xù)使用2.0的API。

在使用一個更高版本的OpenGL ES特性之前,你得在應用程序上檢查這個版本的API是否在這個設備上有效??梢杂袃煞N方式檢查:

1.嘗試創(chuàng)建一個高版本的OpenGL ES的上下文(EGLContext)然后檢查結果。

2.創(chuàng)建最低支持的OpenGL上下文并檢查版本的值。

下面的示例代碼通過創(chuàng)建EGLContext并檢查結果來檢查是否3.0 OpenGL ES版本有效。

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,你應該在代碼中創(chuàng)建OpenGL ES 2.0的上下文來替代3.0然后使用這個API。

下面的代碼示例如何先通過創(chuàng)建最低支持的上下文來檢測OpenGL ES版本,然后檢查版本的字符串:

// Create a minimum supported OpenGL ES context, then check:
String version = javax.microedition.khronos.opengles.GL10.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.

使用這種方式,如果你發(fā)現(xiàn)設備支持更高版本的API,你得先銷毀低版本的OpenGL ES的上下文,然后使用所支持的高版本的API創(chuàng)建一個新的上下文。

選擇OpenGL API版本(Choosing an OpenGL API Version)

OpenGL ES 1.0 API 版本(以及1.1的擴展),2.0版本以及3.0版本都為創(chuàng)建3D游戲,可視化UI提供了高性能的圖形繪制接口。OpenGL ES 2.0和3.0的圖形編程有很大的相似之處,3.0版本其實就是多了一些特性的2.0 版本集合庫。OpenGL ES1.0/1.1的編程與OpenGL ES 2.0和3.0就相當?shù)牟煌?,所以開發(fā)者在開始使用這些APIs開發(fā)之前應該仔細認真考慮如下的因素:

  • 性能- 通常OpenGL ES 2.0和3.0都提供了比ES 1.0/1.1更快的圖形性能。然而,由于不同的硬件廠商對OpenGL ES圖形管道的實現(xiàn),性能也因你運行OpenGL應用程序的Android設備而異。

  • 設備兼容 - 開發(fā)者應該考慮設備的類型,Android的版本以及OpenGL ES的版本是否在他們的目標用戶上有效。獲取更多關于OpenGL跨設備兼容的內容,可參看上文。

  • 代碼的簡便性 - OpenGL ES 1.0/1.1 API提供了穩(wěn)定的功能管道和便利的功能,不過這些功能在OpenGL ES 2.0、3.0 APIs已經(jīng)失效了。對OpenGL ES新手的開發(fā)人員可能會發(fā)現(xiàn)版本1.0 / 1.1的編碼更快,更方便。

  • 圖形控制 - OpenGL ES 2.0和3.0 API通過使用著色器提供完全可編程的管道來提供更高程度的控制。通過更直接地控制圖形處理管道,開發(fā)人員可以創(chuàng)建1.0 / 1.1 API很難生成的效果。

  • 紋理支持 - OpenGL ES 3.0 API 擁有最好的紋理壓縮支持,因為它保證了支持透明度的ETC2壓縮格式的有效性。1.x和2.0 API的實現(xiàn)通常包含ETC1的支持,然而,紋理格式不支持透明度,所以你得針對設備提供的其他壓縮格式資源。

當性能,兼容性,簡便性,圖形控制以及其他因素影響了你的選擇,你就應該基于什么才是最好的用戶體驗的思想去選擇一個OpenGL API版本。

原文鏈接:https://developer.android.google.cn/guide/topics/graphics/opengl.html

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容