OpenGL编程

OpenGL作为强大的图形接口,可以画出丰富且动感十足的动画,但因涉及到很多理论东西和数学知识,所以往往看书能看死一片人,来直接看看源代码吧,里面包含了详细的注解:当然,我们还是先看看效果图:


首先我们需要实现GLSurfaceView.Renderer这样一个内部接口来为我们绘制图形,看源码AbstractRenderer文件:

public abstract class AbstractRenderer implements GLSurfaceView.Renderer {

	//	GL10接口包含了java(TM)程序语言为OpenGL绑定的核心功能
	public void onDrawFrame(GL10 gl) {
		
		// glDisable()方法是关闭某些功能,下面这句是关闭抗抖动
		gl.glDisable(GL10.GL_DITHER);
		
		// glClear()方法时擦除绘图表面
		// GL_COLOR_BUFFER_BIT --- 颜色缓冲区
		// GL_DEPTH_BUFFER_BIT --- 深度缓冲区
		// GL_STENCIL_BUFFER_BIT --- 模型缓冲区
		gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
		
		// glMatrixMode指明那个矩阵是当前矩阵
		// GL_MODELVIEW --- 应用视图矩阵堆的后续矩阵操作
		// GL_PROJECTION --- 应用投射矩阵堆的后续矩阵操作
		// GL_TEXTURE --- 应用文理矩阵堆的后续矩阵操作
		// GL_MATRIX_PALETTE_OES --- 启用矩阵调色板堆栈扩展,并应用矩阵调色板堆栈后续操作 
		gl.glMatrixMode(GL10.GL_MODELVIEW);
		
		// 使用特征矩阵代替当前矩阵
		gl.glLoadIdentity();
		
		// 辅助函数,提供一个更直观的方法来设置modelview变换矩阵,也就是控制照相机的方向
		// 前三个浮点参数: 指定观测点的空间座标
		// 中间三个浮点参数:指定被观测者物体的参考点的座标
		// 后面三个浮点参数: 指定观测点方向为“上”的向量
		// 注:这些座标都是采用世界座标
		GLU.gluLookAt(gl, 0, 0, -5, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
		
		// 启动客户端的某项功能
		gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
		
		// 开始绘画,叫给子类完成
		draw (gl);
	}

	public void onSurfaceChanged(GL10 gl, int width, int height) {
		
		// 控制屏幕大小或者相机“胶片”的尺寸
		gl.glViewport(0, 0, width, height);
		float ratio = (float) width / height;
		gl.glMatrixMode(GL10.GL_PROJECTION);
		gl.glLoadIdentity();
		
		//控制视体或缩放级别
		gl.glFrustumf(-ratio, ratio, -1, 1, 3, 7);
	}

	public void onSurfaceCreated(GL10 gl, EGLConfig config) {
		gl.glDisable(GL10.GL_DITHER);
		
		// 当拥有解释的空间时,GL某些方面的行为可以由hints控制
		gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);
		gl.glClearColor(.5f, .5f, .5f, 1);
		gl.glShadeModel(GL10.GL_SMOOTH);
		gl.glEnable(GL10.GL_DEPTH_TEST);
	}
	
	protected abstract void draw (GL10 gl);
	
}



这样我们在自定义一个实现类,来绘制我们自己的图形,SimpleTriangleRender文件:

public class SimpleTriangleRender extends AbstractRenderer {

	// 三角形的三个点
	private final static int VERTS = 3;

	private FloatBuffer mFVertexBuffer;
	private ShortBuffer mIndexBuffer;

