Android應(yīng)用 :第一個(gè) OpenGL ES 三角形
1.簡(jiǎn)介
OpenGL是由SGI公司開發(fā)的一套3D圖形軟件接口標(biāo)準(zhǔn),OpenGL ES就是眾多版本中的一個(gè)子集。
3D場(chǎng)景中的3D模型的最基本單位是稱為頂點(diǎn)的vertex,它代表三維空間中的一個(gè)點(diǎn)。
盡管OpenGL支持多種多邊形,但是很不幸的是OpenGL ES目前只支持三角形,這主要是出于性能的原因。
OpenGL ES中有一項(xiàng)功能叫做背面裁剪,含義是打開背面裁剪功能后,視角在一個(gè)三角形的背面時(shí)不渲染此三角形,即看不到這個(gè)三角形,此功能可以大大提高渲染效率。
三角形正反面確定:當(dāng)面對(duì)一個(gè)三角形時(shí),若頂點(diǎn)的順序是逆時(shí)針則位于三角形的正面,反之則是反面。
SceneRenderer場(chǎng)景渲染器mSurfaceView.requestFocus()獲取焦點(diǎn)
setFocusableInTouchMode(true)設(shè)置為可觸控
點(diǎn)和線的繪制GL_POINTS,GL_LINES,GL_LINE_START,GL_LINE_LOOP,GL_TRIANGLES,GL_TRIANGLE_STRIP,GL_TRIANGLE_FAN 。
正交投影和透視投影
正交投影是平行投影的一種,設(shè)置正交投影的語句為:gl.glOrthof(left,right,bottom,top,near,far)
透視投影屬于非平行投影,游戲中較多采用,設(shè)置透視投影的語句為:gl.glFrustumf(left,right,bottom,top,near,far)
2. 代碼
布局?xml 代碼
Mainactivity 代碼:
package?com.example.android_sample_4_1; import?android.os.Bundle; import?android.app.Activity; import?android.view.Menu; import?android.widget.LinearLayout; public?class?MainActivity?extends?Activity?{ private?MySurfaceView?mSurfaceView; @Override protected?void?onCreate(Bundle?savedInstanceState)?{ super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mSurfaceView?=?new?MySurfaceView(this); mSurfaceView.requestFocus(); mSurfaceView.setFocusableInTouchMode(true); LinearLayout?ll?=?(LinearLayout)?this.findViewById(R.id.main_liner); ll.addView(mSurfaceView); } @Override protected?void?onPause()?{ //?TODO?Auto-generated?method?stub super.onPause(); mSurfaceView.onPause(); } @Override protected?void?onResume()?{ //?TODO?Auto-generated?method?stub super.onResume(); mSurfaceView.onResume(); } @Override public?boolean?onCreateOptionsMenu(Menu?menu)?{ //?Inflate?the?menu;?this?adds?items?to?the?action?bar?if?it?is?present. getMenuInflater().inflate(R.menu.main,?menu); return?true; } }
MySurfaceView 代碼:
package?com.example.android_sample_4_1; import?javax.microedition.khronos.egl.EGLConfig; import?javax.microedition.khronos.opengles.GL10; import?android.content.Context; import?android.opengl.GLSurfaceView; import?android.view.MotionEvent; public?class?MySurfaceView?extends?GLSurfaceView{ private?final?float?TOUCH_SCALE_FACTOR=180.0f/320;//角度縮放比例,即屏幕寬320,從屏幕的一端滑到另一端,x軸上的差距對(duì)應(yīng)相應(yīng)的需要旋轉(zhuǎn)的角度 private?SceneRenderer?myRenderer;//場(chǎng)景渲染器 private?float?myPreviousY;//上次屏幕上的觸控位置的Y坐標(biāo) private?float?myPreviousX;//上次屏幕上的觸控位置的X坐標(biāo) public?MySurfaceView(Context?context)?{ super(context); //?TODO?Auto-generated?constructor?stub myRenderer?=?new?SceneRenderer(); this.setRenderer(myRenderer); this.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY); } @Override public?boolean?onTouchEvent(MotionEvent?event)?{ float?y?=?event.getY(); float?x?=?event.getX(); switch?(event.getAction())?{ case?MotionEvent.ACTION_MOVE: float?dy?=?y?-?myPreviousY; float?dx?=?x?-?myPreviousX; myRenderer.tr.yAngle?+=?dx?*?TOUCH_SCALE_FACTOR; myRenderer.tr.zAngle?+=?dy?*?TOUCH_SCALE_FACTOR; requestRender(); } myPreviousX?=?x; myPreviousY?=?y; return?true; } private?class?SceneRenderer?implements?GLSurfaceView.Renderer{ Triangle?tr?=?new?Triangle(); public?SceneRenderer()?{ //?TODO?Auto-generated?constructor?stub } @Override public?void?onDrawFrame(GL10?gl)?{ //?TODO?Auto-generated?method?stub gl.glEnable(GL10.GL_CULL_FACE); gl.glShadeModel(GL10.GL_SMOOTH); gl.glFrontFace(GL10.GL_CCW); gl.glClear(GL10.GL_COLOR_BUFFER_BIT|GL10.GL_DEPTH_BUFFER_BIT); gl.glMatrixMode(GL10.GL_MODELVIEW); gl.glLoadIdentity(); gl.glTranslatef(0,?0,?-2.0f); tr.drawSelf(gl); } @Override public?void?onSurfaceChanged(GL10?gl,?int?width,?int?height)?{ //?TODO?Auto-generated?method?stub gl.glViewport(0,?0,?width,?height); gl.glMatrixMode(GL10.GL_PROJECTION); gl.glLoadIdentity(); float?ratio?=?(float)width?/?height; gl.glFrustumf(-ratio,?ratio,?-1,?1,?1,?10); } @Override public?void?onSurfaceCreated(GL10?gl,?EGLConfig?config)?{ //?TODO?Auto-generated?method?stub gl.glDisable(GL10.GL_DITHER); gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT,?GL10.GL_FASTEST); gl.glClearColor(0,?0,?0,?0); gl.glEnable(GL10.GL_DEPTH_TEST); } } }
Triangle類 代碼:
package?com.example.android_sample_4_1; import?java.nio.ByteBuffer; import?java.nio.ByteOrder; import?java.nio.IntBuffer; import?javax.microedition.khronos.opengles.GL10; public?class?Triangle?{ private?IntBuffer?myVertexBuffer;//頂點(diǎn)坐標(biāo)數(shù)據(jù)緩沖 private?IntBuffer?myColorBuffer;//頂點(diǎn)著色數(shù)據(jù)緩沖 private?ByteBuffer?myIndexBuffer;//頂點(diǎn)構(gòu)建的索引數(shù)據(jù)緩沖 int?vCount=0;//頂點(diǎn)數(shù)量 int?iCount=0;//索引數(shù)量 float?yAngle=0;//繞y軸旋轉(zhuǎn)的角度 float?zAngle=0;//繞z軸旋轉(zhuǎn)的角度 public?Triangle(){ vCount=3;//一個(gè)三角形,3個(gè)頂點(diǎn) final?int?UNIT_SIZE=10000;//縮放比例 int?[]vertices=new?int[] ???????{ -8*UNIT_SIZE,6*UNIT_SIZE,0, -8*UNIT_SIZE,-6*UNIT_SIZE,0, 8*UNIT_SIZE,-6*UNIT_SIZE,0 ???????}; //創(chuàng)建頂點(diǎn)坐標(biāo)數(shù)據(jù)緩存,由于不同平臺(tái)字節(jié)順序不同,數(shù)據(jù)單元不是字節(jié)的(上面的事整型的緩存),一定要經(jīng)過ByteBuffer轉(zhuǎn)換,關(guān)鍵是通過ByteOrder設(shè)置nativeOrder() ByteBuffer?vbb=ByteBuffer.allocateDirect(vertices.length*4);//一個(gè)整數(shù)四個(gè)字節(jié),根據(jù)最新分配的內(nèi)存塊來創(chuàng)建一個(gè)有向的字節(jié)緩沖 vbb.order(ByteOrder.nativeOrder());//設(shè)置這個(gè)字節(jié)緩沖的字節(jié)順序?yàn)楸镜仄脚_(tái)的字節(jié)順序 myVertexBuffer=vbb.asIntBuffer();//轉(zhuǎn)換為int型緩沖 myVertexBuffer.put(vertices);//向緩沖區(qū)中放入頂點(diǎn)坐標(biāo)數(shù)據(jù) myVertexBuffer.position(0);//設(shè)置緩沖區(qū)的起始位置 final?int?one=65535;//支持65535色色彩通道 int?[]colors=new?int[]//頂點(diǎn)顏色值數(shù)組,每個(gè)頂點(diǎn)4個(gè)色彩值RGBA { one,one,one,0, one,one,one,0, one,one,one,0 }; ByteBuffer?cbb=ByteBuffer.allocateDirect(colors.length*4); cbb.order(ByteOrder.nativeOrder()); myColorBuffer=cbb.asIntBuffer(); myColorBuffer.put(colors); myColorBuffer.position(0); //為三角形構(gòu)造索引數(shù)據(jù)初始化 iCount=3; byte?[]indices=new?byte[] ????????????{ 0,1,2 ????????????}; //創(chuàng)建三角形構(gòu)造索引數(shù)據(jù)緩沖 myIndexBuffer=ByteBuffer.allocateDirect(indices.length); myIndexBuffer.put(indices); myIndexBuffer.position(0); } public?void?drawSelf(GL10?gl)//GL10是實(shí)現(xiàn)接口GL的一公共接口,包含了一系列常量和抽象方法 { gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);//啟用頂點(diǎn)坐標(biāo)數(shù)組 gl.glEnableClientState(GL10.GL_COLOR_ARRAY);//啟用頂點(diǎn)顏色數(shù)組 gl.glRotatef(yAngle,0,1,0);//根據(jù)yAngle的角度值,繞y軸旋轉(zhuǎn)yAngle gl.glRotatef(zAngle,0,0,1); gl.glVertexPointer//為畫筆指定頂點(diǎn)坐標(biāo)數(shù)據(jù) ( 3, //每個(gè)頂點(diǎn)的坐標(biāo)數(shù)量為3 GL10.GL_FIXED, //頂點(diǎn)坐標(biāo)值的類型為GL_FIXED,整型 0, //連續(xù)頂點(diǎn)坐標(biāo)數(shù)據(jù)之間的間隔 myVertexBuffer //頂點(diǎn)坐標(biāo)數(shù)量 ); gl.glColorPointer//為畫筆指定頂點(diǎn)?顏色數(shù)據(jù) ( 4, GL10.GL_FIXED, 0, myColorBuffer ); gl.glDrawElements//繪制圖形 ( GL10.GL_TRIANGLES, //填充模式,這里是以三角形方式填充 iCount, //頂點(diǎn)數(shù)量 GL10.GL_UNSIGNED_BYTE, //索引值的類型 myIndexBuffer //索引值數(shù)據(jù) ); }}