android opengles 3.0 学习(一):顶点绘制法 VBOs(Vertex Buffer Objects)

前言

目前只有android系统中只有android 4.3或以上支持opengles 3.0,但目前很多运行android 4.3系统的硬件能支持opengles 3.0的也是非常少的。不过幸好,opengles 3.0是向后兼容的,当程序发现硬件不支持opengles 3.0时则会自动调用opengles 2.0的API。废话不多说了,开始进入正题,首先看看本例子的结果:



实现过程:

1. 在manifest中声明程序中使用opengles 3.0

<!-- Tell the system this app requires OpenGL ES 3.0. -->
<uses-feature android:glEsVersion="0x00030000" android:required="true" />
如果程序中使用了纹理压缩的话,还需进行如下声明,以防止不支持这些压缩格式的设备尝试运行程序。

<supports-gl-texture android:name="GL_OES_compressed_ETC1_RGB8_texture" />
<supports-gl-texture android:name="GL_OES_compressed_paletted_texture" />

2.实现两个必不可少的类:GLSurfaceView和GLSurfaceView.Renderer

继承GLSurfaceView类的MySurfaceView.java

package com.gl.gl30_vbos02;

import android.content.Context;
import android.opengl.GLSurfaceView;
import android.util.AttributeSet;
import android.view.MotionEvent;

public class MySurfaceView extends GLSurfaceView {

	private final float TOUCH_SCALE_FACOTOR = 180.0f / 320;
	private GLRender _render = new GLRender();
	
	private float _preX = 0.0f;
	private float _preY = 0.0f;
	
	public MySurfaceView(Context context) 
	{
		super(context);
		// TODO Auto-generated constructor stub
		setEGLContextClientVersion(2);
		this.setRenderer(_render);
		setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
	}

	public MySurfaceView(Context context, AttributeSet attrs) {
		super(context, attrs);
		// TODO Auto-generated constructor stub
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		// TODO Auto-generated method stub
		float x = event.getX();
		float y = event.getY();
		
		switch (event.getAction()) 
		{
			case MotionEvent.ACTION_MOVE:
				float dx = x - _preX;
				float dy = y - _preY;
				
				_render.zrot = dx * TOUCH_SCALE_FACOTOR;
				_render.xrot = dy * TOUCH_SCALE_FACOTOR;
				
				this.requestRender();
				break;
	
			default:
				break;
		}
		_preX = x;
		_preY = y;
		return true;
	}
}

实现GLSurfaceView.Renderer的GLRender.java:

package com.gl.gl30_vbos02;

import java.nio.FloatBuffer;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import android.opengl.GLES30;
import android.opengl.GLSurfaceView.Renderer;
import android.opengl.Matrix;
import android.util.Log;

//@TargetApi(18)
public class GLRender implements Renderer {

	public float xrot, yrot, zrot;
	
	private static final String TAG = "GLRender";
	private final float[] mProjMatrix 	= new float[16];
	private final float[] mVMatrix 		= new float[16];
	private final float[] mMVPMatrix 	= new float[16];
	private final float[] mRotationMatrix = new float[16];
//	private volatile float mAngle;
	
	private CirclePlane _circlePlane;
	//定义环境光
	private FloatBuffer lightAmbient 	= FloatBuffer.wrap(new float[]{0.5f, 0.5f, 0.5f, 1.0f});
	//定义漫散射
	private FloatBuffer lightDiffuse 	= FloatBuffer.wrap(new float[]{1.0f, 1.0f, 1.0f, 1.0f});
	//光源的位置
	private FloatBuffer lightPosition 	= FloatBuffer.wrap(new float[]{0.0f, 0.0f, 2.0f, 1.0f});
	
	public GLRender() {
		// TODO Auto-generated constructor stub
	}

	@Override
	public void onDrawFrame(GL10 gl_unused) {
		// TODO Auto-generated method stub
		//清楚屏幕和深度缓存
		GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT | GLES30.GL_DEPTH_BUFFER_BIT);
		
