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设置为内容视图(也可以在使用常规视图的地方使用此视图)