一、什么是OpenGL ES?
网上介绍很多,这里不多讲,直接简单的讲,OpenGL是一个可以用来画二维或者三维图形库。而OpenGL ES呢,是OpenGL针对嵌入式设备搞的一个库,所以移动开发上用的基本上就是OpenGL ES了。
二、OpenGL ES的基本使用和一些概念
1. 版本
OpenGL ES 有几个版本,对于Android系统API,会有不同的要求。
OpenGL ES版本 | Android系统API |
---|---|
OpenGL ES 1.0&1.1 | Android 1.0 以上 |
OpenGL ES 2.0 | Android 2.2以上 |
OpenGL ES 3.0 | Android 4.3以上 |
OpenGL ES 3.1 | Android 5.0以上 |
这里考虑到Android系统版本,选择OpenGL ES 2.0会是比较好。当然如果不考虑旧版本,使用3.0或者3.1更佳。
2. Android上OpenGL ES基本的类
(1) GLSurfaceView
OpenGL ES在Android开发上,是以GLSurfaceView
为载体进行展示的(或者可以自己用SurfaceView
实现一个,这里简单起见,直接用GLSurfaceView
)。
基本使用:
代码语言:txt复制 GLSurfaceView glView = new GLSurfaceView (context);
// 注意,记得给它设置版本,这里用OpenGL ES 2.0,那就设置version =2;
glVIew.setEGLContextClientVersion(version);
// 重点,所有的绘制逻辑,基本全在这个Renderer里了;
glView.setRenderer(renderer);
// 这个看情况用,不一定需要用这个,但一般会这么用。
// RENDERMODE_WHEN_DIRTY意思是只有你执行requestRender方法才去渲染,会比较节省性能,否则默认隔断时间就进行渲染。
glVIew.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
关于GLSurfaceView
的生命周期方法onResume
和onPause
分别在Activity
的onResume
和onPause
分别调用。
(2) GLSurfaceView.Renderer
上面讲到,这个是GLSurfaceView
的“灵魂”。基本上的各种特效和图形都是在这里写出来的。
Renderer
这个东西,是个接口,有三个方法需要自己去实现它。按照初始的调用顺序来讲下这个三个方法
// 创建GLSurfaceView时回调的方法,主要做一些后面不会常用不变的字段进行初始化操作;
onSurfaceCreated(GL10 gl10, EGLConfig config);
// 回调包括GLSurfaceView大小的变化或屏幕横竖变换;
onSurfaceChanged(GL10 gl10, int width, int height);
// 看名字就知道,是绘制时会回调的方法,在这里做绘制逻辑;上面讲到requestRender,基本就是会来回调这个方法。
onDrawFrame(GL10 gl10);
3. OpenGL中的各种坐标系
1. 屏幕坐标系
众所周知,Android屏幕坐标系是以左上角为原点,横为x轴,竖为y轴。
2. 顶点坐标系
和屏幕坐标系不太一样,OpenGL的顶点坐标是以中心为原点,横为x轴,竖为y轴,垂直于屏幕为z轴。轴的值范围都在-1, 1这个区间内。据说是做归一化处理,显卡计算起来会比较方便喔。
举个栗子,定义一个三角形坐标可以这样:
代码语言:txt复制 private static final float triangleCoords[] = {
0f, 1.0f, // top
-1.0f, 0f, // left bottom
1.0f, 0f // right bottom
};
3. 纹理坐标系
同理,都不一样。OpenGL纹理的坐标系,是以左下角为原点,横为x轴,竖为y轴,轴的值范围都在0, 1这个区间内。
举个例子,定义一个图片纹理坐标可以这样:
代码语言:txt复制 private final float[] mTexCoordSrc ={
0.0f, 0.0f,
0.0f, 1.0f,
1.0f, 0.0f,
1.0f, 1.0f,
};
4. 矩阵与屏幕
由于设备屏幕大小总会不一样,所以就存在需要将OpenGL绘制的东西的坐标与屏幕做一个投影映射。
OpenGL通过定义相机视图矩阵(V)、投影矩阵(P),通过进行矩阵相乘(转换矩阵MVP),使坐标正确地映射到Android设备的屏幕。
这里盗个图,理解起来会比较清楚:
举个栗子:
代码语言:txt复制private float[] mMVPMatrix = new float[16]; // 转换矩阵
private float[] mViewMatrix = new float[16]; // 相机视图矩阵
private float[] mProjectionMatrix = new float[16]; // 投影矩阵
// ......省略
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
// ......省略
Matrix.frustumM(mProjectionMatrix, 0, -ratio, ratio, -1, 1, 3, 7); // 投影矩阵->mProjectionMatrix,透视投影,正交投影用orthoM
Matrix.setLookAtM(mViewMatrix, 0,
0, 0, 0,
0, 0, 0,
0, 0, 0); // 设置相机->mViewMatrix
Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0); // 转换矩阵->mMVPMatrix,这里做了乘法
}
5. Shader
中文人称:着色器。用来描述如何定坐标和渲染。用了一种类C语言的编程语言来写。主要有顶点(vertex)着色器和片段(fragment)着色器两种。基本上都是写OpenGL和这个两个shader打交道,通过shader去告诉OpenGL ES库,你想画在哪、填充什么颜色等等。所以,简单讲Renderer里主要是描述了和shader“打交道”的逻辑。
举一个简单的栗子:
代码语言:txt复制// vertex shader
attribute vec4 aPosition; // 顶点坐标
uniform mat4 uMatrix; // 上面那个mMVPMatrix传进来就是这个东西
void main(){
gl_Position = uMatrix * aPosition; // 最后算出最后的顶点坐标
}
代码语言:txt复制// fragment shader
precision mediump float;
uniform vec4 uColor; // 填充的颜色
void main() {
gl_FragColor = uColor;
}
简单来讲,顶点着色器用来确定坐标,片段着色器用来填充颜色或者纹理的。
三、总结
- OpenGL就是一个画图用的库;
- 在Android上,OpenGL呈现的载体是
GLSurfaceView
; - 使用shader语言去告诉OpenGL你要干嘛(画在什么位置和填充什么颜色或者纹理);