一,、設(shè)置OpenGL ES視圖 設(shè)置OpenGL視圖并不難,,Android上也較簡單。我們一般只需要2個步驟,。 GLSurfaceView 我們要為GLSurfaceView提供一個專門用于渲染的接口 public void setRenderer(GLSurfaceView.Renderer renderer)
GLSurfaceView.Renderer GLSurfaceView.Renderer是一個通用渲染接口,。我們必須實現(xiàn)下面的三個抽象方法: // 畫面創(chuàng)建 public void onSurfaceCreated(GL10 gl, EGLConfig config) // 畫面繪制 public void onDrawFrame(GL10 gl) // 畫面改變 public void onSurfaceChanged(GL10 gl, int width, int height) onSurfaceCreated 在這里我們主要進行一些初始化工作,比如對透視進行修正,、設(shè)置清屏所用顏色等,。 onDrawFrame 繪制當(dāng)前畫面 onSurfaceChanged 當(dāng)設(shè)備水平或者垂直變化時調(diào)用此方法,,設(shè)置新的顯示比例
案例代碼:
- public class OpenGLDemo extends Activity {
- @Override
- public void onCreate(Bundle savedInstanceState) {
- GLSurfaceView view = new GLSurfaceView(this);
- view.setRenderer(new OpenGLRenderer());
- setContentView(view);
- }
- }
復(fù)制代碼 實現(xiàn)renderer需要更多的設(shè)置
- public void onSurfaceCreated(GL10 gl, EGLConfig config) {
- // 黑色背景
- gl.glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
- // 啟用陰影平滑(不是必須的)
- gl.glShadeModel(GL10.GL_SMOOTH);
- // 設(shè)置深度緩存
- gl.glClearDepthf(1.0f);
- // 啟用深度測試
- gl.glEnable(GL10.GL_DEPTH_TEST);
- // 所作深度測試的類型
- gl.glDepthFunc(GL10.GL_LEQUAL);
- // 對透視進行修正
- gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST);
- }
- public void onDrawFrame(GL10 gl) {
- // 清除屏幕和深度緩存
- gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
- }
- public void onSurfaceChanged(GL10 gl, int width, int height) {
- // 設(shè)置畫面的大小
- gl.glViewport(0, 0, width, height);
- // 設(shè)置投影矩陣
- gl.glMatrixMode(GL10.GL_PROJECTION);
- // 重置投影矩陣
- gl.glLoadIdentity();
- // 設(shè)置畫面比例
- GLU.gluPerspective(gl, 45.0f, (float) width / (float) height, 0.1f,100.0f);
- // 選擇模型觀察矩陣
- gl.glMatrixMode(GL10.GL_MODELVIEW);
- // 重置模型觀察矩陣
- gl.glLoadIdentity();
- }
- }
復(fù)制代碼 只要加入這段代碼到OpenGLDemo class里就可實現(xiàn)全屏this.requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
設(shè)置完視圖后,即可編譯運行,,可以看到一個“漂亮”的黑屏 = =,!
二、繪制多邊形前面的教程都是關(guān)于設(shè)置GLSurfaceView.的,,接下來的教程將教我們渲染出一個多邊形,。3D模型用較小的元素創(chuàng)建(點,邊,,面),,他們可以被分別操作。
頂點
在Android中,,我們通過float數(shù)組定義頂點,,并將它放到字節(jié)型緩沖區(qū)內(nèi)來獲取更好的性能。下例的代碼即為上圖所示頂點,。OpenGL ES的很多功能都必須手動的開啟和關(guān)閉,。 - gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
- // 設(shè)置頂點數(shù)據(jù),3代表XYZ坐標(biāo)系
- gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
- // 關(guān)閉頂點設(shè)置
- gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
復(fù)制代碼 邊
面
計算多邊形面的時候,,一定要注意正確的方向.,。因為這將決定哪一面為正面哪一面為背面。 所以我們盡量保證整個項目都使用相同的環(huán)繞,。gl.glFrontFace(GL10.GL_CCW);控制多邊形的正面是如何決定的,。在默認(rèn)情況下,mode是GL_CCW,。mode的值為: GL_CCW 表示窗口坐標(biāo)上投影多邊形的頂點順序為逆時針方向的表面為正面,。 GL_CW 表示頂點順序為順時針方向的表面為正面。頂點的方向又稱為環(huán)繞,。gl.glEnable(GL10.GL_CULL_FACE);gl.glCullFace(GL10.GL_BACK);剔除多邊形的背面,,禁用多邊形背面上的光照、陰影和顏色計算及操作,。gl.glDisable(GL10.GL_CULL_FACE);
多邊形
到了繪制面的時候了,, 我們使用默認(rèn)的逆時針環(huán)繞。下例代碼將繪制上圖多邊形,。- // 將坐標(biāo)數(shù)組放入字節(jié)緩存中
- // (1) 分配緩存,,一個short為2個字節(jié),所以要乘以2
- ByteBuffer ibb = ByteBuffer.allocateDirect(indices.length * 2);
- // (2) 設(shè)置字節(jié)處理規(guī)則
- ibb.order(ByteOrder.nativeOrder());
- // (3) 轉(zhuǎn)換為short型字符
- ShortBuffer indexBuffer = ibb.asShortBuffer();
- // (4) 放入坐標(biāo)數(shù)組
- indexBuffer.put(indices);
- // (5) 復(fù)位
- indexBuffer.position(0);
復(fù)制代碼 渲染是時候弄些玩意兒到屏幕上去了,,繪制時我們將用到兩個函數(shù)public abstract void glDrawArrays(int mode, int first, int count)通過我們構(gòu)造的頂點緩存來繪制頂點public abstract void glDrawElements(int mode, int count, int type, Buffer indices)和glDrawArrays類似,,但需要直接傳入type(索引值的類型,如GL_UNSIGNED_SHORT, or GL_UNSIGNED_INT),和indices(索引緩存)兩者的共同點是,,都必須知道他們需要畫什么,。怎樣渲染圖元,有不同方式,,為了幫助調(diào)試,我們應(yīng)該了解它們,。
Mode:GL_POINTS繪制獨立的點到屏幕
GL_LINE_STRIP連續(xù)的連線,,第n個頂點與第n-1個頂點繪制一條直線
GL_LINE_LOOP和上面相同,但首尾相連
GL_LINES各對獨立的線段
GL_TRIANGLES各個獨立的三角形
GL_TRIANGLE_STRIP
繪制一系列的三角形,,先是頂點 v0, v1, v2, 然后是 v2, v1, v3 (注意規(guī)律), 然后v2, v3, v4等,。該規(guī)律確保所有的三角形都以相同的方向繪制。
GL_TRIANGLE_FAN和GL_TRIANGLE_STRIP類似, 但其先繪制 v0, v1, v2, 再是 v0, v2, v3, 然后 v0, v3, v4等,。
我認(rèn)為GL_TRIANGLES是使用最方便的,,所以我們將先使用它。- public class Square {
- // 頂點坐標(biāo)數(shù)組
- private float vertices[] = { -1.0f, 1.0f, 0.0f, // 0, 左上
- -1.0f, -1.0f, 0.0f, // 1, 左下
- 1.0f, -1.0f, 0.0f, // 2, 右下
- 1.0f, 1.0f, 0.0f, // 3, 右上
- };
- // 連接規(guī)則
- 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) {
- // 逆時針環(huán)繞
- gl.glFrontFace(GL10.GL_CCW);
- // 開啟剔除功能
- gl.glEnable(GL10.GL_CULL_FACE);
- // 剔除背面
- gl.glCullFace(GL10.GL_BACK);
- // 開啟頂點緩存寫入功能
- gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
- // 設(shè)置頂點
- // size:每個頂點有幾個數(shù)指描述,。
- // type:數(shù)組中每個頂點的坐標(biāo)類型,。
- // stride:數(shù)組中每個頂點間的間隔,步長(字節(jié)位移),。
- // pointer:存儲著每個頂點的坐標(biāo)值,。初始值為0
- gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
- gl.glDrawElements(GL10.GL_TRIANGLES, indices.length,
- GL10.GL_UNSIGNED_SHORT, indexBuffer);
- // 關(guān)閉各個功能
- gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
- gl.glDisable(GL10.GL_CULL_FACE);
- }
- }
復(fù)制代碼 我們必須在OpenGLRenderer類中初始化square- square = new Square();<!--EndFragment-->
復(fù)制代碼 并在主繪制方法中調(diào)用square的繪制方法- public void onDrawFrame(GL10 gl) {
- // 清除屏幕和深度緩存
- gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
- // 繪制正方形
- square.draw(gl);
- }
復(fù)制代碼 如果你現(xiàn)在運行應(yīng)用,我們又看到了華麗的黑屏,,為什么,?因為OpenGL ES渲染默認(rèn)的當(dāng)前位置為(0,0,0),窗口的定位也一樣,。而且OpenGL ES不渲染太靠近窗體定位的東西,。解決方法就是移動繪制的位置。- gl.glTranslatef(0, 0, -4); <!--EndFragment-->
復(fù)制代碼 再次運行應(yīng)用你將看到該正方形已經(jīng)被繪制,,但是它好像離我們越來越遠(yuǎn)一樣,,最后消失了。OpenGL ES不會在畫面之間復(fù)位繪制點,,所以我們要自己完成,。- // 重置當(dāng)前的模型觀察矩陣
- gl.glLoadIdentity();<!--EndFragment-->
復(fù)制代碼 現(xiàn)在,我們運行應(yīng)用將會看到一個固定位置的正方形,。
|