android:GLSurfaceView繪製bitmap圖片及glViewport調整的效果
首先看一下GLSurfaceView是如何繪製的。正如android開發文檔中描述的那樣,我們需要new一個GLSurfaceView對象,然後設定一個實現了Renderer介面的對象,我們需要寫一個MyRender類,實現Renderer的方法等等。我們先看一下Activity的寫法。它不需要一個布局檔案,我們將GLSurfaceView對象作為activity的contentview即可。如下:
package net.mobctrl.glsurfaceviewbmp;import java.util.Random;import android.annotation.SuppressLint;import android.app.Activity;import android.opengl.GLSurfaceView;import android.os.Bundle;import android.view.WindowManager;/** * * @author Zheng Haibo * @web http://www.mobctrl.net * */public class MainActivity extends Activity {private GLSurfaceView glView; // Use GLSurfaceViewRandom rnd = new Random();@SuppressLint("NewApi")@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setTheme(android.R.style.Theme_Translucent_NoTitleBar);getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);glView = new GLSurfaceView(this); // Allocate a GLSurfaceViewglView.setRenderer(new MyGLRenderer(this)); // Use a custom rendererglView.getAlpha();this.setContentView(glView); }@Overrideprotected void onPause() {super.onPause();glView.onPause();}@Overrideprotected void onResume() {super.onResume();glView.onResume();android.provider.Settings.System.putInt(this.getContentResolver(),android.provider.Settings.System.SCREEN_BRIGHTNESS,rnd.nextInt(256));}}
下面,我們主要看一下MyGLRender類的寫法,它實現了Renderer介面,具體如下:
package net.mobctrl.glsurfaceviewbmp;import java.util.Random;import javax.microedition.khronos.opengles.GL10;import android.content.Context;import android.opengl.GLSurfaceView.Renderer;import android.opengl.GLU;/** * @date 2014年10月20日 下午2:49:17 * @author Zheng Haibo * @Description: TODO */public class MyGLRenderer implements Renderer {Context context; // Application's contextRandom r = new Random();//private Square square;private GLBitmap glBitmap;private int width = 0;private int height = 0;private long frameSeq = 0;private int viewportOffset = 0;private int maxOffset = 400;public MyGLRenderer(Context context) {this.context = context;//square = new Square();glBitmap = new GLBitmap();}@Overridepublic void onSurfaceChanged(GL10 gl, int width, int height) {if (height == 0) { // Prevent A Divide By Zero Byheight = 1; // Making Height Equal One}this.width = width;this.height = height;gl.glViewport(0, 0, width, height); // Reset The// Current// Viewportgl.glMatrixMode(GL10.GL_PROJECTION); // Select The Projection Matrixgl.glLoadIdentity(); // Reset The Projection Matrix// Calculate The Aspect Ratio Of The WindowGLU.gluPerspective(gl, 45.0f, (float) width / (float) height, 0.1f,100.0f);gl.glMatrixMode(GL10.GL_MODELVIEW); // Select The Modelview Matrixgl.glLoadIdentity();}/** * 每隔16ms調用一次 */@Overridepublic void onDrawFrame(GL10 gl) {gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);// Reset the Modelview Matrixgl.glLoadIdentity();gl.glTranslatef(0.0f, 0.0f, -5.0f); // move 5 units INTO the screen is// the same as moving the camera 5// units away// square.draw(gl);glBitmap.draw(gl);changeGLViewport(gl);}/** * 通過改變gl的視角擷取 * * @param gl */private void changeGLViewport(GL10 gl) {System.out.println("time=" + System.currentTimeMillis());frameSeq++;viewportOffset++;// The// Currentif (frameSeq % 100 == 0) {// 每隔100幀,重設gl.glViewport(0, 0, width, height);viewportOffset = 0;} else {int k = 4;gl.glViewport(-maxOffset + viewportOffset * k, -maxOffset+ viewportOffset * k, this.width - viewportOffset * 2 * k+ maxOffset * 2, this.height - viewportOffset * 2 * k+ maxOffset * 2);}}@Overridepublic void onSurfaceCreated(GL10 gl,javax.microedition.khronos.egl.EGLConfig arg1) {glBitmap.loadGLTexture(gl, this.context);gl.glEnable(GL10.GL_TEXTURE_2D); // Enable Texture Mapping ( NEW )gl.glShadeModel(GL10.GL_SMOOTH); // Enable Smooth Shadinggl.glClearColor(0.0f, 0.0f, 0.0f, 0.5f); // Black Backgroundgl.glClearDepthf(1.0f); // Depth Buffer Setupgl.glEnable(GL10.GL_DEPTH_TEST); // Enables Depth Testinggl.glDepthFunc(GL10.GL_LEQUAL); // The Type Of Depth Testing To Dogl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST);}}
注意,這裡主要實現了如下三個方法:onSurfaceCreated,onSurfaceChanged和onDrawFrame,其中,關鍵就是onDrawFrame方法,它的繪製機制是:1s的時間內,onDrawFrame方法會調用60次,大約每隔16ms調用一次。我們先看裡面的glBitmap對象,changeGLViewport方法待會再說。
package net.mobctrl.glsurfaceviewbmp;import java.nio.ByteBuffer;import java.nio.ByteOrder;import java.nio.FloatBuffer;import javax.microedition.khronos.opengles.GL10;import net.mobctrl.glsurfaceviewbmp.R;import android.content.Context;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.opengl.GLUtils;/** * @date 2014年10月20日 下午3:09:33 * @author Zheng Haibo * @Description: TODO */public class GLBitmap {private FloatBuffer textureBuffer; // buffer holding the texture coordinatesprivate float texture[] = {// Mapping coordinates for the vertices0.0f, 1.0f, // top left (V2)0.0f, 0.0f, // bottom left (V1)1.0f, 1.0f, // top right (V4)1.0f, 0.0f // bottom right (V3)};private FloatBuffer vertexBuffer; // buffer holding the verticesprivate float vertices[] = { -1.0f, -1.0f, 0.0f, // V1 - bottom left-1.0f, 1.0f, 0.0f, // V2 - top left1.0f, -1.0f, 0.0f, // V3 - bottom right1.0f, 1.0f, 0.0f // V4 - top right};public GLBitmap() {ByteBuffer byteBuffer = ByteBuffer.allocateDirect(vertices.length * 4);byteBuffer.order(ByteOrder.nativeOrder());vertexBuffer = byteBuffer.asFloatBuffer();vertexBuffer.put(vertices);vertexBuffer.position(0);byteBuffer = ByteBuffer.allocateDirect(texture.length * 4);byteBuffer.order(ByteOrder.nativeOrder());textureBuffer = byteBuffer.asFloatBuffer();textureBuffer.put(texture);textureBuffer.position(0);}/** The texture pointer */private int[] textures = new int[1];public void loadGLTexture(GL10 gl, Context context) {// loading textureBitmap bitmap = BitmapFactory.decodeResource(context.getResources(),R.drawable.plane_image);// generate one texture pointergl.glGenTextures(1, textures, 0);// ...and bind it to our arraygl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);// create nearest filtered texturegl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER,GL10.GL_NEAREST);gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER,GL10.GL_LINEAR);// Use Android GLUtils to specify a two-dimensional texture image from// our bitmapGLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);// Clean upbitmap.recycle();}public void draw(GL10 gl) {// bind the previously generated texturegl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);// Point to our buffersgl.glEnableClientState(GL10.GL_VERTEX_ARRAY);gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);// Set the face rotationgl.glFrontFace(GL10.GL_CW);// Point to our vertex buffergl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureBuffer);// Draw the vertices as triangle stripgl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, vertices.length / 3);// Disable the client state before leavinggl.glDisableClientState(GL10.GL_VERTEX_ARRAY);gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);}}
從GLBitmap可以看出,我們首先需要載入一個圖片資源,然後產生texture。在這裡,特別說明一下changeGLViewport這個方法,如果在MyRender裡面去掉這個方法,運行結果是,GLSurfaceView繪製出一張圖片。添加這個方法以後,則是周期性得繪製一個由遠到近的一個“動畫”。注意,這種動畫,並沒有改變映像本身,而且調整不通的視角產生的,因此效率很高。它的原理,就像你拿一個相機,去拍攝牆壁上得一幅畫,相機離畫越來越近時,畫面在放大,而牆壁本身並沒有變化。
-------------------------------------------------------------------
更多交流,Android開發聯盟QQ群:272209595