一、概述
偶然看到一个游戏的人物动画,联想到在安卓中绘制3D图形,因为基本在平时的开发中基本都是用canvas绘制自定义View,很少使用这些图形的绘制,因此借此机会练练基本操作,国际惯例看下运行效果
这个gif有BUG,开始我以为没录上,所以期动后愣了一会才反应过来,所以有一些卡顿,凑活看吧。。。。
二、OpenGL与OpenGL ES
OpenGl的全称 Open Graphics Library即开放的图形接口,它定义了一个跨编程语言、跨平台的编程接口规范,它主要用于三维图形的绘制,但在手机等手持终端上运行OpenGl有些不太合适,因此安卓系统内置的是OpenGL ES。
三、2D图形的绘制
先来点简单的部分,万一直接来复杂的部分,吓跑同学怎么办。。。
在Activity中使用:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GLSurfaceView glSurfaceView = new GLSurfaceView(this);
MyRenderer renderer = new MyRenderer();
glSurfaceView.setRenderer(renderer);
setContentView(glSurfaceView);
}
代码很简单,仔细看代码,就可以看出来里面除了创建GLSurfaceView的对象外,只有MyRenderer,一看便知道基本代码中以My开头的类,都是自己实现的子类,所以2D图形绘制也一样,最主要的就是提供一个Renderer;
public class MyRenderer implements GLSurfaceView.Renderer {
public MyRenderer() {
triangleDataBuffer = floatBufferUtil(triangleData);
rectDataBuffer = floatBufferUtil(rectData);
rectDataBuffer2 = floatBufferUtil(rectData2);
pentacleBuffer = floatBufferUtil(pentacle);
triangleColorBuffer = intBufferUtil(triangleColor);
rectColorBuffer = intBufferUtil(rectColor);
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
//关闭抖动
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) {
gl.glViewport(0,0,width,height);
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadIdentity();
float ratio = (float) width/height;
gl.glFrustumf(-ratio,ratio,-1,1,1,10);
}
@Override
public void onDrawFrame(GL10 gl) {
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
gl.glMatrixMode(GL10.GL_MODELVIEW);
//-------------------------绘制图形-------------------------
//重置当前的模型试图矩阵
gl.glLoadIdentity();
//控制图形中心位置
gl.glTranslatef(-0.32f,0.35f,-1.2f);
//设置定点数据
gl.glVertexPointer(3, GL10.GL_FLOAT,0,triangleDataBuffer);
//设置颜色数据
gl.glColorPointer(4, GL10.GL_FIXED,0,triangleColorBuffer);
//绘制图形
gl.glDrawArrays(GL10.GL_TRIANGLES,0,3);
gl.glLoadIdentity();
gl.glTranslatef(0.6f,0.8f,-1.5f);
gl.glRotatef(rotate,0f,0f,0.1f);
gl.glVertexPointer(3, GL10.GL_FLOAT,0,rectDataBuffer);
gl.glColorPointer(4, GL10.GL_FIXED,0,rectColorBuffer);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP,0,4);
gl.glLoadIdentity();
gl.glTranslatef(-0.4f,-0.5f,-1.5f);
gl.glRotatef(rotate,0f,0.2f,0f);
gl.glVertexPointer(3, GL10.GL_FLOAT,0,rectDataBuffer2);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP,0,4);
gl.glLoadIdentity();
gl.glTranslatef(0.4f,-0.5f,-1.5f);
gl.glVertexPointer(3, GL10.GL_FLOAT,0,pentacleBuffer);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP,0,5);
gl.glFinish();
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
rotate +=1;
}
创建MyRenderer实现Renderer,重写
- onDrawFrame(GL10 gl) ;Renderer对象调用该方法绘制GLSurfaceView当前帧
- onSurfaceChanged();当GLSurfaceView的大小改变时回调该方法
-
onSurfaceCreated();当GLSurfaceView创建时调用
具体每个方法详情请参考上面代码中的注释,这里偷个懒不想一一解释了,另外里面定义两个方法intBufferUtil()和floatBufferUtil()将矩阵转换为Buffer:
private IntBuffer intBufferUtil(int[] ints){
IntBuffer mBuffer;
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(ints.length*4);
byteBuffer.order(ByteOrder.nativeOrder());
mBuffer = byteBuffer.asIntBuffer();
mBuffer.put(ints);
mBuffer.position(0);
return mBuffer;
}
private FloatBuffer floatBufferUtil(float[] floats){
FloatBuffer mBuffer;
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(floats.length*4);
byteBuffer.order(ByteOrder.nativeOrder());
mBuffer = byteBuffer.asFloatBuffer();
mBuffer.put(floats);
mBuffer.position(0);
return mBuffer;
}
到此为止2D图形绘制完成,离最终目标只差一步,就是让图形旋转起来,其实很简单就是一个旋转属性
gl.glRotatef(rotate,0f,0.2f,0f);
四、3D图形绘制
只需修改Renderer,其余像2D图形绘制一样,如果我们按前面的过程绘制出3D效果,那么如何按顺序组织三维空间的每个顶点,为了解决此方法,安卓提供了glDrawElements(int mode, int count ,int type, Buffer indices),参数:
- mode; 绘制图形的类型,GL10.GL_TRIANGLE_STRIP或GL10.GL_TRIANGLE
- count ;顶点数量
- indices;顶点坐标数据源,注意的是一维数组,每三个数字代表一个顶点
其余都参照2D,这里只贴出不一样的部分:
//重置当前的模型试图矩阵
gl.glLoadIdentity();
//控制图形中心位置
gl.glTranslatef(-0.6f,0.0f,-1.5f);
gl.glRotatef(rotate,0f,0.2f,0f);
//设置定点数据
gl.glVertexPointer(3, GL10.GL_FLOAT,0,taperVerticesBuffer);
//设置颜色数据
gl.glColorPointer(4, GL10.GL_FIXED,0,taperColorBuffer);
//绘制图形
gl.glDrawElements(GL10.GL_TRIANGLE_STRIP,taperFacetsBuffer.remaining(), GL10.GL_UNSIGNED_BYTE,taperFacetsBuffer);
到此大功告成,代码很简单只做一个简单的演示;