Android OpenGL ES向導學習筆記(掃盲專用)

Android 目前支持下面幾個版本的OpenGL ES API :

OpenGL ES 1.0 和 1.1 :Android 1.0和更高的版本支持這個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.0的API需要實現設備生產廠傢提供的圖形管道,所以一個Android4.3或者更高版本的設備可能並不支持OpenGL ES 3.0.

Android在framework API和NDK都提供瞭對OpenGL的支持

 

OpenGL ES包:

 

1、OpenGL ES 1.0/1.1 API 包
android.opengl — 這個包提供瞭OpenGL ES 1.0/1.1包含類的靜態接口,比javax.microedition.khronos 包裡的接口有更好的性能

GLES10GLES10ExtGLES11GLES11Ext

javax.microedition.khronos.opengles — 這個包裡提供瞭OpenGL ES 1.0/1.1的標準實現

GL10GL10ExtGL11GL11ExtGL11ExtensionPack

2 . OpenGL ES 2.0的API類

android.opengl.GLES20 — 這個包提供瞭OpenGLES 2.0的接口,在Android2.2及以上版本可以使用。

3. OpenGL ES 3.0/3/1的API包
android.opengl — 這個包提供瞭OpenGL ES 3.0/3.1的類接口。

GLES30GLES31GLES31Ext (Android Extension Pack)

為繪制對象映射坐標(Mapping Coordinates for Drawn Objects)

 

在Android設備上面展示圖形的一個基本問題是屏幕的尺寸和形狀都不同,OpenGL假設默認有一個統一的正方形坐標系,並將這些坐標繪制到通常不是正方形的屏幕上面,就像它是一個完美的正方形。

圖1 默認的OpenGL坐標系統(左) 映射到通常的Android設備屏幕(右)

上面的圖片現實瞭OpenGL幀假設的統一坐標系統(左),和這些坐標是怎麼映射到一個通常的設備橫屏上的(右)。為瞭解決這個問題,你可以應用OpenGL投影模式和相機視圖來變換坐標,使你的圖形對象在任何屏幕上面都有正確的比例。
為瞭應用投影和相機視圖,創建一個投影矩陣和相機視圖矩陣,並將它們應用到OpenGL渲染管道,投影矩陣重新計算圖形的坐標,使它們在設備屏幕上面正確映射。相機視圖矩陣從一個特定的視圖位置給渲染對象創建一個變換。

OpenGL ES 1.0的投影和相機視圖

在ES 1.0 API,通過為投影和相機視圖創建相應的矩陣,然後把它們添加到OpenGL環境中。
1.投影矩陣 – 使用設備屏幕的幾何參數創建投影矩陣,來重新計算對象的坐標,使它們以正確的比例繪制。下面的示例代碼展示瞭在onSurfaceChanged()方法中以屏幕的寬高比創建投影矩陣,並把它應用到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
}

2.相機變換矩陣 – 當你使用投影矩陣適應瞭坐標系統後,你必須應用一個相機視圖。下面的代碼展示瞭使用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裡面,應用投影和相機視圖時,首先要向圖形對象的頂點著色器添加一個矩陣成員對象,當矩陣對象添加後,就可以向對象生成和應用投影和相機視圖矩陣。

 

1. 向頂點著色器中添加矩陣 – 為view的投影矩陣創建一個變量,然後將它與著色器的position相乘。下面的著色器代碼,包含的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";

註意:上面的示例在頂點著色器中定義瞭一個矩陣變換成員,你可以給這個矩陣應用一個投影和相機視圖的結合矩陣。根據你的需求,可能會需要定義分開的投影矩陣和相機視圖矩陣成員,以方便分開自由的改變它們。

 

2. 訪問著色器矩陣 – 當你在頂點著色器中創建瞭hook之後,你可以訪問這個變量來應用投影和相機視圖矩陣。下面的代碼顯示瞭如何訪問頂點著色器中定義的矩陣變量:

public void onSurfaceCreated(GL10 unused, EGLConfig config) {

muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");

}