		Matrix.setLookAtM(mVMatrix, 0, 0, 0, -3, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f);
		Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mVMatrix, 0);
		
		this._circlePlane.Draw(mMVPMatrix);
	}

	@Override
	public void onSurfaceChanged(GL10 gl_unused, int width, int height) {
		// TODO Auto-generated method stub
		float ratio = (float) width / height;
		
		//设置OPENGL视口
		GLES30.glViewport(0, 0, width, height);
		
		//设置矩阵投影参数
		Matrix.frustumM(mProjMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
	}
	
	@Override
	public void onSurfaceCreated(GL10 gl, EGLConfig config) {
		// TODO Auto-generated method stub
		
		//black background
		GLES30.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
	 	this._circlePlane = new CirclePlane(10, 20, 1.0f);
	}
	
	public static int loadShader(int type, String shaderCode)
	{
		// create a shader type (GLES30.GL_VERTEX_SHADER)
		// or a fragment shader type (GLES30.GL_FRAGMENT_SHADER)
		int shader = GLES30.glCreateShader(type);
		GLES30.glShaderSource(shader, shaderCode);
		GLES30.glCompileShader(shader);
		
		return shader;
	}
	 
	public static void checkGLError(String glOperation)
	{
		int error;
		while((error = GLES30.glGetError()) != (GLES30.GL_NO_ERROR))
		{
			Log.e(TAG, glOperation + ": glError " + error);
			throw new RuntimeException(glOperation + ": glError " + error);
		}
	}

}


要完成Opengl es工程,最重要是实现上面两个类。另外本程序为了绘制出图中的图案,还有四个类,可在附件中查看。:

CirclePlane.java //实现图案中顶点缓存,这个类有比较多的数学知识,不过只为了实现图案的话,不用理解其中数学算法问题也没关系。只要修改此类就可以绘制出不同的图案

Vertex3f.java //定义了顶点类型

Until.java //生成顶点缓存的公共工具类

OpenGLES30.java //工程的主类



附录

CirclePlane.java

package com.gl.gl30_vbos02;

import java.nio.FloatBuffer;
import android.opengl.GLES30;


//@TargetApi(18)
public class CirclePlane {
	
	static final int COORDS_PRE_VERTEX = 3;
	private final int vertexStride = COORDS_PRE_VERTEX * 4;
	private int vertexCount = 0;
	
	private int mRow = 10;
	private int mColumn = 20;
	private float mRadius = 1.0f;
	private FloatBuffer mPlaneBuffer;
	
	private final int mProgram;
	private int mPositionHandle;
	private int mColorHandle;
	private int mMVPMatrixHandle;
	
	// Set color with red, green, blue and alpha (opacity) values
    float color[] = { 1.0f, 1.0f, 1.0f, 1.0f }; //白色不透明
	
	private final String vertexShaderCode = 
			"uniform mat4 uMVPMatrix;" + 
			"attribute vec4 vPosition;" + 
			"void main()" +
			"{" +
			"	gl_Position = vPosition * uMVPMatrix;" +
			"}";
	private final String fragmentShaderCode = 
			"precision mediump float;" + 
			"uniform vec4 vColor;" + 
			"void main()" +
			"{" +
			"	gl_FragColor = vColor;" +
			"}";
	