	public SimpleTriangleRender(Context context) {

		// 创建一个字节缓冲区,每个点有3个浮点值,因为它有三个座标,每个浮点值占用4字节,所以需要3 * 4,而一个三角
		// 行有3个顶点,故需要3 * 3 * 4个字节空间来存放
		ByteBuffer vbb = ByteBuffer.allocateDirect(VERTS * 3 * 4);

		// 使用本机字节顺序对缓冲字节进行排序
		vbb.order(ByteOrder.nativeOrder());

		// 收集缓冲字节到本地缓冲区中
		mFVertexBuffer = vbb.asFloatBuffer();

		ByteBuffer ibb = ByteBuffer.allocateDirect(VERTS * 2);
		ibb.order(ByteOrder.nativeOrder());
		mIndexBuffer = ibb.asShortBuffer();

		// 一次放入数据,下同
		float[] coords = { -0.5f, -0.5f, 0, 0.5f, -0.5f, 0, 0.0f, 0.5f, 0 };
		for (int i = 0; i < VERTS; i++) {
			for (int j = 0; j < 3; j++) {
				mFVertexBuffer.put(coords[i * 3 + j]);
			}
		}
		short[] myIndecesArray = { 0, 1, 2 };
		for (int i = 0; i < 3; i++) {
			mIndexBuffer.put(myIndecesArray[i]);
		}
		mFVertexBuffer.position(0);
		mIndexBuffer.position(0);
	}

	protected void draw(GL10 gl) {

		// 设置当前颜色, R,G,B,alpha
		gl.glColor4f(1.0f, 0, 0, 0.5f);

		// 定义一个顶点座标矩阵
		// 第一个参数是维数,二维则为2,三维则为3
		// 第二个参数表示座标需要解释为浮点数
		// 第三个参数表示每个点分开的字节数
		// 第四个参数是指向缓冲区的指针
		gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mFVertexBuffer);

		// 按照OpenGL ES所支持的一种原始形状来绘制这些点
		// 第一个参数:绘制的几何图形
		// 第二个参数:指明被渲染的元素个数
		// 第三个参数:指向索引指的类型
		// 第四个参数:指向索引缓冲区
		gl.glDrawElements(GL10.GL_TRIANGLES, VERTS, GL10.GL_UNSIGNED_SHORT,
				mIndexBuffer);
	}

}

最后我们在Activity中呈现我们所画出的东西出来,OpenGLTestHarnessActivity文件:

public class OpenGLTestHarnessActivity extends Activity {

	//***************************************
	//	GLSurfaceView类提供如下功能:
	//	* 在OpenGL ES和view系统之间建立联系
	//	* 使得OpenGL ES可以工作在Activity生命周期中
	//	* 可以选择合适的frame buffer像素格式
	//	* 创建并管理一个单独的渲染线程,可以实现平滑的动画
	//	* 提供debugging工具和API
	//***************************************
	private GLSurfaceView mTestHarness = null;

	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		// setContentView(R.layout.activity_main);
		mTestHarness = new GLSurfaceView(this);
		
		//If no setEGLConfigChooser method is called, then by default the view will choose an RGB_565 
		//surface with a depth buffer depth of at least 16 bits.
		//	无需特殊的EGL配置选择程序,采用默认的配置即可
		mTestHarness.setEGLConfigChooser(false);	
		
		//	GLSurfaceView.Renderer接口支持使用派生类进行绘制。它允许GLSurfaceView在表面发生改变时调用它来进行绘制,
		//  这是程序员通常使用的主要接口
		mTestHarness.setRenderer(new SimpleTriangleRender (this));
		
		// 渲染模式	1、GLSurfaceView.RENDERMODE_WHEN_DIRTY --- 通知渲染;一般是等待用户交互时进行渲染
		//			2、GLSurfaceView.RENDERMODE_CONTINUOUSLY --- 持续渲染;大多数3D游戏都是进行持续渲染的
		mTestHarness.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
		
		setContentView(mTestHarness);
	}

	protected void onPause() {
		super.onPause();
		mTestHarness.onPause();
	}

	protected void onResume() {
		super.onResume();
		mTestHarness.onResume();
	}
	
	

}


到此基本上就结束了,你可以将OpenGLTestHarnessActivity设置为主Activity,也可以在主Activity中通过Intent来启动这个Activity,最后看到了结果。

我们总结一下使用OpenGL ES画图的基本步骤吧:

 * 1、实现Renderer接口
 * 2、在呈现器的实现中提供绘图所必需的Camera设置
 * 3、在实现的onDrawFrame方法中提供绘图代码
 * 4、构造GLSurfaceView
 * 5、设置在GLSurfaceView中实现的呈现器
 * 6、指定是否需要将GLSurfaceView制作成动画
 * 7、在Activity中将GLSurfaceView设置为内容视图(也可以在使用常规视图的地方使用此视图)

发布了22 篇原创文章 · 获赞 19 · 访问量 23万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章