本章将从绘图这方面总结OpenGL ES的绘图实际操作过程,以及绘图的相关原理。
一、 基本绘图过程:
1. GLSurfaceView 用来展示OpenGL ES所绘制的图形,其中封装了Surface。
创建对象: new GLSurfaceView(this); 此处this指的是Activity
2. 使用GLSurfaceView的setRenderer(Renderer renderer);方法关联渲染对象。即要写一个具体类实现Interface GLSurfaceView.Renderer。
实现Renderer接口:
public class MyRender implements GLSurfaceView.Renderer {
// IOPenGLDrawer 是自定义的一个具体绘图接口。
private IOpenGLDrawer drawObject;
public MyRender(IOpenGLDrawer drawObject) {
this.drawObject = drawObject;
}
@Override
public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {
// 设置背景颜色为黑色
gl10.glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
// 设置阴影为平滑模式(非必须的设置)
gl10.glShadeModel(GL10.GL_SMOOTH);
// 设置深度缓存
gl10.glClearDepthf(1.0f);
// 启用深度测试
gl10.glEnable(GL10.GL_DEPTH_TEST);
// 所做深度测试的类型
gl10.glDepthFunc(GL10.GL_LEQUAL);
// 对透视进行修正
gl10.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST);
}
@Override
public void onSurfaceChanged(GL10 gl10, int width, int height) {
gl10.glViewport(0, 0, width, height);
gl10.glMatrixMode(GL10.GL_PROJECTION);
gl10.glLoadIdentity();
GLU.gluPerspective(gl10, 90f, (float)width/(float)height, 0.1f, 100f);
gl10.glMatrixMode(GL10.GL_MODELVIEW);
gl10.glLoadIdentity();
}
@Override
public void onDrawFrame(GL10 gl10) {
// 清除屏幕颜色和深度缓存
gl10.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
// 绘制
drawObject.DrawScene(gl10);
}
}
顺便看一下IOPenGLDrawer接口吧:
public interface IOpenGLDrawer {
void DrawScene(GL10 gl);
}
然后你要绘制的对象,封装一个类(下面我封装了DrawPoint.java、DrawLine.java、Draw20D.java、DrawSolarSystem.java等类,主要是为了了解一下绘制过程,看两个有代表性的,一个绘制点、一个绘制20面体)
——-一大波代码快来袭————————–
DrawPoint.java
public class DrawPoint implements IOpenGLDrawer {
private final FloatBuffer vertexBuffer;
private float vertexArray[] = {
-1f, -0.4f * 1.732f, 0.0f,
0.8f, -0.4f * 1.732f, 0.0f,
0.0f, 0.4f * 1.732f, 0.0f,
0.0f, 0.0f, 0.0f
};
public DrawPoint() {
ByteBuffer vbb = ByteBuffer.allocateDirect(vertexArray.length * 4);
vbb.order(ByteOrder.nativeOrder());
vertexBuffer = vbb.asFloatBuffer();
vertexBuffer.put(vertexArray);
vertexBuffer.position(0);
}
@Override
public void DrawScene(GL10 gl) {
// 相当于设置画笔颜色
gl.glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
// 设置点的大小
gl.glPointSize(8f);
// 重置为单位矩阵
gl.glLoadIdentity();
// 向Z轴负方向平移4个单位?随便定的
gl.glTranslatef(0, 0, -4);
// 开启顶点设置
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
// 设置顶点数组,顶点坐标为3维坐标, 坐标数据类型为GL10.GL_FLOAT, 数据从数组的第一位开始下标为0, 顶点数组为vertexBuffer
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
// 有了数据后开始绘制点
/**
* 可以有两种绘制方法:
* 1.使用 {@link GL10#glDrawArrays(int mode, int first, int count)}
* 这个方法。此方法的特点是绘制的顺序是由给定的数组的数据顺序确定的,顺序不能被改变
* 1) @params mode 是指的绘制的图形类型:(有点、线、三角形三种图形):
* (在这里假定顶点有P0, P1, P2, P3, P4, P5, P6, P7)
* {@link GL10#GL_POINTS} 指定主表的散点即 P0~P7 8个单独的点
* {@link GL10#GL_LINES} 单独的线段 P0P1/P2P3/P4P5/P6P7 *P8不会被绘制
* {@link GL10#GL_LINE_STRIP} 连续的线段 P0P1/P1P2/P2P3/P3P4/P4P5/P5P6/P6P7/P7P8
* {@link GL10#GL_LINE_LOOP} 首尾相连封闭的线段 P0P1/P1P2/P2P3/P3P4/P4P5/P5P6/P6P7/P7P8/P8P0
* {@link GL10#GL_TRIANGLES} 多个不想关联的三角形 P0P1P2/P3P4P5 不足三个点的将不会被绘制
* {@link GL10#GL_TRIANGLE_STRIP} 相关联的三角形 P0P1P2/P1P2P3/P2P3P4/P3P4P5/P4P5P6/P5P6P7
* {@link GL10#GL_TRIANGLE_FAN} 共顶点的三角形 P0P1P2/P0P2P3/P0P3P4/P0P4P5/P0P5P6/P0P6P7
*
* 2) @params first 是指的从第几个顶点开始绘制,一般为0
* 3) @params count 是总计要绘制的顶点的数量,设置为vertexArray.length
* 2.使用 {@link GL10#glDrawElements(int mode, int count, int type, java.nio.Buffer indices)}
* 1) @params mode 同 {@link GL10#glDrawArrays(int mode, int first, int count)}中的 @params mode
* 2) @params count 绘制图元的数量乘以一个图元的顶点数?
* 3) @params type 只能使用 {@link GL10#GL_UNSIGNED_BYTE} {@link GL10#GL_UNSIGNED_SHORT}
* 4) @params indices 指向索引存储位置的指针
*/
gl.glDrawArrays(GL10.GL_POINTS, 0, vertexArray.length/3);
// 关闭顶点设置
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
}
}
Draw20D.java:
public class Draw20D implements IOpenGLDrawer {
private static final float X = 0.525731112119133606f;
private static final float Z = 0.850650808352039932f;
// 1.顶点数组
private static final float vertices[] = {
-X, 0.0f, Z,
X, 0.0f, Z,
-X, 0.0f, -Z,
X, 0.0f, -Z,
0.0f, Z, X,
0.0f, Z, -X,
0.0f, -Z, X,
0.0f, -Z, -X,
Z, X, 0.0f,
-Z, X, 0.0f,
Z, -X, 0.0f,
-Z, -X, 0.0f
};
// 2.这个数组是干什么的?
private static final short indices[] = {
0,4,1, 0,9,4, 9,5,4, 4,5,8, 4,8,1,
8,10,1, 8,3,10, 5,3,8, 5,2,3, 2,7,3,
7,10,3, 7,6,10, 7,11,6, 11,0,6, 0,1,6,
6,1,10, 9,0,11, 9,11,2, 9,2,5, 7,2,11
};
// 3.颜色数组
private static final float colors[] = {
0f,0f,1f,1f,
0f,1f,0f,1f,
1f,0f,0f,1f,
0f,1f,1f,1f,
1f,0f,1f,1f,
1f,1f,0f,1f,
0.1f, 0.2f, 0.3f, 1f,
0.1f, 0.3f, 0.2f, 1f,
0.2f, 0.1f, 0.3f, 1f,
0.2f, 0.3f, 0.1f, 1f,
0.3f, 0.2f, 0.1f, 1f,
0.3f, 0.1f, 0.2f, 1f
};
private FloatBuffer vertexBuffer, colorBuffer;
private ShortBuffer indicesBuffer;
private float angle = 0f;
public Draw20D() {
ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4);
vbb.order(ByteOrder.nativeOrder());
vertexBuffer = vbb.asFloatBuffer();
vertexBuffer.put(vertices);
vertexBuffer.position(0);
ByteBuffer ibb = ByteBuffer.allocateDirect(indices.length * 2);
ibb.order(ByteOrder.nativeOrder());
indicesBuffer = ibb.asShortBuffer();
indicesBuffer.put(indices);
indicesBuffer.position(0);
ByteBuffer cbb = ByteBuffer.allocateDirect(colors.length * 4);
cbb.order(ByteOrder.nativeOrder());
colorBuffer = cbb.asFloatBuffer();
colorBuffer.put(colors);
colorBuffer.position(0);
}
// 此处要注意代码的顺序,不能在绘制前关闭顶点设置!!!
@Override
public void DrawScene(GL10 gl) {
// 1.设置画笔的颜色为红色
gl.glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
gl.glLoadIdentity();
gl.glTranslatef(0, 0, -4);
// 2.让20面体进行旋转
gl.glRotatef(angle, 0, 1, 0);
// 3.1 只绘制前面
gl.glFrontFace(GL10.GL_CCW);
// 3.2 开启忽略面
gl.glEnable(GL10.GL_CULL_FACE);
// 3.3 设置忽略背面
gl.glCullFace(GL10.GL_BACK);
// 3.4 关闭忽略面
gl.glDisable(GL10.GL_CULL_FACE);
// 4.1 开启顶点设置
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
// 4.2 将顶点数据传入
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
// 5.1 开启颜色设置
gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
// 5.2 将颜色数据传入
gl.glColorPointer(4, GL10.GL_FLOAT, 0, colorBuffer);
/**
* 此处使用的是 {@link GL10#glDrawElements(int mode, int count, int type, java.nio.Buffer indices)}
* 1) @params mode 同 {@link GL10#glDrawArrays(int mode, int first, int count)}中的 @params mode
* 2) @params count 绘制图元的数量乘以一个图元的顶点数?
* 3) @params type 只能使用 {@link GL10#GL_UNSIGNED_BYTE} {@link GL10#GL_UNSIGNED_SHORT}
* 4) @params indices 指向索引存储位置的指针--->这是一个short数组,并且次数组包含的因子是顶点的下标index
* 此处的顶点数组的长度是12,因此index是0~11,这个索引数组包含的信息是要绘制的三角形的三个顶点的坐标
* 在顶点数组中的index,因此成为索引指针。索引指针绘制三角形时以三个数据为一组。
*/
gl.glDrawElements(GL10.GL_TRIANGLES, indices.length,
GL10.GL_UNSIGNED_SHORT, indicesBuffer);
// 5.3 关闭颜色设置
gl.glDisableClientState(GL10.GL_COLOR_ARRAY);
// 4.3 关闭顶点设置
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
angle++;
}
}
源代码下载
——————不怎么华丽的分割线—————————————–
关于其中一些重要方法的解释请看第四章:Android OpenGL ES开发教程(四)