前言
OpenSL 是处理音频的底层开源库, OpenGL 则是处理图像相关的库,而对于移动端 Cpu/内存资源比较吃紧的特性,OpenGL 将部分不那么重要的 Api 进行精简,推出了 OpenGL ES ,相当于一个 mini 版本
从 2014 年以来,OpenGL ES 经历多个版本迭代,现在已经迭代到 3.x 版本
image.png如何在 Android 上使用
首先它对应了专门的控件 GLSurfaceView,我们用 Opengl ES 可以直接绘制的图形有点、线、三角形,其他图像怎么办呢? -- 用三角形拼接
- 构建 OpenGL ES 环境
// AndroidManifest 文件中声明
<uses-feature android:glEsVersion="0x00020000" android:required="true" />
- 使用 GLSurfaceView
- 构建渲染程序
ByteBuffer 是什么
缓冲区是一段定长的存储空间,用于加速频繁的 I/O 操作,其实就是临时存储数据,直接与信道(Channel)打交道,写入数据到信道或者从信道读取。
- ByteBuffer 的使用方法
- allocate(int capacity):从堆空间中分配一个容量大小为capacity的byte数组作为缓冲区的byte数据存储器
- allocateDirect(int capacity):通过操作系统来创建内存块用作缓冲区,而不在JVM堆栈中
- wrap(byte[] array):这个缓冲区的数据会存放在byte数组中
- wrap(byte[] array,int offset, int length):在上一个方法的基础上可以指定偏移量和长度
实践:使用 OpenGL 绘制 2D 三角形
OpenGL ES 是直接作用于硬件,所以有较高的性能优势,但是需要特定的语言来实现,比如在绘制三角形中,我们需要自己手写 shader (着色器),Opsl ES 2.0 以后需要自己实现了,在 1.x 版本都是系统的工作,提高了开发难度
- 定义三角形
- 定义顶点着色器、片源着色器
- 绘制
/**
* 用于 OpenGL 绘制的三角形
* @author : kai.mao
* @date : 2020/9/2
*/
public class GLTriangle {
private final int mProgram;
// 为顶点创建一个浮点型缓冲区
private FloatBuffer vertexBuffer;
private static final int VERTEX_COUNT = 3;
//用于存取attribute修饰的变量的位置编号
private int mPositionHandle;
//用于存取uniform修饰的变量的位置编号
private int mColorHandle;
private final int vertexCount = triangleCoords.length / VERTEX_COUNT;
// 4 bytes per vertex
private final int vertexStride = VERTEX_COUNT * 4;
static float triangleCoords[] = {
// top 屏幕顶端中心点
0.0f, 1.0f, 1.0f,
// bottom left 屏幕底部左下角
-1.0f, -1.0f, 0.0f,
// bottom right 屏幕底部右下角
1.0f, -1.0f, 0.0f
//以上坐标z都为0 创建一个平面的三角形
};
// Set color with red, green, blue and alpha (opacity) values
private float color[] = { 0.63671875f, 0.76953125f, 0.22265625f, 1.0f };
public GLTriangle(){
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(triangleCoords.length * 4);
byteBuffer.order(ByteOrder.nativeOrder());
vertexBuffer = byteBuffer.asFloatBuffer();
vertexBuffer.put(triangleCoords);
vertexBuffer.position(0);
// 加载顶点着色器
int vertexShader = TriangleRender.loadShader(GLES20.GL_VERTEX_SHADER,vertexShaderCode);
// 加载片元着色器
int frameShader = TriangleRender.loadShader(GLES20.GL_FRAGMENT_SHADER,fragmentShaderCode);
// 创建 gl 程序
mProgram = GLES20.glCreateProgram();
GLES20.glAttachShader(mProgram, vertexShader);
GLES20.glAttachShader(mProgram, frameShader);
// 链接程序
GLES20.glLinkProgram(mProgram);
}
/**
* 顶点着色器代码
* attribute变量(属性变量)只能用于顶点着色器中
* uniforms变量(一致变量)用来将数据值从应用程其序传递到顶点着色器或者片元着色器。 。
* varying变量(易变变量)是从顶点着色器传递到片元着色器的数据变量。
* gl_Position (必须)为内建变量,表示变换后点的空间位置。
*/
private final String vertexShaderCode =
"attribute vec4 vPosition;" + // 应用程序传入顶点着色器的顶点位置
"void main() {" +
" gl_Position = vPosition;" + // 设置此次绘制此顶点位置
"}";
/**
* 片元着色器代码
*/
private final String fragmentShaderCode =
"precision mediump float;" + // 设置工作精度
"uniform vec4 vColor;" + // 应用程序传入着色器的颜色变量
"void main() {" +
" gl_FragColor = vColor;" + // 颜色值传给 gl_FragColor内建变量,完成片元的着色
"}";
public void draw(){
// 使用 GL 程序
GLES20.glUseProgram(mProgram);
mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
GLES20.glVertexAttribPointer(mPositionHandle, VERTEX_COUNT,
GLES20.GL_FLOAT, false,
vertexStride, vertexBuffer);
GLES20.glEnableVertexAttribArray(mPositionHandle);
mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
GLES20.glUniform4fv(mColorHandle, 1, color, 0);
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);
GLES20.glDisableVertexAttribArray(mPositionHandle);
}
}
/**
* @author : kai.mao
* @date : 2020/9/2
*/
public class TriangleRender implements GLSurfaceView.Renderer {
GLTriangle glTriangle;
public TriangleRender() {
}
@Override
public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {
glTriangle = new GLTriangle();
}
@Override
public void onSurfaceChanged(GL10 gl10, int width, int height) {
GLES20.glViewport(0, 0, width, height);
}
@Override
public void onDrawFrame(GL10 gl10) {
//清空颜色
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
glTriangle.draw();
}
/**
* 加载并编译着色器代码
* 渲染器类型type={GLES20.GL_VERTEX_SHADER, GLES20.GL_FRAGMENT_SHADER}
* 渲染器代码 GLSL
*/
public static int loadShader(int type, String shaderCode){
int shader = GLES20.glCreateShader(type);
//加载shader代码 glShaderSource 和 glCompileShader
GLES20.glShaderSource(shader, shaderCode);
GLES20.glCompileShader(shader);
return shader;
}
}
PS
- 三角形的顶点顺序以逆时针排列,叫做 “卷曲顺序”,原因统一使用卷曲顺序有利于 OpenGL 的优化,可以减少绘制那些无法被看到的图形
- OpenGL 的坐标系和 Android 坐标系不一样,Android 中的坐标系顶点位于屏幕左上角,而 OpenGL 世界坐标系位于屏幕中心位置
- OpenGL 程序需要使用 Java + GLSL 两种语言编写
网友评论