概要
Android包含對開放圖形庫(OpenGL®),特別是OpenGL ES API的高性能2D和3D圖形的支持。OpenGL是一個跨平臺的圖形API,它爲3D圖形處理硬件指定了一個標準的軟件接口。OpenGL ES是適用於嵌入式設備的OpenGL規範。Android支持幾種版本的OpenGL ES API:
- OpenGL ES 1.0和1.1 - 此API規範受Android 1.0及更高版本的支持。
- 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.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創建和操作圖形:GLSurfaceView和 GLSurfaceView.Renderer。如果您的目標是在Android應用程序中使用OpenGL,那麼瞭解如何在活動中實現這些類應該是您的第一個目標。
GLSurfaceView
這個類是一個View可以使用OpenGL API調用來繪製和操作對象的地方,並且在功能上類似於a SurfaceView。
你可以通過創建一個實例GLSurfaceView並添加 Renderer它來使用這個類。但是,如果要捕獲觸摸屏事件,則應擴展GLSurfaceView該類以實現觸摸偵聽器,如OpenGL培訓課程中的“ 響應觸摸事件”所示【原】。
GLSurfaceView.Renderer
該接口定義了在a中繪製圖形所需的方法GLSurfaceView。您必須將此接口的實現作爲單獨的類提供,並GLSurfaceView使用它將其附加到您的實例 GLSurfaceView.setRenderer()。
該GLSurfaceView.Renderer界面要求您實施以下方法:
- onSurfaceCreated():系統在創建時調用此方法一次GLSurfaceView。使用此方法執行僅需要發生一次的操作,例如設置OpenGL環境參數或初始化OpenGL圖形對象。
- onDrawFrame():系統在每次重繪時調用此方法GLSurfaceView。使用此方法作爲繪製(和重新繪製)圖形對象的主要執行點。
- onSurfaceChanged():系統在GLSurfaceView幾何更改時調用此方法,包括GLSurfaceView設備屏幕的大小或方向的更改。例如,當設備從縱向變爲橫向時,系統調用此方法。使用此方法來響應GLSurfaceView容器中的更改。
OpenGL ES軟件包
一旦使用GLSurfaceView和建立了OpenGL ES的容器視圖GLSurfaceView.Renderer,就可以使用以下類來調用OpenGL API:
-
OpenGL ES 1.0 / 1.1 API包
-
android.opengl- 這個包爲OpenGL ES 1.0 / 1.1類提供了一個靜態接口,並且比javax.microedition.khronos包接口有更好的性能。
GLES10 GLES10Ext GLES11 GLES11Ext
-
javax.microedition.khronos.opengles - 這個包提供了OpenGL ES 1.0 / 1.1的標準實現。
GL10 GL10Ext GL11 GL11Ext GL11ExtensionPack
-
-
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類的接口。從Android 4.3(API級別18)開始,可以使用3.0版本。從Android 5.0(API級別21)開始,可以使用版本3.1。
GLES30 GLES31 GLES31Ext(Android擴展包)
-
如果您想立即開始使用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>。有關可用紋理壓縮格式的更多信息,請參閱紋理壓縮支持【原】。
在您的清單中聲明紋理壓縮要求時,會將您的應用程序從不支持至少一種聲明壓縮類型的設備的用戶隱藏起來。有關Google Play過濾如何處理紋理壓縮的更多信息,請參閱文檔中的 Google Play和紋理壓縮過濾【原】部分<supports-gl-texture>。
繪製繪製對象的座標
在Android設備上顯示圖形的基本問題之一是其屏幕可能會在大小和形狀上有所不同。OpenGL假定一個正方形的統一座標系統,並且默認情況下,將這些座標愉快地繪製到典型的非方形屏幕上,就好像它是完全正方形的。
圖1.映射到典型Android設備屏幕(右側)的默認OpenGL座標系(左側)。
上圖顯示了左側的OpenGL框架假定的統一座標系統,以及這些座標實際如何在右側橫向映射到典型的設備屏幕。要解決這個問題,您可以應用OpenGL投影模式和相機視圖來轉換座標,以便您的圖形對象在任何顯示器上都具有正確的比例。
爲了應用投影和相機視圖,您可以創建投影矩陣和相機視圖矩陣,並將它們應用於OpenGL渲染管線。投影矩陣重新計算圖形的座標,以便它們正確映射到Android設備屏幕。相機視圖矩陣創建一個轉換,用於從特定的眼睛位置呈現對象。
OpenGL ES 1.0中的投影和相機視圖
在ES 1.0 API中,通過創建每個矩陣然後將它們添加到OpenGL環境來應用投影和相機視圖。
-
投影矩陣 - 使用設備屏幕的幾何圖形創建投影矩陣,以重新計算對象座標,以便以正確的比例繪製它們。以下示例代碼演示瞭如何修改實現的onSurfaceChanged()方法,以GLSurfaceView.Renderer 根據屏幕的寬高比創建投影矩陣,並將其應用於OpenGL渲染環境。
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 }
-
相機變換矩陣 - 使用投影矩陣調整座標系後,還必須應用相機視圖。以下示例代碼顯示如何修改實現的onDrawFrame()方法GLSurfaceView.Renderer 以應用模型視圖並使用該 GLU.gluLookAt()實用程序創建模擬攝像頭位置的查看變形。
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中,通過首先將矩陣成員添加到圖形對象的頂點着色器中來應用投影和相機視圖。通過添加此矩陣成員,您可以生成投影和相機查看矩陣並將其應用於對象。
-
向頂點着色器添加矩陣 - 爲視圖投影矩陣創建一個變量,並將其包含爲着色器位置的乘數。在以下示例頂點着色器代碼中,包含的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";
注意:上面的示例在頂點着色器中定義了一個單獨的變換矩陣成員,您可以在其中應用組合投影矩陣和相機視圖矩陣。根據您的應用需求,您可能需要在頂點着色器中定義單獨的投影矩陣和相機查看矩陣成員,以便您可以單獨更改它們。
- 訪問着色器矩陣 - 在頂點着色器中創建鉤子以應用投影和相機視圖後,可以訪問該變量以應用投影和相機查看矩陣。以下代碼顯示如何修改實現的onSurfaceCreated()方法GLSurfaceView.Renderer以訪問上面頂點着色器中定義的矩陣變量。
public void onSurfaceCreated(GL10 unused, EGLConfig config) { muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix"); }
-
創建投影和相機查看矩陣 - 生成投影和查看矩陣以應用圖形對象。以下示例代碼顯示瞭如何修改實現的方法onSurfaceCreated()和 onSurfaceChanged()方法,以 GLSurfaceView.Renderer根據設備的屏幕高寬比創建相機視圖矩陣和投影矩陣。
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); }
-
應用投影和相機觀看矩陣 - 要應用投影和相機視圖變換,請將矩陣相乘,然後將它們設置爲頂點着色器。以下示例代碼顯示瞭如何修改實現的onDrawFrame()方法,GLSurfaceView.Renderer以便將以上代碼中創建的投影矩陣和相機視圖組合起來,然後將其應用於要由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應用投影和相機視圖的完整示例,請參閱使用OpenGL ES 類顯示圖形
形狀面對和纏繞
在OpenGL中,形狀的表面是由三維空間中的三個或更多點定義的表面。一組三個或三個以上的三維點(稱爲OpenGL中的頂點)具有正面和背面。你怎麼知道哪個面朝前,哪個面朝後?好問題。答案與卷繞有關,或者說,你定義一個形狀的點的方向。
圖1.座標列表的圖解,轉換爲逆時針繪圖順序。
在這個例子中,三角形的點按照逆時針方向的順序定義。這些座標繪製的順序決定了形狀的卷繞方向。默認情況下,在OpenGL中,逆時針繪製的面是正面。圖1中顯示的三角形被定義爲您正在查看形狀的正面(由OpenGL解釋),另一面是背面。
爲什麼重要的是要知道形狀的哪一面是正面?答案與OpenGL常用的功能有關,稱爲面部剔除。面部剔除是OpenGL環境的一個選項,它允許渲染管線忽略(不計算或繪製)形狀的背面,節省時間,內存和處理週期:
// 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規範已得到支持。從Android 2.2(API級別8)開始,該框架支持OpenGL ES 2.0 API規範。OpenGL ES 2.0被大多數Android設備支持,並且推薦用於使用OpenGL開發的新應用程序。在提供OpenGL ES 3.0 API實現的設備上,Android 4.3(API級別18)及更高級別支持OpenGL ES 3.0。有關支持給定版本的OpenGL ES的支持Android的設備的相關數量的信息,請參閱 OpenGL ES版本儀表板。
使用OpenGL ES 1.0 / 1.1 API的圖形編程與使用2.0和更高版本的顯着不同。1.x版本的API具有更多的便捷方法和固定的圖形管道,而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框架支持ETC1壓縮格式作爲標準功能,包括 ETC1Util工具類和etc1tool壓縮工具(位於Android SDK中<sdk>/tools/)。有關使用紋理壓縮的Android應用程序的示例,請參閱CompressedTextureActivityAndroid SDK(<sdk>/samples/<version>/ApiDemos/src/com/example/android/apis/graphics/)中的代碼示例。
小心:大多數Android設備都支持ETC1格式,但不保證可用。要檢查設備是否支持ETC1格式,請調用該ETC1Util.isETC1Supported()方法。
注意: ETC1紋理壓縮格式不支持具有透明度的紋理(Alpha通道)。如果您的應用程序需要具有透明度的紋理,則應調查目標設備上可用的其他紋理壓縮格式。
使用OpenGL ES 3.0 API時,ETC2 / EAC紋理壓縮格式保證可用。這種紋理格式提供出色的壓縮比和高視覺質量,格式也支持透明度(alpha通道)。
除了ETC格式之外,Android設備基於其GPU芯片組和OpenGL實現對紋理壓縮提供了各種支持。您應該調查您所針對的設備上的紋理壓縮支持,以確定應用程序應支持的壓縮類型。爲了確定給定設備支持哪些紋理格式,您必須 查詢設備並查看OpenGL擴展名,這些名稱可識別設備支持哪些紋理壓縮格式(以及其他OpenGL功能)。一些常用的紋理壓縮格式如下:
- ATITC(ATC) - ATI紋理壓縮(ATITC或ATC)適用於各種設備,並支持RGB紋理的固定速率壓縮,支持和不支持Alpha通道。這種格式可以用幾個OpenGL擴展名錶示,例如:
- GL_AMD_compressed_ATC_texture
- GL_ATI_texture_compression_atitc
- PVRTC - PowerVR紋理壓縮(PVRTC)適用於各種設備,並支持每像素2位和4位紋理(帶或不帶Alpha通道)。這種格式由以下OpenGL擴展名錶示:
- GL_IMG_texture_compression_pvrtc
- S3TC(DXT n / DXTC) - S3紋理壓縮(S3TC)具有多種格式變體(DXT1至DXT5),並且可用性較差。該格式支持4位alpha或8位alpha通道的RGB紋理。這些格式由以下OpenGL擴展名錶示:
- GL_EXT_texture_compression_s3tc
某些設備僅支持DXT1格式變體; 這個有限的支持由以下OpenGL擴展名錶示:
- GL_EXT_texture_compression_dxt1
- 3DC - 3DC紋理壓縮(3DC)是一種不太廣泛的格式,支持帶alpha通道的RGB紋理。這種格式由以下OpenGL擴展名錶示:
- GL_AMD_compressed_3DC_texture
警告:所有設備都不支持這些紋理壓縮格式。這些格式的支持可能因製造商和設備而異。有關如何確定特定設備上的紋理壓縮格式的信息,請參閱下一節。
注意:一旦決定了應用程序支持哪種紋理壓縮格式,請確保使用<supports-gl-texture> 在清單中聲明它們 。使用此聲明可以通過Google Play等外部服務進行過濾,以便您的應用僅安裝在支持您的應用所需格式的設備上。有關詳細信息,請參閱 OpenGL清單聲明。
確定OpenGL擴展
根據OpenGL ES API支持的擴展,OpenGL的實現因Android設備而異。這些擴展包括紋理壓縮,但通常還包含OpenGL功能集的其他擴展。
確定特定設備上支持哪些紋理壓縮格式和其他OpenGL擴展:
- 在目標設備上運行以下代碼以確定支持哪些紋理壓縮格式:
String extensions = javax.microedition.khronos.opengles.GL10.glGetString( GL10.GL_EXTENSIONS);
- 查看此方法的輸出以確定設備支持哪些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參數的方法。以下代碼片段顯示瞭如何執行此操作的示例:
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的版本。您可以通過以下兩種方式之一來完成此操作:
- 嘗試創建更高級別的OpenGL ES上下文(EGLContext)並檢查結果。
-
創建一個支持最小的OpenGL ES上下文並檢查版本值。
以下示例代碼演示瞭如何通過創建EGLContext並檢查結果來檢查可用的OpenGL ES版本。這個例子展示瞭如何檢查OpenGL ES 3.0版本: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版本,然後檢查版本字符串:
// 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.
使用這種方法,如果您發現設備支持更高級別的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版本。
update:2018.4.24
聯繫我
QQ:94297366
微信打賞:https://pan.baidu.com/s/1dSBXk3eFZu3mAMkw3xu9KQ
公衆號推薦: