Android OpenGL ES 画出三棱锥
如今VR這么火,感覺有必要學(xué)學(xué)OpenGL。什么是OpenGL ES ,OpenGL ES (OpenGL for Embedded System ) 為適用于嵌入式系統(tǒng)的一個免費二維和三維圖形庫。OpenGL ES 定義了一個在移動平臺上能夠支持 OpenGL 最基本功能的精簡標(biāo)準(zhǔn),以適應(yīng)如手機,PDA 或其它消費者移動終端的顯示系統(tǒng)。這篇文章不著重講理論方法的東西,關(guān)于理論知識大家可以去看這個Android OpenGL ES 開發(fā)教程 從入門到精通。我們這里主要呈現(xiàn)如何實現(xiàn)。首先我們實現(xiàn)一個三角形,然后再去現(xiàn)實一個三棱錐。
實現(xiàn)三角形
效果圖如下:
這是一個旋轉(zhuǎn)的三角形,本來想用gifcam截個動態(tài)圖,但是效果不好。還是用圖片代替了。基本效果能看出來。
實現(xiàn)的代碼如下(TrangleRenderer.java):
package com.chm.view;import android.opengl.GLSurfaceView;import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; import java.nio.ShortBuffer;import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10;/*** Created by charmingfst on 2016/11/16.*/ public class TrangleRenderer implements GLSurfaceView.Renderer {private float[] mTriangleArray = {0f, 1f, 0f, //p0-1f, -1f, 0f, //p11f, -1f, 0f, //p2};private float[] colors = {0f, 0f, 0f, 1f,0f, 0f, 1f, 1f,0f, 1f, 0f, 1f,};private FloatBuffer vertexBuffer;private FloatBuffer colorBuffer;private float angle;public TrangleRenderer() {//點相關(guān)//先初始化buffer,數(shù)組的長度*4,因為一個float占4個字節(jié)ByteBuffer bb = ByteBuffer.allocateDirect(mTriangleArray.length * 4);//以本機字節(jié)順序來修改此緩沖區(qū)的字節(jié)順序bb.order(ByteOrder.nativeOrder());vertexBuffer = bb.asFloatBuffer();//將給定float[]數(shù)據(jù)從當(dāng)前位置開始,依次寫入此緩沖區(qū)vertexBuffer.put(mTriangleArray);//設(shè)置此緩沖區(qū)的位置。如果標(biāo)記已定義并且大于新的位置,則要丟棄該標(biāo)記。vertexBuffer.position(0);//顏色相關(guān)ByteBuffer bb2 = ByteBuffer.allocateDirect(colors.length * 4);bb2.order(ByteOrder.nativeOrder());colorBuffer = bb2.asFloatBuffer();colorBuffer.put(colors);colorBuffer.position(0);}@Overridepublic void onSurfaceCreated(GL10 gl, EGLConfig config) {// 設(shè)置白色為清屏gl.glClearColor(1, 1, 1, 1);}//屏幕解鎖打開、屏幕發(fā)生旋轉(zhuǎn)、對象創(chuàng)建時@Overridepublic void onSurfaceChanged(GL10 gl, int width, int height) {float ratio = (float) width / height;// 設(shè)置OpenGL場景的大小,(0,0)表示窗口內(nèi)部視口的左下角,(w,h)指定了視口的大小gl.glViewport(0, 0, width, height);// 設(shè)置投影矩陣gl.glMatrixMode(GL10.GL_PROJECTION);// 重置投影矩陣gl.glLoadIdentity();// 設(shè)置視口的大小gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);//以下兩句聲明,以后所有的變換都是針對模型(即我們繪制的圖形)gl.glMatrixMode(GL10.GL_MODELVIEW);gl.glLoadIdentity();}//這個方法默認會每隔一段時間調(diào)用一次@Overridepublic void onDrawFrame(GL10 gl) { // 通知OpenGL使用單一的顏色來渲染 // gl.glColor4f(1.0f, 0.0f, 0.0f, 1.0f);// 清除屏幕和深度緩存gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);// 重置當(dāng)前的模型觀察矩陣gl.glLoadIdentity();// 允許設(shè)置頂點//GL10.GL_VERTEX_ARRAY頂點數(shù)組gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);// 允許設(shè)置顏色//GL10.GL_COLOR_ARRAY顏色數(shù)組gl.glEnableClientState(GL10.GL_COLOR_ARRAY);//將三角形在z軸上移動gl.glTranslatef(0f, 0.0f, -2.0f);gl.glRotatef(angle, 0, 1, 0);// 設(shè)置三角形gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);// 設(shè)置三角形顏色gl.glColorPointer(4, GL10.GL_FLOAT, 0, colorBuffer);// 繪制三角形gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 3);// 取消顏色設(shè)置gl.glDisableClientState(GL10.GL_COLOR_ARRAY);// 取消頂點設(shè)置gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);//繪制結(jié)束gl.glFinish();angle++;} }上面代碼每一步都解釋比較清楚了。如果還是不明白,那就去把上面提到的教程看一遍。這里提一下,對于onSurfaceChanged方法,注意:代碼的順序和和坐標(biāo)變換的過程是相反的。這個方法內(nèi)的代碼幾乎可以作為固定寫法。
下面將圖像顯示出來。
public class MainActivity extends AppCompatActivity {private boolean supportsEs2;private GLSurfaceView glView;private float rotateDegreen = 0;private TrangleRenderer glRenderer;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);checkSupported();if (supportsEs2) {glView = new GLSurfaceView(this);glRenderer = new TrangleRenderer();glView.setRenderer(glRenderer);setContentView(glView);} else {setContentView(R.layout.activity_main);Toast.makeText(this, "當(dāng)前設(shè)備不支持OpenGL ES 2.0!", Toast.LENGTH_SHORT).show();}}@Overrideprotected void onResume() {super.onResume();if (glView != null) {glView.onResume();}}private void checkSupported() {ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);ConfigurationInfo configurationInfo = activityManager.getDeviceConfigurationInfo();supportsEs2 = configurationInfo.reqGlEsVersion >= 0x2000;boolean isEmulator = Build.VERSION.SDK_INT > Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1&& (Build.FINGERPRINT.startsWith("generic")|| Build.FINGERPRINT.startsWith("unknown")|| Build.MODEL.contains("google_sdk")|| Build.MODEL.contains("Emulator")|| Build.MODEL.contains("Android SDK built for x86"));supportsEs2 = supportsEs2 || isEmulator;}@Overrideprotected void onPause() {super.onPause();if (glView != null) {glView.onPause();}}}GLSurfaceView的setRenderer方法接收一個Renderer用于顯示3D模型的視圖。checkSupported方法檢測當(dāng)前設(shè)備是否支持OpenGL。
至此,我們就可以在手機上看到一個旋轉(zhuǎn)的三角型了,下面看看如何實現(xiàn)一個旋轉(zhuǎn)的三棱錐。
實現(xiàn)三棱錐
這里就涉及到了“面”相關(guān)的概念,什么是前面,什么是后面,以及多少個點組成一個面。如果面沒搞清楚,很可能就畫不出一個3D視圖了。
效果圖:
實現(xiàn)代碼如下(PyramidRenderer .java):
public class PyramidRenderer implements GLSurfaceView.Renderer {private float[] mTriangleArray = {0f, 1f, 0f, //p0-1f, -1f, 0f, //p11f, -1f, 0f, //p20f, 0f, 1f //p3};//定義面的頂點的順序很重要 因為頂點的順序定義了面的朝向(前向或是后向)private short indices[] = new short[]{0, 1, 2,0, 3, 1,0, 2, 3,2, 1, 3};private float[] colors = {0f, 0f, 0f, 1f,0f, 0f, 1f, 1f,0f, 1f, 0f, 1f,1f, 0f, 0f, 1f,};private FloatBuffer vertexBuffer;private FloatBuffer colorBuffer;private ShortBuffer indexBuffer;private float angle;public PyramidRenderer() {//點相關(guān)//先初始化buffer,數(shù)組的長度*4,因為一個float占4個字節(jié)ByteBuffer bb = ByteBuffer.allocateDirect(mTriangleArray.length * 4);//以本機字節(jié)順序來修改此緩沖區(qū)的字節(jié)順序bb.order(ByteOrder.nativeOrder());vertexBuffer = bb.asFloatBuffer();//將給定float[]數(shù)據(jù)從當(dāng)前位置開始,依次寫入此緩沖區(qū)vertexBuffer.put(mTriangleArray);//設(shè)置此緩沖區(qū)的位置。如果標(biāo)記已定義并且大于新的位置,則要丟棄該標(biāo)記。vertexBuffer.position(0);//顏色相關(guān)ByteBuffer bb2 = ByteBuffer.allocateDirect(colors.length * 4);bb2.order(ByteOrder.nativeOrder());colorBuffer = bb2.asFloatBuffer();colorBuffer.put(colors);colorBuffer.position(0);//面相關(guān)ByteBuffer bb3 = ByteBuffer.allocateDirect(indices.length * 2);bb3.order(ByteOrder.nativeOrder());indexBuffer = bb3.asShortBuffer();indexBuffer.put(indices);indexBuffer.position(0);}@Overridepublic void onSurfaceCreated(GL10 gl, EGLConfig config) {// 設(shè)置白色為清屏gl.glClearColor(1, 1, 1, 1);gl.glEnable(GL10.GL_DEPTH_TEST); // 啟用深度緩存gl.glClearDepthf(1.0f); // 設(shè)置深度緩存值gl.glDepthFunc(GL10.GL_LEQUAL); // 設(shè)置深度緩存比較函數(shù)gl.glShadeModel(GL10.GL_SMOOTH);// 設(shè)置陰影模式GL_SMOOTH// Really nice perspective calculations.gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST);}//屏幕解鎖打開、屏幕發(fā)生旋轉(zhuǎn)、對象創(chuàng)建時@Overridepublic void onSurfaceChanged(GL10 gl, int width, int height) {float ratio = (float) width / height;// 設(shè)置OpenGL場景的大小,(0,0)表示窗口內(nèi)部視口的左下角,(w,h)指定了視口的大小gl.glViewport(0, 0, width, height);// 設(shè)置投影矩陣gl.glMatrixMode(GL10.GL_PROJECTION);// 重置投影矩陣gl.glLoadIdentity();// 設(shè)置視口的大小gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);//以下兩句聲明,以后所有的變換都是針對模型(即我們繪制的圖形)gl.glMatrixMode(GL10.GL_MODELVIEW);gl.glLoadIdentity();}@Overridepublic void onDrawFrame(GL10 gl) { // 通知OpenGL使用單一的顏色來渲染 // gl.glColor4f(1.0f, 0.0f, 0.0f, 1.0f);// 清除屏幕和深度緩存gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);gl.glLoadIdentity();gl.glTranslatef(0, 0, -4);gl.glRotatef(angle, 0, 1, 0);gl.glFrontFace(GL10.GL_CCW);//打開忽略后面設(shè)置gl.glEnable(GL10.GL_CULL_FACE);//兩個參數(shù)GL_FRONT和GL_BACK分別表示禁用多邊形正面或者背面上的光照、陰影和顏色計算及操作,消除不必要的渲染計算gl.glCullFace(GL10.GL_FRONT);gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);gl.glColorPointer(4, GL10.GL_FLOAT, 0, colorBuffer);gl.glEnableClientState(GL10.GL_COLOR_ARRAY);gl.glDrawElements(GL10.GL_TRIANGLES, indices.length,GL10.GL_UNSIGNED_SHORT, indexBuffer);gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);gl.glDisable(GL10.GL_CULL_FACE);//繪制結(jié)束gl.glFinish();angle++;} }然后,修改一下MainActivity.java中的代碼,將TrangleRenderer替換為PyramidRenderer就可以在屏幕上顯示出一個旋轉(zhuǎn)的三棱錐了。
歡迎關(guān)注公眾號,有什么問題可以交流。
總結(jié)
以上是生活随笔為你收集整理的Android OpenGL ES 画出三棱锥的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 基于STM32的OV7725摄像头拍照实
- 下一篇: HAC集群中,计划重新初始化数据库使用原