1. 程式人生 > >OpenGL ES基礎教程

OpenGL ES基礎教程

一、設定OpenGL ES檢視

設定OpenGL檢視並不難,Android上也較簡單。我們一般只需要2個步驟。

GLSurfaceView

我們要為GLSurfaceView提供一個專門用於渲染的介面

public void  setRenderer(GLSurfaceView.Renderer renderer)

 

GLSurfaceView.Renderer

GLSurfaceView.Renderer是一個通用渲染介面。我們必須實現下面的三個抽象方法:

// 畫面建立

public void onSurfaceCreated(GL10 gl, EGLConfig config)

// 畫面繪製

public void onDrawFrame(GL10 gl)

// 畫面改變

public void onSurfaceChanged(GL10 gl, int width, int height)

onSurfaceCreated

在這裡我們主要進行一些初始化工作,比如對透視進行修正、設定清屏所用顏色等。

onDrawFrame

繪製當前畫面

onSurfaceChanged

當裝置水平或者垂直變化時呼叫此方法,設定新的顯示比例

 

案例程式碼:

 
  1. public class OpenGLDemo extends Activity {
  2.     @Override
  3.     public void onCreate(Bundle savedInstanceState) { 
  4.         GLSurfaceView view = new GLSurfaceView(this);
  5.         view.setRenderer(new OpenGLRenderer());
  6.         setContentView(view);
  7.     }
  8. }
複製程式碼實現renderer需要更多的設定
 
  1.     public void onSurfaceCreated(GL10 gl, EGLConfig config) {
  2.         // 黑色背景
  3.         gl.glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
  4.         // 啟用陰影平滑(不是必須的)
  5.         gl.glShadeModel(GL10.GL_SMOOTH);
  6.         // 設定深度快取
  7.         gl.glClearDepthf(1.0f);
  8.         // 啟用深度測試
  9.         gl.glEnable(GL10.GL_DEPTH_TEST);
  10.         // 所作深度測試的型別
  11.         gl.glDepthFunc(GL10.GL_LEQUAL);
  12.         // 對透視進行修正
  13.         gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST);
  14.     }
  15.     public void onDrawFrame(GL10 gl) {
  16.         // 清除螢幕和深度快取
  17.         gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
  18.     }
  19.     public void onSurfaceChanged(GL10 gl, int width, int height) {
  20.         // 設定畫面的大小
  21.         gl.glViewport(0, 0, width, height);
  22.         // 設定投影矩陣
  23.         gl.glMatrixMode(GL10.GL_PROJECTION);
  24.         // 重置投影矩陣
  25.         gl.glLoadIdentity();
  26.         // 設定畫面比例
  27.         GLU.gluPerspective(gl, 45.0f, (float) width / (float) height, 0.1f,100.0f);
  28.         // 選擇模型觀察矩陣
  29.         gl.glMatrixMode(GL10.GL_MODELVIEW);
  30.         // 重置模型觀察矩陣
  31.         gl.glLoadIdentity();
  32.     }
  33. }
複製程式碼 只要加入這段程式碼到OpenGLDemo class裡就可實現全屏this.requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
    WindowManager.LayoutParams.FLAG_FULLSCREEN);
設定完檢視後,即可編譯執行,可以看到一個“漂亮”的黑屏 = =!
 

   

二、繪製多邊形前面的教程都是關於設定GLSurfaceView.的,接下來的教程將教我們渲染出一個多邊形。3D模型用較小的元素建立(點,邊,面),他們可以被分別操作。
頂點

    

   在Android中,我們通過float陣列定義頂點,並將它放到位元組型緩衝區內來獲取更好的效能。下例的程式碼即為上圖所示頂點。OpenGL ES的很多功能都必須手動的開啟和關閉。

  1. gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
  2. // 設定頂點資料,3代表XYZ座標系
  3. gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
  4. // 關閉頂點設定
  5. 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.     // 將座標陣列放入位元組快取中
  2.     // (1) 分配快取,一個short為2個位元組,所以要乘以2
  3.     ByteBuffer ibb = ByteBuffer.allocateDirect(indices.length * 2);
  4.     // (2) 設定位元組處理規則
  5.     ibb.order(ByteOrder.nativeOrder());
  6.     // (3) 轉換為short型字元
  7.     ShortBuffer indexBuffer = ibb.asShortBuffer();
  8.     // (4) 放入座標陣列
  9.     indexBuffer.put(indices);
  10.     // (5) 復位
  11.     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是使用最方便的,所以我們將先使用它。
  1. public class Square {
  2.     // 頂點座標陣列
  3.     private float vertices[] = { -1.0f, 1.0f, 0.0f, // 0, 左上
  4.         -1.0f, -1.0f, 0.0f, // 1, 左下
  5.         1.0f, -1.0f, 0.0f, // 2, 右下
  6.         1.0f, 1.0f, 0.0f, // 3, 右上
  7.     };
  8.     // 連線規則
  9.     private short[] indices = { 0, 1, 2, 0, 2, 3 };
  10.     // 頂點快取
  11.     private FloatBuffer vertexBuffer;
  12.     // 索引快取
  13.     private ShortBuffer indexBuffer;
  14.     public Square() {
  15.         // 一個float為4 bytes, 因此要乘以4
  16.         ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4);
  17.         vbb.order(ByteOrder.nativeOrder());
  18.         vertexBuffer = vbb.asFloatBuffer();
  19.         vertexBuffer.put(vertices);
  20.         vertexBuffer.position(0);
  21.         // short型別同理
  22.         ByteBuffer ibb = ByteBuffer.allocateDirect(indices.length * 2);
  23.         ibb.order(ByteOrder.nativeOrder());
  24.         indexBuffer = ibb.asShortBuffer();
  25.         indexBuffer.put(indices);
  26.         indexBuffer.position(0);
  27.         }
  28.     /**
  29.      * 繪製正方形到螢幕
  30.      * 
  31.      * @param gl
  32.      */
  33.     public void draw(GL10 gl) {
  34.         // 逆時針環繞
  35.         gl.glFrontFace(GL10.GL_CCW);
  36.         // 開啟剔除功能
  37.         gl.glEnable(GL10.GL_CULL_FACE);
  38.         // 剔除背面
  39.         gl.glCullFace(GL10.GL_BACK);
  40.         // 開啟頂點快取寫入功能
  41.         gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
  42.         // 設定頂點
  43.         // size:每個頂點有幾個數指描述。
  44.         // type:陣列中每個頂點的座標型別。
  45.         // stride:陣列中每個頂點間的間隔,步長(位元組位移)。
  46.         // pointer:儲存著每個頂點的座標值。初始值為0
  47.         gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
  48.         gl.glDrawElements(GL10.GL_TRIANGLES, indices.length,
  49.         GL10.GL_UNSIGNED_SHORT, indexBuffer);
  50.         // 關閉各個功能
  51.         gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
  52.         gl.glDisable(GL10.GL_CULL_FACE);
  53.     }
  54. }
複製程式碼 我們必須在OpenGLRenderer類中初始化square
  1. square = new Square();<!--EndFragment-->
複製程式碼 並在主繪製方法中呼叫square的繪製方法
  1. public void onDrawFrame(GL10 gl) {
  2.     // 清除螢幕和深度快取
  3.     gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
  4.     // 繪製正方形
  5.     square.draw(gl);
  6. }
複製程式碼 如果你現在執行應用,我們又看到了華麗的黑屏,為什麼?因為OpenGL ES渲染預設的當前位置為(0,0,0),視窗的定位也一樣。而且OpenGL ES不渲染太靠近窗體定位的東西。解決方法就是移動繪製的位置。
  1. gl.glTranslatef(0, 0, -4);  <!--EndFragment-->
複製程式碼 再次執行應用你將看到該正方形已經被繪製,但是它好像離我們越來越遠一樣,最後消失了。OpenGL ES不會在畫面之間復位繪製點,所以我們要自己完成。
  1. // 重置當前的模型觀察矩陣
  2. gl.glLoadIdentity();<!--EndFragment-->
複製程式碼 現在,我們執行應用將會看到一個固定位置的正方形。


 
 OpenGLDemo02.rar (64.87 KB, 下載次數: 460)