3. 創建投影和相機視圖矩陣 – 生成可應用到圖形對象的投影和視圖矩陣。下面的代碼根據設備屏幕的寬高比創建瞭投影矩陣和相機視圖矩陣。

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. 應用投影和相機視圖矩陣 – 應用投影和相機視圖變換,將兩個矩陣相乘,然後將它們設置到頂點著色器。下面的代碼顯示瞭如何將矩陣應用到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 1.0/1.1API與2.0或者更高的版本是明顯不同的,1.x版本有更多方便的方法和固定的管線,而2.0和3.0API通過OpenGL著色器提供瞭更多對管線的直接控制。你應該仔細考慮繪制圖形的需求,並且選擇最適合應用的api。
OpenGL ES 3.0 API比2.0提供跟多的特性和更好的性能,同時也向下兼容。這意味著你可以將應用的OpenGL ES的版本設置為2.0,並選擇性地包含一些有效的3.0圖形特性。

紋理壓縮支持

 

紋理壓縮可以通過減少內存來顯著地提高OpenGL的性能,使內存使用的效率更高。Android framework提供瞭ETC1壓縮格式作為標準特性,包含瞭ETC1Util工具類和etctool壓縮工具(位於Android SDK下的/tools/目錄)。
大部分的Android設備都支持ETC1格式,但是不保證它一定支持。檢查設備是否支持ETC1格式,調用 ETC1Util.isETC1Supported()方法。ETC1紋理壓縮格式不支持有透明度的紋理(alpha通道),如果應用需要有透明度的紋理,你應該選擇目標設備支持的其他紋理壓縮格式。
當使用OpenGL ES3.0時,ETC2/EAC紋理壓縮格式是確保可用的,這種紋理格式有優秀的壓縮比率,高質量的視覺效果,並且也支持透明度。
除瞭ETC格式,Android設備支持很多其他格式的紋理壓縮,根據GPU芯片和OpenGL實現的不同略有差異。你應該調查目標設備所支持的壓縮格式,然後再確定應用支持哪些壓縮格式。為瞭確定設備支持哪些紋理格式,你必須查詢設備,檢查OpenGL的擴展名,它們標記瞭哪些紋理壓縮格式可以被支持。一些常見的紋理壓縮格式有以下幾種:

ATITC (ATC) – ATI紋理壓縮在許多設備上面都支持,它支持RGB紋理壓縮但不包含alpha通道,一些OpenGL擴展名可以代表這種格式,比如:
GL_AMD_compressed_ATC_texture
GL_ATI_texture_compression_atitc

PVRTC-PVRTC紋理壓縮在許多設備上面都支持,支持每個像素2位或者4位的紋理,包含或者不包含alpha通道都可以。下面的OpenGL擴展名可以代表這種格式,比如:
GL_IMG_texture_compression_pvrtc

S3TC (DXTn/DXTC)-S3有一些格式變化(從DXT1到DXT5),使用並不是很廣泛。它支持包含4位或者8位alpha通道的RGB紋理。下面的OpenGL擴展名可以代表這種格式,比如:
GL_OES_texture_compression_S3TC
GL_EXT_texture_compression_s3tc
GL_EXT_texture_compression_dxt1
GL_EXT_texture_compression_dxt3
GL_EXT_texture_compression_dxt5

3DC – 3DC紋理壓縮是比較少使用的支持包含alpha通道的RGB紋理,下面的OpenGL擴展名可以代表這種格式:
GL_AMD_compressed_3DC_texture

這些紋理壓縮格式並不是在所有的設備上面都支持,不同的制造商和設備支持的格式不同,一旦你決定應用哪種紋理壓縮格式,一定要在manifest文件中使用 進行聲明,使用這個聲明可以被Google Play過濾掉不支持這些格式的設備。

確定OpenGL擴展

 

OpenGL實現根據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規范描述的核心集,將這些擴展打包促進瞭不同設備上面功能的一致,同時允許開發者充分利用手機GPU設備的最新功能。
AEP還提高瞭對圖像、著色器存儲緩存和片元著色器原子計數器的支持。
app使用AEP時,比如在manifest裡進行聲明,另外,平臺版本必須支持它

使用 hasSystemFeature(String)方法來驗證是否支持AEP,傳遞FEATURE_OPENGLES_EXTENSION_PACK作為參數:

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

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *