Android開發筆記(一百五十六)通過渲染紋理展示地球儀

上一篇文章介紹瞭如何使用GL10描繪三維物體的線段框架,後面給出的立方體和球體效果圖,雖然看起來具備立體的輪廓,可離真實的物體還差得遠。因爲現實生活中的物體不僅僅有個骨架,還有花紋有光澤(比如衣服),所以若想讓三維物體更加符合實際,就得給它加一層皮,也可以說是加一件衣服,這個皮毛大衣用OpenGL的術語稱呼則爲“紋理”。

三維物體的骨架是通過三維座標系表示的,每個點都有x、y、z三個方向上的數值大小。那麼三維物體的紋理也需要通過紋理座標系來表達,但紋理座標並非三維形式而是二維形式,這是怎麼回事呢?打個比方,裁縫店給顧客製作一件衣服,首先要丈量顧客的身高、肩寬,以及胸圍、腰圍、臀圍等三圍,然後才能根據這些身體數據剪裁布料,這便是所謂的量體裁衣。那做衣服的一匹一匹布料又是什麼樣子的?當然是攤開來一大片一大片整齊的布匹了,明顯這些布匹近似於二維的平面。但是最終的成品衣服穿在顧客身上卻是三維的模樣,顯然中間必定有個從二維布匹到三維衣服的轉換過程。轉換工作的一系列計算,離不開前面測量得到的身高、肩寬、三圍等等,其中身高和肩寬是直線的長度,而三圍是曲線的長度。如果把三圍的曲線剪斷並拉直,就能得到直線形式的三圍;同理,把衣服這個三維的曲面剪開,然後把它攤平,得到平面形式的衣服。於是,剪開並攤平後的平面衣服,即可與原始的平面布匹對應起來了。因此,紋理座標的目的就是標記被攤平衣服的二維座標,從而將同屬二維座標系的布匹一塊一塊貼上去。

在OpenGL體系之中,紋理座標又稱UV座標,通過兩個浮點數組合來設置一個點的紋理座標(U,V),其中U表示橫軸,V表示縱軸。紋理座標不關心物體的三維位置,好比一個人不管走到哪裏,不管做什麼動作,身上穿的還是那件衣服。紋理座標所要表述的,是衣服的一小片一小片分別來自於哪塊布料,也就是說,每一小片衣服各是由什麼材質構成。既可以是棉布材質,也可以是絲綢材質,還可以是尼龍材質,紋理只是衣服的脈絡,材質纔是最終貼上去的花色。

給三維物體穿衣服的動作,通常叫做給三維圖形貼圖,更專業地說叫紋理渲染。渲染紋理的過程主要由三大項操作組成,分別說明如下:

一、啓用紋理的一系列開關設置,該系列又包括下述步驟:
1、渲染紋理肯定要啓用紋理功能了,並且爲了能夠正確渲染,還需同時啓用深度測試。啓用深度測試的目的,是隻繪製物體朝向觀測者的正面,而不繪製物體的背面。上一篇文章的立方體和球體因爲沒有開啓深度測試,所以背面的線段也都畫了出來。啓用紋理與深度測試的代碼示例如下:
        // 啓用某功能,對應的glDisable是關閉某功能。
        // GL_DEPTH_TEST指的是深度測試。啓用紋理時必須同時開啓深度測試,
        // 這樣只有像素點前面沒有東西遮擋之時,該像素點纔會予以繪製。
        gl.glEnable(GL10.GL_DEPTH_TEST);
        // 啓用紋理
        gl.glEnable(GL10.GL_TEXTURE_2D);
2、OpenGL默認的環境光是沒有特定光源的散光,如果要實現特定光源的光照效果,則需開啓燈照功能,另外至少啓用一個光源,或者同時啓用多個光源。下面是隻開啓一處燈光的代碼例子:
        // 開啓燈照效果
        gl.glEnable(GL10.GL_LIGHTING);
        // 啓用光源0
        gl.glEnable(GL10.GL_LIGHT0);
3、就像人可以穿着多件衣服那樣,三維物體也能接連描繪多種紋理,於是每次渲染紋理都得分配一個紋理編號。這個紋理編號的分配操作有點拗口,開發者不用太在意,只管按照下面例行公事便成:
        int[] textures = new int[1];
        // 告訴OpenGL去生成textures.textures中存放了創建的Texture ID
        gl.glGenTextures(1, textures, 0);
        //通知OpenGL庫使用這個Texture
        gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);
