开篇前言
开启OpenGL ES 3.x学习之路之前,先了解几个基本概念:OpenGL和OpenCV是什么?两者区别是什么?OpebGL ES是什么?
OpenGL
OpenGL(Open Graphics Library),用于渲染2D、3D矢量图形的跨语言、跨平台的应用程序编程接口(API)。
OpenCV
OpenCV(Open Source Computer Vision Library),是一个基于BSD许可(开源)发行的跨平台计算机视觉库
OpenGL和OpenCV的区别
区别在于Computer Vision和Computer Graphics这两个学科之间的区别,前者专注于从采集到的视觉图像中获取信息,是用机器来理解图像;后者是用机器绘制合适的视觉图像给人看。简单来说可以这么理解,OpenCV是图像到数据的处理,OpenGL是数据到图像的处理。所以OpenGL是去做绘制处理在原图像基础上做开发实现2D、3D场景等特效,例如滤镜贴纸、色值修改对图像效果处理。
OpenGL ES
OpenGL ES (OpenGL for Embedded Systems) 是 OpenGL 三维图形 API 的子集,针对手机、PDA和游戏主机等嵌入式设备而设计,是OpenGL的针对设备平台做的裁切版本,去除平台上没必要特性。
着色器和渲染管线
渲染管线也称渲染流水线,渲染工作是通过渲染管线中多个相互独立的处理单元进行并行处理的,这种模式极大地提升了渲染效率。OpenGL ES的渲染管线实质上就是一系列绘制过程,在这个过程中将待渲染相关描述信息数据经过渲染管线输出最终图像。
Vertex Shader(顶点着色器) 和 Fragment Shader(片元着色器) 是可编程管线。可编程管线支持动态编程,例如OpenGL ES当中是由着色语言GLSL脚本提供编程能力。开发者通过编写GLSL脚本语言动态修改渲染过程从而实现不同渲染绘制图像。
Vertex Shader顶点着色器
可编辑处理单元,通过开发产生纹理坐标、颜色、点位置等顶点属性
- 输入值主要包括attribute(位置、颜色、法向量)、uniform(光源、摄像机位置、投影矩阵)、采样器、临时变量。
- 输出值主要包括varying、内建变量(gl_Position、gl_FrontFacing和gl_PointSize等)。
图元装配
图元组装是指顶点数据根据设置的绘制方式被结合成完整的图元。例如绘制点、线时一个点是一个图元、一条线有两个点组成是一个图元。图元装配另一个过程是裁剪,保留完全在视锥体中的图元,丢弃完全不在视锥体中的图元,对一半在一半不在的图元进行裁剪。裁剪之后,顶点位置就被转换成了屏幕坐标。
光栅化
光栅化过程就是将需要显示物体投影到平面上的过程。因为不同角度观看物体会导致投射都平面的结果有所不同,所以在不同角度下投射物体最终呈现的效果也会有所不同。在光栅化过程中基本图元会转换成片元对象,片元对象其实就是可显示在屏幕上的像素对象,其包含了位置,颜色,纹理坐标等信息。片元对象通过片元着色器最终在屏幕上渲染显示。
Fragment Shader片元着色器
片元着色器是用于处理片元值及其相关数据的可编程单元,其可以执行纹理的采样、颜色的汇总、计算雾颜色等操作。
- 输入值主要包含in0~in(n)是顶点着色器传递的量。
- 输出值主要包含out变量一般指的是由片元着色器写入计算完成的片元颜色值的变量。
初识OpenGL ES应用程序
在Android应用中使用GLSurfaceView实现OpenGL应用开发。GLSurfaceView继承于SurfaceView,内嵌surface专门负责OpenGL渲染,通过配置GL ES参数去实现渲染工作。 下面通过一个Demo例子来看看如何在Android平台开发OpenGL应用。
RendererView
首先创建一个GLRenderer类继承GLSurfaceView对象。构造函数中创建SceneRenderer并调用setRenderer方法设置渲染器。
public class SampleX1GLRenderer extends GLSurfaceView {
SceneRenderer mRenderer;//自定义渲染器的引用
SampleX1 sampleX1;
public SampleX1GLRenderer(Context context) {
super(context);
this.setEGLContextClientVersion(3);
mRenderer = new SceneRenderer();
this.setRenderer(mRenderer);
this.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
}
}
SceneRenderer
SceneRenderer是继承于GLSurfaceView.Renderer,主要实现三个方法onSurfaceCreated、onDrawFrame、onSurfaceChanged。
- onSurfaceCreated 当GL画布准备好后回调,在画布创建后可调用GLES30.glClearColor方法设置屏幕背景色,同时创建SampleX1对象。SampleX1的作用在后面讲解。
- onSurfaceChanged 当画布大小发生改变时回调,首次创建时同样也会回调。在这里调用GLES30.glViewport方法设置视图的大小和位置信息。
- onDrawFrame 该方法就是真正去做OpenGL绘制的回调。在该方法中去做画布绘制操作。
需要注意的是Renderer渲染器三个回调方法并不是在主线程中执行,而是在GL的绘制线程中执行。
private class SceneRenderer implements GLSurfaceView.Renderer {
public void onDrawFrame(GL10 gl) {
//清除深度缓冲与颜色缓冲
GLES30.glClear(GLES30.GL_DEPTH_BUFFER_BIT | GLES30.GL_COLOR_BUFFER_BIT);
sampleX1.drawSelf();
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
//设置屏幕背景色RGBA
GLES30.glClearColor(0, 0, 0, 1.0f);
//创建三角形对对象
sampleX1 = new SampleX1(SampleX1GLRenderer.this);
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
//设置视窗大小及位置
GLES30.glViewport(0, 0, width, height);
}
}
SampleX1
SampleX1对象主要工作是准备需要在GL上做绘制的数据信息。mVertexBuffer是顶点坐标缓冲、mColorBuffer是顶点颜色缓冲、mVertexShader表示顶点着色器、mFragmentShader表示片元着色器。整个数据准备和绘制流程都在这个类中实现:
- 首先执行initVertexData方法准备好顶点坐标和顶点颜色的缓冲数据。
- 初始化着色器,读取顶点着色器文件vsh和片元文件fsh创建两者着色器。
- 接着通过两者着色器创建程序program,通过程序获取到顶点位置引用和顶点颜色引用。
- 最后就是绘制过程,向顶点位置引用和顶点颜色引用将顶点坐标缓冲数据和顶点颜色缓冲数据送入渲染管线。最终调用GLES30绘制方法去执行想要做的绘制操作。
public class SampleX1 {
int mProgram;//自定义渲染管线程序id
int maPositionHandle; //顶点位置属性引用
int maColorHandle; //顶点颜色属性引用
String mVertexShader;//顶点着色器代码脚本
String mFragmentShader;//片元着色器代码脚本
FloatBuffer mVertexBuffer;//顶点坐标数据缓冲
FloatBuffer mColorBuffer;//顶点着色数据缓冲
public SampleX1(GLSurfaceView mv) {
//调用初始化顶点数据的initVertexData方法
initVertexData();
//调用初始化着色器的intShader方法
initShader(mv);
}
public void initVertexData()//初始化顶点数据的方法
{
float vertices[] = new float[]{//顶点坐标数组
0, 0, 0,//原点 x,y,z
-0.5f, 0.5f, 0,
0.5f, 0.5f, 0,
-0.5f, -0.5f, 0,
0.5f, -0.5f, 0,
-0.25f, 0.25f, 0,
0.25f, 0.25f, 0,
-0.25f, -0.25f, 0,
0.25f, -0.25f, 0,
0.3f, 0.3f, 0,
0.6f, 0.3f, 0,
0.3f, 0.0f, 0,
0.6f, 0.0f, 0,
0.4f, 0.4f, 0.2f,
0.7f, 0.4f, 0.2f,
0.4f, 0.1f, 0.2f,
0.7f, 0.1f, 0.2f,
};
mVertexBuffer = BufferUtil.creatFloatBuffer(vertices);
float colors[] = new float[]{//顶点颜色数组
1, 1, 1, 0,//白色 rgba
1, 0, 0, 0,
0, 1, 0, 0,
1, 0, 0, 0,
0, 1, 0, 0,
1, 0, 0, 0,
0, 1, 0, 0,
1, 0, 0, 0,
0, 1, 0, 0,
1, 0, 0, 0,
0, 1, 0, 0,
1, 0, 0, 0,
0, 1, 0, 0,
1, 0, 0, 0,
0, 1, 0, 0,
1, 0, 0, 0,
0, 1, 0, 0,
};
mColorBuffer = BufferUtil.creatFloatBuffer(colors);
}
//初始化着色器的方法
public void initShader(GLSurfaceView mv) {
//加载顶点着色器的脚本内容
mVertexShader = ShaderUtil.loadFromAssetsFile("samplex1/vertex_samplex_1.vsh", mv.getResources());
//加载片元着色器的脚本内容
mFragmentShader = ShaderUtil.loadFromAssetsFile("samplex1/frag_samplex_1.fsh", mv.getResources());
//基于顶点着色器与片元着色器创建程序
mProgram = ShaderUtil.createProgram(mVertexShader, mFragmentShader);
//获取程序中顶点位置属性引用
maPositionHandle = GLES30.glGetAttribLocation(mProgram, "aPosition");
//获取程序中顶点颜色属性引用
maColorHandle = GLES30.glGetAttribLocation(mProgram, "aColor");
}
public void drawSelf() {
//指定使用某套shader程序
GLES30.glUseProgram(mProgram);
//将顶点位置数据送入渲染管线
GLES30.glVertexAttribPointer(
maPositionHandle, 3,
GLES30.GL_FLOAT, false,
3 * 4, mVertexBuffer);
//将顶点颜色数据送入渲染管线
GLES30.glVertexAttribPointer(
maColorHandle, 4,
GLES30.GL_FLOAT, false,
4 * 4, mColorBuffer);
//启用顶点位置数据数组
GLES30.glEnableVertexAttribArray(maPositionHandle);
//启用顶点颜色数据数组
GLES30.glEnableVertexAttribArray(maColorHandle);
GLES30.glDrawArrays(GLES30.GL_POINTS, 0, 5);
GLES30.glDrawArrays(GLES30.GL_LINES, 1, 4);
GLES30.glDrawArrays(GLES30.GL_LINE_STRIP, 5, 4);
GLES30.glDrawArrays(GLES30.GL_LINE_LOOP,9,4);
GLES30.glDrawArrays(GLES30.GL_POINTS, 13, 4);
}
}
fsh文件
#version 300 es
precision mediump float;
in vec4 vColor; //接收从顶点着色器过来的参数
out vec4 fragColor;//输出到的片元颜色
void main()
{
fragColor = vColor;//给此片元颜色值
}
vsh文件
#version 300 es
layout (location = 0) in vec3 aPosition; //顶点位置
layout (location = 1) in vec4 aColor; //顶点颜色
out vec4 vColor; //用于传递给片元着色器的变量
void main()
{
gl_Position = vec4(aPosition,1); //根据总变换矩阵计算此次绘制此顶点位置
vColor = aColor;//将接收的颜色传递给片元着色器
gl_PointSize = 10.0;//点大小
}
以上在画布中实现了点(GL_POINTS)和线(GL_LINES)的绘制,程序运行后的效果如下:
参考
- 《OpenGL ES 3.x游戏开发》
- OpenCV 与 OpenGL 的关系是什么?
- OpenGL ES渲染管线(渲染流程)