之前一直想去看看OPEN-GL的文章,也想了解下在Android平台下是如何利用它去开发一个3D场景,比如游戏,或虚拟场景。看了gongziya的博客,他记录自己学习Android平台的OPENGL开发的过程,于是也想记录下自己的一些轨迹,也算是对学习的一个总结。
本文基本上是参考gongziya的文章(http://gongziya.com/721/android%E7%9A%84opengl%E6%95%99%E7%A8%8B%E7%AC%AC%E4%B8%80%E8%AF%BE%EF%BC%9A%E5%88%9B%E5%BB%BA%E7%AA%97%E5%8F%A3/)
在对该文进行测试,理解的基础上,逐渐加入自己的一些困惑和想法,希望能最终形成一个适合自己的文档。
【目的】
了解OPENGL的开发过程,通过学习系列教程,能顺利开发常规的3D游戏,能了解OPENGL的机理;如果能将OPENGL和meeting中的PD,AS结合起来,那更是一件美妙的事情。
【计划】
9.10-14,学习完gongziya的教程;每日作出总结。
9.17-21,。。。【更新中】
***********************开始吧**********************************
Android下OPENGL的开发,并不需要特别的环境,只要Android的SDK即可。gongziya是熟悉OPENGL,而不大了解Andrroid,我则刚好相反,个人感觉应该对OPENGL有一定的了解,才能很好的理解设计的思路。
【第一部分 GLSurfaceView】
Android为我们提供了GLSurfaceView,能方便地显示自己的OPENGL视图,在GLSurfaceView类中包含了一个专门用于渲染3D的接口Renderer。所以想要使用OPENGL渲染,必要创建一个OPENGLRender类,实现这个接口:
class OpenGLRender implements Renderer
{
@Override
public void onDrawFrame(GL10 gl) {
// draw what you want to draw
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
// when the screen changed, do the things here. For example, rotate the screen.
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
// do the initialize work here.
}
}
这个接口中的三个方法作用分别如下:
onDrawFrame:绘图的代码;
onSurfaceChanged:屏幕发生变化时的操作,旋转等;
onSurfaceCreated: 初始化工作
将这个Render应用到Activity的代码很简单:
Renderer render = new OpenGLRenderer();//创建render
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
GLSurfaceView glView = new GLSurfaceView(this);//创建GLSurfaceView
glView.setRenderer(render);//设置render,使之生效
setContentView(glView);//把glview设置为屏幕内容
}
这样,如何实现Render中的三个方法,就成为了重中之重,也是我们发挥想象力的地方。
【第二部分 Renderer 基本方法】
(1) onSurfaceCreated
在这里进行初始化工作,设置各个参数:
gl.glClearColor: 设置屏幕背景;
gl.glShadeModel: 设置阴影平滑;
//设置深度缓存,通过深度缓存可以让OpenGL知道场景中物体的深度关系,这样我们在绘制物体时,不会把后面的物体绘制到前面的物体上。
gl.glClearDepthf, gl.glEnable, gl.glDepthFunc,主要由这三个方法控制。
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST); //设置透视性能----这个有什么好处?
Q1:初始化中,要进行哪些必要的设置?
(2) onSurfaceChanged
当屏幕发生改变的时候,需要在这里进行相关的操作,不管如何,该方法至少调用一次。
// Sets the current view port to the new size.
gl.glViewport(0,0, width, height);
// Select the projection matrix
gl.glMatrixMode(GL10.GL_PROJECTION); -----设置投影矩阵
// Reset the projection matrix
gl.glLoadIdentity();--------------------------重置投影矩阵
// Calculate the aspect ratio of the window
gl.glFrustumf(-ratio, ratio, -1,1,1,10);---设置视口大小 参考http://gongziya.com/693/opengl中的投影和视口变换/
// Select the modelview matrix
gl.glMatrixMode(GL10.GL_MODELVIEW); -----------选择模型观察矩阵
// Reset the modelview matrix
gl.glLoadIdentity();---------------------------重置模型观察矩阵
Q2:选择和重置投影矩阵,模型观察矩阵,有什么作用?窗口,视口的概念是什么?
(3) onDrawFrame
绘制屏幕中的图形。
// 重置当前的模型观察矩阵
gl.glLoadIdentity();
// 左移 1.5 单位,并移入屏幕 6.0
gl.glTranslatef(-1.5f,0.0f, -6.0f);-------------------设置图形位置?
//启用定点数组
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);----------顶点数组是什么概念?
// 设置三角形顶点 (int size, int type, int stride, Buffer pointer)
gl.glVertexPointer(3, GL10.GL_FLOAT, 0,TrianBuffer);---3:xyz三维;type表明是浮点型;stride连续顶点之间的偏移量??pointer是顶点坐标数据的顶点缓存。strider和pointer如何理解?
//绘制三角形glDrawArrays(intmode,intfirst,intcount) ---count标明数组个数,从索引为0开始,总共有3个
gl.glDrawArrays(GL10.GL_TRIANGLES,0,3);
/* 渲染正方形 */
// 重置当前的模型观察矩阵
gl.glLoadIdentity();
// 左移 1.5 单位,并移入屏幕 6.0
gl.glTranslatef(1.5f,0.0f, -6.0f);
//设置和绘制正方形
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, QuaterBuffer);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP,0,4);
//取消顶点数组
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
调用glLoadIdentity()之后,您实际上将
当前点移到了屏幕中心,X坐标轴从左至右,Y坐标轴从下至上,Z坐标轴从里至外。OpenGL屏幕中心的坐标值是X和Y轴上的0.0f点。中心左面的坐标值是负值,右面是正值。移向屏幕顶端是正值,移向屏幕底端是负值。移入屏幕深处是负值,移出屏幕则是正值。
glTranslatef(x,y,z)沿着 X,Y 和 Z 轴移动。根据前面的次序,下面的代码沿着X轴左移1.5个单位,Y轴不动(0.0f),最后移入屏幕6.0f个单位。注意在glTranslatef(x,y,z)中当您移动的时候,您并不是相对屏幕中心移动,而是相对与当前所在的屏幕位置。
【第三部分 添加颜色和动画效果】
颜色和动画,都是属于onDrawFrame里面的内容,也就是实际上在屏幕上要显示出来的东西。
(1) 添加颜色
添加颜色的逻辑比较简单,主要分为四步:构造颜色数组(你要给该图形填充什么颜色);启用颜色数组;绘画;关闭颜色数组;
A. 构建颜色数组
//三角形的顶点颜色值(r,g,b,a)
private float colorvertices[] =
{
1.0f,0.0f,0.0f,1.0f,
0.0f,1.0f,0.0f,1.0f,
0.0f,0.0f,1.0f,1.0f,
};
private FloatBuffer ColorBuffer;
//给colorBuffer赋值,这个操作同之前构造三角形,四边形的动作是一样,需要构造一个能被OPENGL接收的数据类型Buffer
//下面设置三角形颜色数据
ByteBuffer cbb = ByteBuffer.allocateDirect(colorvertices.length * 4);
cbb.order(ByteOrder.nativeOrder());
ColorBuffer = cbb.asFloatBuffer();
ColorBuffer.put(colorvertices);
ColorBuffer.position(0);
B. 启用颜色数组
gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
C. 绘画
gl.glColorPointer(4, GL10.GL_FLOAT, 0, ColorBuffer);
D. 关闭颜色数组
gl.glDisableClientState(GL10.GL_COLOR_ARRAY);
【注意】启用和关闭的动作是成对出现的;如果是单色填充,则不需要启用颜色数组这个操作。直接填充即可。 gl.glColor4f(0.5f, 0.5f, 1.0f, 1.0f);
(2) 添加动画
动画旋转的操作,主要通过glRotatef(Angle,Xvector,Yvector,Zvector)来实现,它负责让对象绕某个轴旋转。这个命令有很多用处(用处体现在哪儿?)。
Angle 通常是个变量代表对象转过的角度。
Xvector,Yvector和Zvector三个参数则共同决定旋转轴的方向。形象地理解,可以根据这三个参数画一个轴,图形就是围绕该轴转动。
//设置旋转
gl.glRotatef(rotateTri, 0.0f, 1.0f, 0.0f);
【注意】通过该例子明白,onDrawFrame是每帧都会调用的,不断刷新;gl.glRotatef的动作,要放置在gl.glTranslatef 动作之后,即先定位置,再旋转;
我们说了很久的顺时针,逆时针画图,到底有什么用呢
gl.glEnable(GL10.GL_CULL_FACE);
//设置openggl有剔除效果,就是看不到的面就不画,当然可以增加效率
gl.glFrontFace(GL10.GL_CCW);
//设置逆时针方向为正面
gl.glCullFace(GL10.GL_BACK);
//设置背面被剔除,不画
Cull就是剔除的意思