4、如同衣服有很寬鬆的款式,也有很緊身的款式,對於這些不是那麼合身的情況,OpenGL要怎麼去渲染放大或者縮小了的紋理?此時就要指定下述的紋理參數設置了:
        //用來渲染的Texture可能比要渲染的區域大或者小,所以需要設置Texture需要放大或是縮小時OpenGL的模式
        //GL_TEXTURE_MAG_FILTER表示放大的情況,GL_TEXTURE_MIN_FILTER表示縮小的情況
        //常用的兩種模式爲GL10.GL_LINEAR和GL10.GL_NEAREST。
        //需要比較清晰的圖像使用GL10.GL_NEAREST,而使用GL10.GL_LINEAR則會得到一個較模糊的圖像
        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
        //當定義的材質座標點超過UV座標定義的大小(UV座標爲0,0到1,1),這時需要告訴OpenGL庫如何去渲染這些不存在的Texture部分。
        //有兩種設置:GL_REPEAT 重複Texture。GL_CLAMP_TO_EDGE 只靠邊線繪製一次。
        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE);
        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE);
5、最後還要聲明一個位圖對象綁定該紋理,表示後續的紋理渲染動作將使用該位圖包裹三維物體,綁定位圖材質的代碼如下所示:
        // 將Bitmap資源和Texture綁定起來,即指定一個具體的材質
        GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, mBitmap, 0);

二、計算材質的紋理座標
三維物體的每個頂點座標都以(x,y,z)構成,因此若要表達三個頂點的空間位置,就需要大小爲3*3=9的浮點數組。本文開頭提到紋理座標是二維的,因此表達三個頂點的紋理座標只需大小爲3*2=6的浮點數組。至於詳細的紋理座標計算,則依據具體物體的形狀以及材質的尺寸來決定,這裏不再贅述。

三、在三維圖形上根據紋理點座標逐個貼上對應的材質
渲染紋理除了要打開頂點開關,還要打開材質開關。同理,綁定頂點座標的時候,也要綁定紋理座標。因爲材質是一片一片的花色,所以調用glDrawArrays繪製方法時,要指定採取GL10.GL_TRIANGLE_STRIP方式,表示本次繪圖畫的是一個三角形的平面,這樣從位圖對象裁剪出來的花紋就貼圖完成了。
下面是進行材質貼圖的繪製代碼例子:
    private void drawGlobe(GL10 gl) {
        //打開材質開關
        gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
        //打開頂點開關
        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
        for(int i= 0;i<=divide; i++){
            //綁定每片皮膚的頂點座標
            gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mVertices.get(i));
            //綁定每片皮膚的紋理座標,第一個參數爲2表示紋理座標是二維的
            gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, mTextureCoords.get(i));
            //GL_LINE_STRIP只繪製線條,GL_TRIANGLE_STRIP纔是畫三角形的面
            gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, divide*2+2);
        }
        //關閉頂點開關
        gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
        //關閉材質開關
        gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
    }

最後觀察一下把世界地圖貼到球體上面形成地球儀的效果,下面是原始的世界地圖平面,可以看到底部的南極洲被拉得很大:


下面是利用OpenGL貼圖成功的三維地球儀轉動動畫,看起來就逼真多了:



話說上面竟然是三維動畫,其實OpenGL繪製三維動畫很簡單,由於GLSurfaceView的渲染器會持續調用onDrawFrame函數,因此只要在該函數中設置漸變的變換數值,即可輕鬆實現以下動畫效果:
1、調用glRotatef方法設置漸變的角度,可實現三維物體的旋轉動畫;
2、調用glTranslatef方法設置漸變的位移,可實現三維物體的平移動畫;
3、調用glScalef方法設置漸變的放大或縮小倍率,可實現三維物體的縮放動畫;


點此查看Android開發筆記的完整目錄


__________________________________________________________________________
本文現已同步發佈到微信公衆號“老歐說安卓”,打開微信掃一掃下面的二維碼,或者直接搜索公衆號“老歐說安卓”添加關注,更快更方便地閱讀技術乾貨。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章