2012/3/19
第二节
这节讲Renderer。首先,定义一个自己的Renderer类,比如,MyRenderer。这个类要实现Renderer接口。代码如下:
public class MyRenderer implements Renderer
{
public void onDrawFrame(GL10 gl) {}
public void onSurfaceChanged(GL10gl, intwidth, intheight) {}
public void onSurfaceCreated(GL10gl, EGLConfig config) {}
}
这里讲一下implements。Java中有两个关于继承的很相似的东西,extends和implements关键字。extends和就是C++里面的继承,但是Java里面没有多重继承,所以设计了implements。这里有个帖子讲得特别好,http://apps.hi.baidu.com/share/detail/16567303。大概什么意思呢?就是说,把方法看为对象,拥有几个方法的实体为一个新的对象。这时使用implements,来实现各个方法。我说得很抽象,不懂的话最好还是看下上面这个帖子。
然后来说下Renderer。我认为可以看为一个容器,其中布满了各种要渲染的对象,比如几个立方体,以及他们的颜色等。在实现Renderer的时候,可以直接利用eclipse的补全功能写出需要实现的接口。分别讲一下。
onSurfaceCreated(GL10gl, EGLConfig arg1)中,需要做很多初始化工作,一般来说,有这么几件事。
1. 设置清屏时背景的颜色。
gl.glClearColor(0.0f,0.0f, 0.0f, 1.0f);
2. 开启深度测试。
gl.glEnable(GL10.GL_DEPTH_TEST);
3. 启用背面裁剪。
gl.glEnable(GL10.GL_CULL_FACE);
4. 设置初始深度值。
gl.glClearDepthf(1.0f);
5. 设置深度判定规则。
gl.glDepthFunc(GL10.GL_LEQUAL);
6. 选择颜色过渡模式。
gl.glShadeModel(GL10.GL_SMOOTH);
接下来说onSurfaceChanged(GL10gl, int w, int h)。要做这么几件事:
1. 设置视口。什么是视口?可以理解为窗口,就是没有windows中窗口的边缘罢了。换句话说,就是设置你的程序在屏幕上所占的空间。一般设为全屏。
gl.glViewport(0, 0, w, h);
2. 设置透视模式。这里涉及到几个函数:gl.glMatrixMode(GL10.GL_PROJECTION),gluPerspective()和glOrtho()。详细的介绍可以回顾我之前在csdn上发的帖子:http://blog.youkuaiyun.com/ouchz/article/details/7213338。这里大概讲下。glMatrixMode是用来指定哪一个矩阵是当前矩阵,而它的参数代表要操作的目标。GL_PROJECTION是对投影矩阵操作,后面一般跟gluPerspective,设置锥形视图。GL_MODELVIEW是对模型视景矩阵操作,后面一般跟glulookat,设置视点等。顺便讲下glOrtho(),,这叫做正交视图,和锥形视图对比起来理解就很好懂了。一般,我们写三维场景,都是用锥形视图而非正交视图。代码如下:
publicvoid onSurfaceChanged(GL10 gl, int w, int h) {
gl.glViewport(0,0, w, h);
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadIdentity();
GLU.gluPerspective(gl,45.0f,((float)w)/h, 0.1f, 15f);
}
最后是onDrawFrame。要绘制的各种模型就在这里面设置。抛开光照、纹理等不讲,先从最简单的入手。
1. 清除屏幕和深度缓存
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
2. 设置视景矩阵。
gl.glClear(GL10.GL_COLOR_BUFFER_BIT|GL10.GL_DEPTH_BUFFER_BIT);
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
GLU.gluLookAt(gl,eyex,eyey,eyez,0,0, 0, 0, 1, 0);
3. 定义一个float数组,装入坐标信息。
4. glVertexPointer关联顶点数组。该函数的第四个参数类型是Buffer,需要对float类型进行转换。Java里面有个FloatBuffer,其中有个wrap方法,可以把float类型转换为FloatBuffer。但是,我自己在使用时,发现float数组比较大的时候,程序会异常。所以,推荐使用下面这个功能函数:
publicFloatBuffer makeFloatBuffer(float[] arr) {
ByteBuffer bb =ByteBuffer.allocateDirect(arr.length * 4); bb.order(ByteOrder.nativeOrder());
FloatBufferfb = bb.asFloatBuffer();
fb.put(arr);
fb.position(0);
returnfb;
}
另外,详细讲解下glVertexPointer的几个参数:
voidglVertexPointer(GLint size,GLenum type,GLsizei stride,const GLvoid *pointer)
size,指绑定数组后,每个图形的顶点会用到数组中多少个数据做为顶点的数据。Type,规定调用数组的数据时应该用什么类型。一般与定义的数组的类型是一至的。Stride,规定每个顶点应该从数组中移动的字节。一般情况下写0系统会自动识别。识别方式为size*sizeof(数组定义时报类型)。Pointer,要绑定数组的地址。
5. 启用顶点数组。
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
6. 绘制操作。比如,旋转、平移、上色、绘制。代码如下:
gl.glRotatef(xrot, 1, 0, 0); //绕着(0,0,0)与(1,0,0)即x轴旋转
gl.glRotatef(yrot, 0, 1, 0);
gl.glTranslatef(3f, 0, 0);
gl.glColor4f(1.0f, 0, 0, 1.0f); //设置颜色,红色
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4); //绘制正方型FRONT面
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 4, 4);
讲下这个函数:glDrawArrays。参数一指明以TRIANGLE_STRIP的方式画面,参数二是开始绘制的顶点号,参数三是一共要绘制的顶点数。
另外,glRotatef的第一个参数是角度,以度为单位。方便测试的话,可以写45度。
7. 还有一个很重要的,构造函数。在里面做各种数据的初始化。可以把数组的wrap工作放到里面。
public MyRenderer(){};


被折叠的 条评论
为什么被折叠?