	public CirclePlane(int row, int column, float radius) 
	{
		// TODO Auto-generated constructor stub
		this.mRow = row;
		this.mColumn = column;
		this.mRadius = radius;
		this.createGraphics();
		
		int vertexShader = GLRender.loadShader(GLES30.GL_VERTEX_SHADER, vertexShaderCode);
		int fragmentShader = GLRender.loadShader(GLES30.GL_FRAGMENT_SHADER, fragmentShaderCode);
		
        this.mProgram = GLES30.glCreateProgram();             // create empty OpenGL Program
        GLES30.glAttachShader(this.mProgram, vertexShader);   // add the vertex shader to program
        GLES30.glAttachShader(this.mProgram, fragmentShader); // add the fragment shader to program
        GLES30.glLinkProgram(this.mProgram);                  // create OpenGL program executables
	}
	private void createGraphics()
	{
		Vertex3f vertexs[][] = new Vertex3f[this.mRow][this.mColumn];
		float intervalR = this.mRadius / this.mRow;
		Vertex3f centralPos = new Vertex3f(0.0f, 0.0f, 0.0f);
		for(int i=0;i<this.mRow;i++)
		{
			float tmpR = intervalR * i;
			for(int j=0;j<this.mColumn;j++)
			{
				double angle = 2 * j * Math.PI / (this.mColumn - 1);
				vertexs[i][j] = new Vertex3f((float)(tmpR * Math.cos(angle)), (float)(tmpR * Math.sin(angle)), centralPos.z);
			}
		}
		
		//创建三角形顶点
		int len = 2 * (this.mRow -1) * (this.mColumn - 1) * 3;
		this.vertexCount = len;
		Vertex3f tri[] = new Vertex3f[len];
		int index = 0;
		for(int i=0;i<this.mRow-1;i++)
		{
			for(int j=0;j<this.mColumn-1;j++)
			{
				tri[index] = vertexs[i][j];
				tri[index+1] = vertexs[i+1][j];
				tri[index+2] = vertexs[i+1][j+1];
				
				tri[index+3] = vertexs[i][j];
				tri[index+4] = vertexs[i+1][j+1];
				tri[index+5] = vertexs[i+1][j];
				
				index += 6;
			}
		}
		
		//设置顶点缓存
		float[] plane = new float[len*3];
		for(int i=0;i<len;i++)
		{
			int vertexI = 3 * i;
			plane[vertexI] = tri[i].x;
			plane[vertexI+1] = tri[i].y;
			plane[vertexI+2] = tri[i].z;
		}
		
		this.mPlaneBuffer = Util.getFloatBuffer(plane);
//		plane = null;
	} 
	public void Draw(float[] mvpMatrix)
	{
		GLES30.glUseProgram(this.mProgram);
		this.mPositionHandle = GLES30.glGetAttribLocation(this.mProgram, "vPosition");
		GLES30.glEnableVertexAttribArray(this.mPositionHandle);
		GLES30.glVertexAttribPointer(this.mPositionHandle, COORDS_PRE_VERTEX, 
									GLES30.GL_FLOAT, false, this.vertexStride, this.mPlaneBuffer);
		
		this.mColorHandle = GLES30.glGetUniformLocation(this.mProgram, "vColor");
		GLES30.glUniform4fv(this.mColorHandle, 1, this.color, 0);
		
		this.mMVPMatrixHandle = GLES30.glGetUniformLocation(this.mProgram, "uMVPMatrix");
		GLRender.checkGLError("glGetUniformLocation");
		GLES30.glUniformMatrix4fv(this.mMVPMatrixHandle, 1, false, mvpMatrix, 0);
		GLRender.checkGLError("glUniformMatrix4fv");
		
		GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, this.vertexCount);
		
		GLES30.glDisableVertexAttribArray(this.mPositionHandle);
		
//		gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
//		gl.glVertexPointer(3, GL10.GL_FLOAT, 0, this.mPlaneBuffer);
//		gl.glDrawArrays(GL10.GL_TRIANGLES, 0, (this.mRow-1)*(this.mColumn-1)*2*3);
//		
//		gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
//		gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
//		gl.glFinish();
	}
}


Vertex3f.java

package com.gl.gl30_vbos02;

public class Vertex3f {

	public float x = 0.0f;
	public float y = 0.0f;
	public float z = 0.0f;
	
	public Vertex3f(float x, float y, float z)
	{
		this.x = x;
		this.y = y;
		this.z = z;
	}
}

Until.java

package com.gl.gl30_vbos02;


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




public class Util {


	//获取整形缓冲数据
	public static IntBuffer getIntBuffer(int[] vertexs)
	{
		IntBuffer buffer;
		ByteBuffer qbb = ByteBuffer.allocateDirect(vertexs.length * 4);
		qbb.order(ByteOrder.nativeOrder());
		buffer = qbb.asIntBuffer();
		buffer.put(vertexs);
		buffer.position(0);
		return buffer;
	}
	
	//获取浮点形缓冲数据
	public static FloatBuffer getFloatBuffer(float[] vertexs)
	{
		FloatBuffer buffer;
		
		ByteBuffer qbb = ByteBuffer.allocateDirect(vertexs.length * 4);
		qbb.order(ByteOrder.nativeOrder());
		buffer = qbb.asFloatBuffer();
		buffer.put(vertexs);
		buffer.position(0);
		return buffer;
	}
	
	//获取字节型缓冲数据
	public static ByteBuffer getByteBuffer(byte[] vertexs)
	{
		ByteBuffer buffer = null;
		
		buffer = ByteBuffer.allocateDirect(vertexs.length);
		buffer.put(vertexs);
		buffer.position(0);
		
		return buffer;
	}
}

OpenGLES30.java

package com.gl.gl30_vbos02;

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

public class OpenGLES30 extends Activity {

	private GLSurfaceView mGL30View;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		mGL30View = new MySurfaceView(this);
		setContentView(mGL30View);
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.open_gles30, menu);
		return true;
	}
	
	@Override
	protected void onResume() {
		// TODO Auto-generated method stub
		super.onResume();
		this.mGL30View.onResume();
	}

	@Override
	protected void onPause() {
		// TODO Auto-generated method stub
		super.onPause();
		this.mGL30View.onPause();
	}

}

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值