在过去十年伴随着移动互联网的发展浪潮,音视频行业得以飞速发展,音视频技术方面有几个神器是音视频从业者的必备技能,其中OpenGL ES是移动端视频图像处理的核心技能,诸如抖音,美拍,直播中的美白、磨皮、大眼、瘦脸、贴纸等都是OpenGL ES佳作。
安卓上提供了一套基于Java的OpenGL ES的API,但最终还是通过调用Native的方法实现的。此外,音视频是属于一种通用技术,平台相关性不是很明显,普遍的做法是实现一套跨平台的SDK,这样节约的代价是相当可观。基于音视频技术的一些特点,跨平台语言选用c/c++已成为了默认的标准,但c++层面并没有像GLSurfaceView这样封装完整的环境供开发者使用,需要程序猿们手动来实现这个流程。
本篇文章是除了基本概念之外的另一个入门篇,基于c++的OpenGL ES开发环境搭建,具体的来说,是将GLSurfaceView的核心代码用c++来实现,并开放一些重要的api做多线程开发(代码以Android为例)。
二、EGL环境搭建
1、在CmakeLists.txt的加入这样的依赖
目的是在链接目标文件时将安卓中的libEGL.so和libOpenGLES_2.so这两个动态链接库加入开发者的代码中供使用,可以理解为没有这个#include相关文件会file not find。
target_link_libraries(
target
GLESv2
2、创建渲染表面
通过调用这个eglGetDisplay() 这个Api来创建渲染表面
3、初始化
通过eglInitialize()来初始化渲染表面
4、获取配置
通过eglChooseConfig来配置OpenGL 版本,RGBA每个通道的精度等
5、创建上下文
通过eglCreateContext来获取EGL环境的上下文
至此5步,EGL环境已经搭建完毕,在使用过程中要根据当前线程的环境适时调 eglMakeCurrent切换线程,每一帧渲染之后调用eglSwapBuffers交换前后台内容。
6、一个简单的EGL换将帮助类
这个类的原型是谷歌提供的一个非官方demo,我将其中的Java代码转换成c++实现,实测有效
#include <GLES2/gl2.h>
#include <EGL/egl.h>
#include <android/native_window.h>
class EglCore {
public:
EglCore();
EglCore(const EglCore &) = delete;
EglCore(EglCore &&) = delete;
bool init();
void makeCurrent(EGLSurface surface);
void swapBuffer(EGLSurface surface);
EGLSurface createEGLSurface(ANativeWindow *window);
void destroyEGLSurface(EGLSurface surface);
void destroy();
private:
EGLDisplay _display{nullptr};
EGLConfig _config{nullptr};
EGLContext _context{nullptr};
bool EglCore::init() {
if (_display != EGL_NO_DISPLAY) {
return false;
_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (EGL_NO_DISPLAY == _display) {
LOGI("egl get no display");
return false;
GLint majorVersion, minorVersion;
if (!eglInitialize(_display, &majorVersion, &minorVersion)) {
LOGI("egl initialize fail");
return false;
EGLint config_attrs[] = {
EGL_BUFFER_SIZE, 32,
EGL_ALPHA_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_RED_SIZE, 8,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_NONE
int num_configs = 0;
EGLConfig config;
if (!eglChooseConfig(_display, config_attrs, &config, 1, &num_configs)) {
LOGI("egl choose config error");
return false;
if (config) {
EGLint context_attrs[] = {
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE
EGLContext context = eglCreateContext(_display, config, share_context, context_attrs);
return EGL_NO_CONTEXT != _context;
EGLSurface EglCore::createEGLSurface(ANativeWindow *window) {
EGLint width, height;
EGLSurface surface = eglCreateWindowSurface(_display, _config, window, nullptr);
if (EGL_NO_SURFACE == surface) {
LOGI("egl create surface error");
return nullptr;
return surface;
void EglCore::destroyEGLSurface(EGLSurface surface) {
eglDestroySurface(_display, surface);
void EglCore::makeCurrent(EGLSurface surface) {
if (!eglMakeCurrent(_display, surface, surface, _context)) {
LOGI("egl make current error");
void EglCore::swapBuffer(EGLSurface surface) {
if (!eglSwapBuffers(_display, surface)) {
LOGI("egl swap error");
void EglCore::destroy() {
eglMakeCurrent(_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglDestroyContext(_display, _context);
eglTerminate(_display);
_display = EGL_NO_DISPLAY;
_context = EGL_NO_CONTEXT;
三、用c++实现GLSurfaceView
本文提供一个精简版的OpenGL开发环境,能满足相机渲染等基本需求,更多的内容请查看GLSurfaceView源码获取帮助
#include "glview.h"
GlView::GlView() {
LOGI("GLView Constructor");
void GlView::createSurface(jobject surface, int w, int h) {
window = ANativeWindow_fromSurface(env, surface);
this->width = w;
this->height = h;
is_exit = false;
future = std::async(std::launch::async, &GlView::guardedRun, this);
void GlView::guardedRun() {
if (window != nullptr) {
eglCore = new EglCore();
eglCore->init();
eglSurface = eglCore->createEGLSurface(window);
eglCore->makeCurrent(eglSurface);
if (render && window) {
render->onSurfaceCreate(nullptr, width, height);
render->onDraw();
while (!is_exit) {
cond.wait(lock);
if (render) {
render->onDraw();
if (eglCore) {
eglCore->swapBuffer(eglSurface);
if (render) {
render->onSurfaceDestroy();
if (eglCore) {
eglCore->destroyEGLSurface(eglSurface);
eglCore->destroy();
delete eglCore;
LOGI("GL thread quit");
void GlView::destroySurface() {
is_exit = true;
cond.notify_all();
if (future.isValid()) {
future.wait();
void GlView::setRender(IRender *render_) {
this->render = render_;
void GlView::requestRender() {
cond.notify_all();
GlView::~GlView() {
LOGI("~ GlView");
有了一个类似Java层的GLSurfaceView,那么后续的开发就变得容易,只需要将重心放在滤镜的实现上即可。
四、多线程开发
要在多个线程中使用OpenGL ES开发,需先了解一些状态机的概念,其实很简单。前面在创建EGL环境的时候有个eglContext的概念,这个EGL环境的上下文,可在不同线程共享,切换线程的时候通过这个context调用eglMakeCurrent即可使用当前拥有的program一次包装好的滤镜对象。
Broadcast Receiver: 广播接收器,Android的广播要么来自系统,要么来自普通程序。
可以在AndroidMenifest.xml注册广播接收器或在代码中注册。
4. service启动默认是什么线程
答:默认启动的是主线程
5. 使用过哪些设计模式进行开发
答:单例模式、工厂模式、观察者模式
6. 线程
池有什么优点缺点
答:优点就是减少创建线程的消耗,每次创建线程都从线程池中拿。
缺点是一直占用内存。
7. 多种布局方式的特
设定一个场景,要读取一个opengl渲染线程中一帧的画面到内存,可以使用glReadPixel函数,但是这个函数存在很糟糕的性能问题,为了解决这个问题可以使用如下步骤:
1. 两个opengl渲染线程: A线程负责前台窗口的渲染,B线程在pbuffer上渲染,并且把A...
为了在Java线程进行
OpenGL调用,需要为java线程初始化
OpenGL环境,init
OpenGL函数展示了初始化
OpenGL环境的过程。在setup
OpenGL方法
中,在线程上先执行该调用即可。Java代码示例如下:
1 package com.thornbirds.unity;
3 public class PluginTexture {
要开发基于OpenGL ES的实时滤镜相机,您需要以下开发环境:
1. 一台计算机:您需要一台配有OpenGL ES 2.0或更高版本的计算机,以便能够编写和测试您的代码。
2. 编辑器:您需要一个能够编写OpenGL ES代码的编辑器。一些常用的编辑器包括Visual Studio Code、Sublime Text和Atom。
3. OpenGL ES SDK:您需要安装适用于您的目标平台的OpenGL ES SDK。例如,如果您的目标平台是Android设备,则需要安装Android NDK。
4. 相机API:您需要使用相机API来访问设备的摄像头。对于Android设备,您可以使用Camera2 API。
5. 滤镜库:您需要选择并集成一个滤镜库,以便能够添加各种滤镜效果。一些常用的滤镜库包括GPUImage和OpenCV。
6. 设备测试:最后,您需要在目标设备上测试您的应用程序。您可以使用Android模拟器进行测试,但最好在实际设备上进行测试,以确保您的应用程序能够正常工作并获得所需的性能。