1. 程式人生 > >OpenGL ES繪製3D圖形

OpenGL ES繪製3D圖形

package com.example.tyxiong.myapplication;

import android.app.Activity;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.view.View;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;

import javax.microedition.khronos.egl.EGLConfig;
import
javax.microedition.khronos.opengles.GL10; /*OpenGL與3D開發. *2句概述: * OpenGL是跨平臺,跨語言的開放圖形庫介面. 主要是用於3D圖形開發. * Android系統內建了OpenGL ES支援. OpenGL ES是OpenGL的子集.--只支援繪製三角形 * 對於2D(平面)的三角形而言,由三個點構成,每個點的座標由(x,y)值構成.三個頂處理同一平面上.只需要提供每個頂點的座標值即可 * 對於3D(立體)的三稜錐而言,由4個頂點構成,每個頂點的座標由(x,y,x)值構成,各頂點不在同一平面,需要提供2方面資料:1 3D圖形每個頂點的座標值. 2 3D圖形每個平面由哪些頂點組成. * * 三維座標系:與數學裡座標系相同(螢幕中心點為座標原點) * * Android5.0支援OpenGL ES3.1 android有opengl包, 3工具類:GLSurfaceView GLU GLUtils 功能強大. * * OpenGL ES繪製2D圖形: * GLSurfaceView元件用於顯示3D圖形,其中GLSurfaceView.Renderer來完成繪製SurfaceView中的3D圖形. 使用分3步: * 1 建立GLSurfaceView元件,並用Activity顯示. * 2 建立GLSurfaceView.Renderer介面的子類例項--實現3個方法(方法中有GL10引數 表示畫筆) * 2-1 onSurfaceCreate()--Surface被建立時回撥,方法內常進行初始化GL10引數. * 2-2 onSurfaceChanged()--Surface被改變回調,常進行3D場景的設定. * 2-3 onDrawFrame()--代表繪製的當前幀,繪製3D圖形. * 3 呼叫GLSurfaceView的setRenderer()方法來指定Renderer物件. * * 呼叫GL10繪製2D(平面)圖形的步驟 6步: 只需要提供所有頂點的座標值.glDrawArrays(mode,first,count) * 1 glEnableClientState(GL10.GL_VERTEX_ARRAY)啟用頂點陣列. * 2 glEnableClientState(GL10.GL_COLOR_ARRAY)啟動頂點顏色陣列 * 3 glVertexPointer(size,type,stride,pointer)設定頂點座標值. 每個頂點的值組成元素個數3 值型別int/fix float 步幅0 座標值資料(一維形如x1,y1,z1) * 4 glColorPointer(size,type,stride,pointer) 設定頂點顏色值.(rgbn)4 * 5 glDrawArrays(mode,first,count)繪製2D圖形(平面) GL10.GL_TRIANGLES三角形/GL10.GL_TRIANGLE_STRIP多三角形. 開始頂點 頂點總數. * 6 glFinish()結束繪製,glDisableClientState(int)停用頂點座標和顏色資料. * * * 繪製3D圖形,(立方體)用到方法 glDrawElements(mode,count,type,indices) indices包裝了一個長度為3N(N為三角形數 3個頂點指定一個三角形)的陣列 * * 前面說了需要提供2方面資料:1 所有頂點的座標值(x,y,z). 2 需要指定3D圖形的每個平面由哪三個頂點組成. * 1 所有頂點的座標值. * 2 需要建 indices陣列來指定每個三角形由哪三個頂點組成. * glDrawElements(mode,count,type,indices)//繪製3D圖形,多個三角形 頂點總數(3*三角形數) 頂點indices裡的元素型別 buffer * * 其它相同的操作.. * 下面用的三稜錐例子,比較簡單,無須考慮頂點的排列方式(四面形,由多個三個角形組成頂點排列方式 當然可以用3Dmax軟體自動生成 頂點座標, 平面頂點組成) * * * * * * */
public class MainActivity extends Activity { GLSurfaceView surfaceView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); surfaceView = new GLSurfaceView(this); final MyRenderer myRenderer = new MyRenderer(); surfaceView.setRenderer(myRenderer); surfaceView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);//繪製靜圖,無須重新整理需要重繪時requestRenderer()節省cpu
setContentView(surfaceView); surfaceView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { surfaceView.requestRender();//請求執行onDrawFrame() } }); } } class MyRenderer implements GLSurfaceView.Renderer { float[] rectData = new float[]//三稜錐,4面(4個三角形 )4個頂點 上\左\右\裡 { 0f, 1f, 0f, -1f, -1f, 0f, 1f, -1f, 0f, 0f, 0f, -1f }; byte[] indices = new byte[]//4個三角形的3*4頂點 { 0, 1, 2, 0, 1, 3, 0, 2, 3, 1, 2, 3 }; int[] rectColor = new int[]//rgbn頂點的顏色值(3*4頂點顏色值重複了不必要) { 65535, 0, 0, 0, 0, 65535, 0, 0, 0, 0, 65535, 0, 0, 65535, 65535, 0 }; private float angle = 0f; public FloatBuffer floatUtils(float[] floatArr) {//繪製方法要求資料為Buffer型別 float[]-->FloatBuffer[] ByteBuffer byteBuffer = ByteBuffer.allocateDirect(floatArr.length * 4);//要求用allocateDirect()方法,只有ByteBuffer有該方法,so byteBuffer.order(ByteOrder.nativeOrder()); //要求nativeOrder Java 是大端位元組序(BigEdian), // 而 OpenGL 所需要的資料是小端位元組序(LittleEdian) FloatBuffer floatBuffer = byteBuffer.asFloatBuffer(); floatBuffer.put(floatArr); floatBuffer.flip(); return floatBuffer; } public IntBuffer intUtils(int[] intArr) { ByteBuffer byteBuffer = ByteBuffer.allocateDirect(intArr.length * 4); byteBuffer.order(ByteOrder.nativeOrder()); IntBuffer intBuffer = byteBuffer.asIntBuffer(); intBuffer.put(intArr); intBuffer.flip(); return intBuffer; } @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) {//對GL設定引數 關閉抗抖動 設定透視修正模式 // 清屏黑色 平滑 開啟深度測試 深度測試型別. gl.glDisable(GL10.GL_DITHER); gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST); gl.glClearColor(0, 0, 0, 0); gl.glShadeModel(GL10.GL_SMOOTH); gl.glEnable(GL10.GL_DEPTH_TEST); gl.glDepthFunc(GL10.GL_LEQUAL); } @Override public void onSurfaceChanged(GL10 gl, int width, int height) {//3D視窗場景 視窗大小位置 投影矩陣 // 重置繪製座標原點 透視視窗寬高比 透視視窗空間大小 gl.glViewport(0, 0, width, height); gl.glMatrixMode(GL10.GL_PROJECTION);//距離越遠,小 對映座標系設定為"投影矩陣" gl.glLoadIdentity(); float ratio = (float) width / height; gl.glFrustumf(-ratio, ratio, -1f, 1f, 1f, 10f);//三維座標系的值不是直接取螢幕畫素,是根據該方法 // 設定空間大小範圍內可見最後2個引數為絕對值. } @Override public void onDrawFrame(GL10 gl) {//繪製當圖形 清除螢幕快取深度快取 開啟頂點資料 頂點顏色 設定頂點資料 // 顏色資料 繪製2D圖形 繪製完成 關閉頂點&&顏色資料 gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); gl.glMatrixMode(GL10.GL_MODELVIEW);//需要繪圖時設定為"模型檢視" gl.glLoadIdentity(); gl.glTranslatef(0f, 0f, -5f);//改變繪圖座標原點 gl.glRotatef(angle++, 0f, 0.5f, 0f);//y軸旋轉 gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); gl.glEnableClientState(GL10.GL_COLOR_ARRAY); FloatBuffer pointerData = floatUtils(rectData); IntBuffer pointColor = intUtils(rectColor); gl.glVertexPointer(3, GL10.GL_FLOAT, 0, pointerData); gl.glColorPointer(4, GL10.GL_FIXED, 0, pointColor); gl.glDrawElements(GL10.GL_TRIANGLE_STRIP, 12, GL10.GL_UNSIGNED_BYTE, ByteBuffer.wrap(indices));//Byte[]型別可直接包裝成Buffer // gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4); gl.glFinish(); gl.glDisableClientState(GL10.GL_VERTEX_ARRAY); gl.glDisableClientState(GL10.GL_COLOR_ARRAY); } }