前言:
最近找工作的朋友,可能在各大招聘网站上投递简历的时候,都会发现。招聘岗位的最下方,有一个加分项“熟悉OpenGl或者OpenCv优先”。而这个加分项其实就是目前互联网上特别流行的人脸识别技术编程。百度百科告诉我们“人脸识别,是基于人的脸部特征信息进行身份识别的一种生物识别技术。用摄像机或摄像头采集含有人脸的图像或视频流,并自动在图像中检测和跟踪人脸,进而对检测到的人脸进行脸部的一系列相关技术,通常也叫做人像识别、面部识别。”。
▲概念要点
在开始编码之前,这里先介绍一下概念术语
- OpenGl 被严格定义为“一种到图形硬件的软件接口”。不是一种编程语言,而是一种API(Application Programming Interface,应用程序编程接口)。作为一种用于实时3D图形的工业标准API,已经得到了广泛的认可和接受。
- 术语“三维”,即3D,指的是被描述或显示的对象具有三个测量维度:宽度,高度和深度。
- 渲染,渲染上获得对三维对象的几何描述并把它变成屏幕对象的一种行为。
- 3D图形的常见用途:交互游戏,仿真到科学,医疗或商业用途的数据虚拟
▲新建第一个应用程序FirstOpenGlDemo
在MainActivity中创建GLSurfaceView实例,使用rendererSet记住GLSurfaceView是否处于有效的状态
private GLSurfaceView glSurfaceView;
private boolean rendererSet = false;
判断是否支持OpenGl 2.0。获取设备配置信息,取出reqGlEsVersion 变量检查OpenGL ES版本号。如果版本号为0x20000或后续版本,我们就可以使用OpenGl ES2.0的API
ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
ConfigurationInfo configurationInfo = activityManager.getDeviceConfigurationInfo();
boolean supportsEs2 = configurationInfo.reqGlEsVersion >= 0x20000
|| (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1
&& (Build.FINGERPRINT.startsWith("generic")
|| Build.FINGERPRINT.startsWith("unknown")
|| Build.MODEL.contains("google_sdk")
|| Build.MODEL.contains("Emulator")
|| Build.MODEL.contains("Android SDK build for x86")));
为OpenGL ES 2.0配置渲染表面
if (supportsEs2) {
//Request an OpenGl ES 2.0 compatible context
glSurfaceView.setEGLContextClientVersion(2);
//Assign our renderer
glSurfaceView.setRenderer(new FirstOpenGlProjectRenderer());
rendererSet = true;
} else {
Toast.makeText(this, "This device does not support OpenGl Es 2.0", Toast.LENGTH_SHORT).show();
}
使GLSurfaceView显示在屏幕上
setContentView(glSurfaceView);
▲处理好Android Activity生命周期
他们确保surface视图正确暂停并继续后台渲染线程,同时释放和续用OpenGl上下文
@Override
protected void onPause() {
super.onPause();
if (rendererSet) {
glSurfaceView.onPause();
}
}
@Override
protected void onResume() {
super.onResume();
if (rendererSet) {
glSurfaceView.onResume();
}
}
在后台线程中渲染
GLSurfaceView会在一个单独的线程中调用渲染器的方法。默认情况下,GLSurfaceView会以显示设备的刷新频率不断地渲染,当然,它也可以配置为按请求渲染,只需要用GlSurfaceView.RENDERMODE_WHEN_DIRTY作为参数调用GLSurfaceView.setRenderMode()即可。
既然Android的GLSurfaceView在后台线程中执行渲染,就必须要小心,只能在这个渲染线程中调用OpenGl,在Android主线程中使用UI(用户界面你)相关的调用;两个线程之间的通信可以用如下方法:在主线程中的GLSurfaceView实例可以调用queueEvent ()方法传递一个Runnable给后台渲染线程,渲染线程可以调用Activity的runOnUIThread()来传递事件(event)给主线程
创建Renderer类
下面先看下渲染器接口定义的方法:
onSurfaceCreated(GL10 gl, EGLConfig config)当Surface被创建的时候,GLSurfaceView会调用这个方法;这发生在应用程序第一次运行的时候,并且,当设备被唤醒或者用户从其他activity切换回来时,这个方法可可能被调用。在实践中,这意味着,当应用程序运行时,本方法可能会被调用多次。
onSurfaceChanged(GL10 gl, int width, int height)在Surface被创建以后,每次Surface尺寸变化时,这个方法都会被GLSurfaceView调用到。在横屏,竖屏来回切换的时候,Surface尺寸会发生变化
onDrawFrame(GL10 gl)当绘制一帧时,这个方法会被GLSurfaceView调用。在这个方法中,我们一定要绘制一些东西,即使只是清空屏幕;因为,在这个方法返回后,渲染缓冲区会被交换并显示在屏幕上,如果什么都没画,可能会看到糟糕的闪烁效果。
为了实现清空屏幕,并调用自定义颜色(红色)显示在屏幕上。首先需要新建渲染器FirstOpenGlProjectRenderer类并实现GLSurfaceView.Renderer接口
在onSurfaceCreated()中调用glClearColor(1.0f,0.0f,0.0f,0.0f)设置清屏时屏幕用的颜色。前三个参数为红,绿,蓝,最后一个为半透明度或透明度
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
//设置清屏时屏幕用的颜色
//前三个参数为红,绿,蓝。最后一个为半透明度或透明度
gl.glClearColor(1.0f, 0.0f, 0.0f, 0.0f);
}
在onSurfaceChanged()中调用glViewport(0, 0, width, height)设置视口(viewport)尺寸,告诉OpenGl可以用来渲染的surface的大小
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
//设置视口(viewport)尺寸,告诉OpenGl可以用来渲染的surface的大小
//Set the OpenGl viewport to fill the entire surface
gl.glViewport(0, 0, width, height);
}
在onDrawFrame中调glClear(GL10.GL_COLOR_BUFFER_BIT)清空屏幕。这样会擦出屏幕上的所有颜色,并用之前glClearColor()调用自定义的颜色填充整个屏幕。
@Override
public void onDrawFrame(GL10 gl) {
//清空屏幕,并调用之前glClearColor的颜色填充整个surface
//clear the rendering surface
gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
}
运行之后会得到以下页面
▲这里需要注意的是,如果使用模拟器运行程序,屏幕无任何渲染效果。eclipse的话需要查看模拟器的配置选项“Use Host GPU”是否已经勾选上。android studio的话需要看下在创建模拟器时,以下选项是否勾选上
完整代码:
public class MainActivity extends AppCompatActivity {
private GLSurfaceView glSurfaceView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 判断设备是否支持OpenGl ES 2.0
if (IsSupported()) {
// 先建GLSurfaceView实例
glSurfaceView = new GLSurfaceView(this);
// 创建渲染器实例
MyRenderer mRenderer = new MyRenderer();
// 设置渲染器
glSurfaceView.setRenderer(mRenderer);
// 显示SurfaceView
setContentView(glSurfaceView);
}
}
private boolean IsSupported() {
ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
ConfigurationInfo configurationInfo = activityManager.getDeviceConfigurationInfo();
boolean supportsEs2 = configurationInfo.reqGlEsVersion >= 0x2000;
boolean isEmulator = Build.VERSION.SDK_INT > Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1
&& (Build.FINGERPRINT.startsWith("generic")
|| Build.FINGERPRINT.startsWith("unknown")
|| Build.MODEL.contains("google_sdk")
|| Build.MODEL.contains("Emulator")
|| Build.MODEL.contains("Android SDK built for x86"));
supportsEs2 = supportsEs2 || isEmulator;
return supportsEs2;
}
public class MyRenderer implements GLSurfaceView.Renderer {
// Surface创建的时候调用
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
// 设置清屏颜色为红色
gl.glClearColor(1f, 0f, 0f, 0f);
}
// Surface改变的的时候调用
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
// 设置窗口大小
gl.glViewport(0, 0, width, height);
}
// 在Surface上绘制的时候调用
@Override
public void onDrawFrame(GL10 gl) {
// 清除屏幕
gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
}
}
@Override
protected void onPause() {
super.onPause();
if (glSurfaceView != null) {
glSurfaceView.onPause();
}
}
@Override
protected void onResume() {
super.onResume();
if (glSurfaceView != null) {
glSurfaceView.onResume();
}
}
}