編輯:Android開發實例
二、繪制多邊形
前面的教程都是關於設置GLSurfaceView.的,接下來的教程將教我們渲染出一個多邊形。3D模型用較小的元素創建(點,邊,面),他們可以被分別操作。
頂點
在Android中,我們通過float數組定義頂點,並將它放到字節型緩沖區內來獲取更好的性能。
下例的代碼即為上圖所示頂點。
OpenGL ES的很多功能都必須手動的開啟和關閉。
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
// 設置頂點數據,3代表XYZ坐標系
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
// 關閉頂點設置
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
邊
面
計算多邊形面的時候,一定要注意正確的方向.。因為這將決定哪一面為正面哪一面為背面。 所以我們盡量保證整個項目都使用相同的環繞。
gl.glFrontFace(GL10.GL_CCW);
控制多邊形的正面是如何決定的。在默認情況下,mode是GL_CCW。
mode的值為:
GL_CCW 表示窗口坐標上投影多邊形的頂點順序為逆時針方向的表面為正面。
GL_CW 表示頂點順序為順時針方向的表面為正面。
頂點的方向又稱為環繞。
gl.glEnable(GL10.GL_CULL_FACE);
gl.glCullFace(GL10.GL_BACK);
剔除多邊形的背面,禁用多邊形背面上的光照、陰影和顏色計算及操作。
gl.glDisable(GL10.GL_CULL_FACE);
多邊形
到了繪制面的時候了, 我們使用默認的逆時針環繞。
下例代碼將繪制上圖多邊形。
- // 將坐標數組放入字節緩存中
- // (1) 分配緩存,一個short為2個字節,所以要乘以2
- ByteBuffer ibb = ByteBuffer.allocateDirect(indices.length * 2);
- // (2) 設置字節處理規則
- ibb.order(ByteOrder.nativeOrder());
- // (3) 轉換為short型字符
- ShortBuffer indexBuffer = ibb.asShortBuffer();
- // (4) 放入坐標數組
- indexBuffer.put(indices);
- // (5) 復位
- indexBuffer.position(0);
渲染
是時候弄些玩意兒到屏幕上去了,繪制時我們將用到兩個函數
public abstract void glDrawArrays(int mode, int first, int count)
通過我們構造的頂點緩存來繪制頂點
public abstract void glDrawElements(int mode, int count, int type, Buffer indices)
和glDrawArrays類似,但需要直接傳入type(索引值的類型,如GL_UNSIGNED_SHORT, or GL_UNSIGNED_INT),和indices(索引緩存)
兩者的共同點是,都必須知道他們需要畫什麼。怎樣渲染圖元,有不同方式,為了幫助調試,我們應該了解它們。
Mode:
GL_POINTS
繪制獨立的點到屏幕
GL_LINE_STRIP
連續的連線,第n個頂點與第n-1個頂點繪制一條直線
GL_LINE_LOOP
和上面相同,但首尾相連
GL_LINES
各對獨立的線段
GL_TRIANGLES
各個獨立的三角形
GL_TRIANGLE_STRIP
繪制一系列的三角形,先是頂點 v0, v1, v2, 然後是 v2, v1, v3 (注意規律), 然後v2, v3, v4等。該規律確保所有的三角形都以相同的方向繪制。
GL_TRIANGLE_FAN
和GL_TRIANGLE_STRIP類似, 但其先繪制 v0, v1, v2, 再是 v0, v2, v3, 然後 v0, v3, v4等。
我認為GL_TRIANGLES是使用最方便的,所以我們將先使用它。
- public class Square {
- // 頂點坐標數組
- private float vertices[] = { -1.0f, 1.0f, 0.0f, // 0, 左上
- -1.0f, -1.0f, 0.0f, // 1, 左下
- .0f, -1.0f, 0.0f, // 2, 右下
- .0f, 1.0f, 0.0f, // 3, 右上
- };
- // 連接規則
- private short[] indices = { 0, 1, 2, 0, 2, 3 };
- // 頂點緩存
- private FloatBuffer vertexBuffer;
- // 索引緩存
- private ShortBuffer indexBuffer;
- public Square() {
- // 一個float為4 bytes, 因此要乘以4
- ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4);
- vbb.order(ByteOrder.nativeOrder());
- vertexBuffer = vbb.asFloatBuffer();
- vertexBuffer.put(vertices);
- vertexBuffer.position(0);
- // short類型同理
- ByteBuffer ibb = ByteBuffer.allocateDirect(indices.length * 2);
- ibb.order(ByteOrder.nativeOrder());
- indexBuffer = ibb.asShortBuffer();
- indexBuffer.put(indices);
- indexBuffer.position(0);
- }
- /**
- * 繪制正方形到屏幕
- *
- * @param gl
- */
- public void draw(GL10 gl) {
- // 逆時針環繞
- gl.glFrontFace(GL10.GL_CCW);
- // 開啟剔除功能
- gl.glEnable(GL10.GL_CULL_FACE);
- // 剔除背面
- gl.glCullFace(GL10.GL_BACK);
- // 開啟頂點緩存寫入功能
- gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
- // 設置頂點
- // size:每個頂點有幾個數指描述。
- // type:數組中每個頂點的坐標類型。
- // stride:數組中每個頂點間的間隔,步長(字節位移)。
- // pointer:存儲著每個頂點的坐標值。初始值為0
- gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
- gl.glDrawElements(GL10.GL_TRIANGLES, indices.length,
- GL10.GL_UNSIGNED_SHORT, indexBuffer);
- // 關閉各個功能
- gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
- gl.glDisable(GL10.GL_CULL_FACE);
- }
- }
我們必須在OpenGLRenderer類中初始化square
square = new Square();<!--EndFragment-->
並在主繪制方法中調用square的繪制方法
public void onDrawFrame(GL10 gl) {
// 清除屏幕和深度緩存
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
// 繪制正方形
square.draw(gl);
}
如果你現在運行應用,我們又看到了華麗的黑屏,為什麼?因為OpenGL ES渲染默認的當前位置為(0,0,0),窗口的定位也一樣。而且OpenGL ES不渲染太靠近窗體定位的東西。解決方法就是移動繪制的位置。
gl.glTranslatef(0, 0, -4); <!--EndFragment-->
再次運行應用你將看到該正方形已經被繪制,但是它好像離我們越來越遠一樣,最後消失了。
OpenGL ES不會在畫面之間復位繪制點,所以我們要自己完成。
// 重置當前的模型觀察矩陣
gl.glLoadIdentity();<!--EndFragment-->
現在,我們運行應用將會看到一個固定位置的正方形。
Android應用程序可以在許多不同地區的許多設備上運行。為了使應用程序更具交互性,應用程序應該處理以適合應用程序將要使用的語言環境方面的文字,數字,文件等。在本章中,我
Android應用程序可以在許多不同地區的許多設備上運行。為了使應用程序更具交互性,應用程序應該處理以適合應用程序將要使用的語言環境方面的文字,數字,文件等。在本章中,我
當開發基於軟件模式的游戲時,通過縮放視頻緩沖區來適應顯示尺寸是最棘手的問題之一。當面對眾多不同的分辨率時(比如開放環境下的Android),該問題會變得更加麻煩,
我們在前面介紹過Hello world示例,這裡的Hello world 的Layout定義稍有不