1. 程式人生 > >Android OpenGL ES- Cube Map

Android OpenGL ES- Cube Map

               

Cube Map,中文可以翻譯成立方環境對映,下面是摘自維基百科的說明:

立方反射對映是用立方對映使得物體看起來如同在反射周圍環境的一項技術。通常,這通常使使用者外渲染中使用的 skybox 完成。儘管由於反射物周圍的物體無法在結果中看到,所以這並不是一個真正的反射,但是通常仍然可以達到所期望的效果。

通過確定觀察物體的向量就可以進行立方對映反射,照相機光線在照相機向量與物體相交的位置按照曲面法線方向進行反射,這樣傳到立方圖(cube map)取得紋素(texel)的反射光線在照相機看來好像位於物體表面,這樣就得到了物體的反射效果。

簡單的講,就是你把一個具有金屬反射特性的茶壺放在一個房間中,茶壺的金屬表面會反射房間的場景,Cube Map就是解決如何將場景(環境)的內容顯示在茶壺的表面,如下圖所示:

本例使用環面(Torus)做為反射的表面,在OpenGL ES中任何3D物體,最終都是通過三角形來構造的,本例程式碼generateTorusGrid 和Grid物件用來構造環面的頂點座標。具體演算法有興趣的可以自行研究(需要有立體幾何的知識,這裡不詳細解釋)。

Cube map技術說到底就是用一個虛擬的立方體(cube)包圍住物體,眼睛到物體某處的向量eyevec經過反射(以該處的法線為對稱軸),反射向量reflectvec射到立方體上,就在該立方體上獲得一個紋素了(見下圖)。明顯,我們需要一個類似天空盒般的6張紋理貼在這個虛擬的立方體上。按CUBE MAPPING原意,就是一種enviroment map,因此把周圍場景渲染到這6張紋理裡是“正統”的。也就是每次渲染時,都作一次離線渲染,分別在每個矩形中心放置相機“拍下”場景,用FBO渲染到紋理,然後把這張紋理作為一個cube map物件的六紋理之一。這樣即使是動態之物也能被對映到物體表面了(雖然缺點是不能對映物體自身的任何部分)。

本例使用的六張圖為res/raw 目錄下的 skycubemap0 — skycubemap5 ,如下圖所示

使用Cube Map,首先要檢測裝置是否支援Cube Map 材質,本例使用以下程式碼檢測裝置是否支援Cube Map。

private boolean checkIfContextSupportsCubeMap(GL10 gl) {return checkIfContextSupportsExtension(gl, "GL_OES_texture_cube_map"); } /*** This is not the fastest way to check for an extension, but fine if* we are only checking for a few extensions each time a context is created.* @param
gl* @param extension* @return true if the extension is present in the current context.*/
private boolean checkIfContextSupportsExtension(GL10 gl, String extension) {String extensions = " " + gl.glGetString(GL10.GL_EXTENSIONS) + " ";// The extensions string is padded with spaces between extensions, but not// necessarily at the beginning or end. For simplicity, add spaces at the// beginning and end of the extensions string and the extension string.// This means we can avoid special-case checks for the first or last// extension, as well as avoid special-case checks when an extension name// is the same as the first part of another extension name.return extensions.indexOf(" " + extension + " ") >= 0;}

Cube Map (使用6張圖),處呼叫設定Cube Map外,其基本使用步驟類似於普通材質的使用。本例使用資源,其設定Cube Map的基本步驟如下:

1. 調入影象資源

if (mContextSupportsCubeMap) {int[] cubeMapResourceIds = new int[]{R.raw.skycubemap0, R.raw.skycubemap1, R.raw.skycubemap2,R.raw.skycubemap3, R.raw.skycubemap4, R.raw.skycubemap5};mCubeMapTextureID = generateCubeMap(gl, cubeMapResourceIds);} .... private int generateCubeMap(GL10 gl, int[] resourceIds) {checkGLError(gl);int[] ids = new int[1];gl.glGenTextures(1, ids, 0);int cubeMapTextureId = ids[0];gl.glBindTexture(GL11ExtensionPack.GL_TEXTURE_CUBE_MAP, cubeMapTextureId);gl.glTexParameterf(GL11ExtensionPack.GL_TEXTURE_CUBE_MAP,GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);gl.glTexParameterf(GL11ExtensionPack.GL_TEXTURE_CUBE_MAP,GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR); for (int face = 0; face < 6; face++) {InputStream is = getResources().openRawResource(resourceIds[face]);Bitmap bitmap;try {bitmap = BitmapFactory.decodeStream(is);} finally {try {is.close();} catch(IOException e) {Log.e("CubeMap", "Could not decode texture for face " + Integer.toString(face));}}GLUtils.texImage2D(GL11ExtensionPack.GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, 0,bitmap, 0);bitmap.recycle();}checkGLError(gl);return cubeMapTextureId;}

2. 繫結材質

函式generateCubeMap返回一個Texture的ID,在OpenGL ES中使用材質時,需要繫結材質

gl.glActiveTexture(GL10.GL_TEXTURE0);checkGLError(gl);gl.glEnable(GL11ExtensionPack.GL_TEXTURE_CUBE_MAP);checkGLError(gl);gl.glBindTexture(GL11ExtensionPack.GL_TEXTURE_CUBE_MAP, mCubeMapTextureID);checkGLError(gl);GL11ExtensionPack gl11ep = (GL11ExtensionPack) gl;gl11ep.glTexGeni(GL11ExtensionPack.GL_TEXTURE_GEN_STR,GL11ExtensionPack.GL_TEXTURE_GEN_MODE,GL11ExtensionPack.GL_REFLECTION_MAP);checkGLError(gl);gl.glEnable(GL11ExtensionPack.GL_TEXTURE_GEN_STR);checkGLError(gl);gl.glTexEnvx(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE, GL10.GL_DECAL);...mGrid.draw(gl);...gl.glDisable(GL11ExtensionPack.GL_TEXTURE_GEN_STR);

這樣就給環面物體添加了環境材質,顯示結果如下: