你还不知道 OpenGL ES 和 EGL 的关系?
什么是 EGL
与设备的原生窗口系统通信;
查询绘图表面的可用类型和配置;
创建绘图表面;
在OpenGL ES 和其他图形渲染API之间同步渲染;
管理纹理贴图等渲染资源。
OpenGL ES 的平台无关性正是借助 EGL 实现的,EGL 屏蔽了不同平台的差异(Apple 提供了自己的 EGL API 的 iOS 实现,自称 EAGL)。
本地窗口相关的 API 提供了访问本地窗口系统的接口,而 EGL 可以创建渲染表面 EGLSurface ,同时提供了图形渲染上下文 EGLContext,用来进行状态管理,接下来 OpenGL ES 就可以在这个渲染表面上绘制。
egl、opengles 和设备之间的关系
图片中:
Display(EGLDisplay) 是对实际显示设备的抽象;
Surface(EGLSurface)是对用来存储图像的内存区域 FrameBuffer 的抽象,包括 Color Buffer(颜色缓冲区), Stencil Buffer(模板缓冲区) ,Depth Buffer(深度缓冲区);
Context (EGLContext) 存储 OpenGL ES 绘图的一些状态信息;
在 Android 平台上开发 OpenGL ES 应用时,类 GLSurfaceView 已经为我们提供了对 Display , Surface , Context 的管理,即 GLSurfaceView 内部实现了对 EGL 的封装,可以很方便地利用接口 GLSurfaceView.Renderer 的实现,使用 OpenGL ES API 进行渲染绘制,很大程度上提升了 OpenGLES 开发的便利性。
当然我们也可以自己实现对 EGL 的封装,本文就是在 Native 层对 EGL 进行封装,不借助于 GLSurfaceView ,实现图片后台渲染,利用 GPU 完成对图像的高效处理。
EGL 的应用
EGL 后台渲染实现效果图
使用 EGL 渲染的一般步骤:
-
获取 EGLDisplay 对象,建立与本地窗口系统的连接
调用 eglGetDisplay 方法得到 EGLDisplay。 -
初始化 EGL 方法
打开连接之后,调用 eglInitialize 方法初始化。 -
获取 EGLConfig 对象,确定渲染表面的配置信息
调用 eglChooseConfig 方法得到 EGLConfig。 -
创建渲染表面 EGLSurface
通过 EGLDisplay 和 EGLConfig ,调用 eglCreateWindowSurface 或 eglCreatePbufferSurface 方法创建渲染表面,得到 EGLSurface,其中 eglCreateWindowSurface 用于创建屏幕上渲染区域,eglCreatePbufferSurface 用于创建屏幕外渲染区域。 -
创建渲染上下文 EGLContext
通过 EGLDisplay 和 EGLConfig ,调用 eglCreateContext 方法创建渲染上下文,得到 EGLContext。 -
绑定上下文
通过 eglMakeCurrent 方法将 EGLSurface、EGLContext、EGLDisplay 三者绑定,绑定成功之后 OpenGLES 环境就创建好了,接下来便可以进行渲染。 -
交换缓冲
OpenGLES 绘制结束后,使用 eglSwapBuffers 方法交换前后缓冲,将绘制内容显示到屏幕上,而屏幕外的渲染不需要调用此方法。 -
释放 EGL 环境
绘制结束后,不再需要使用 EGL 时,需要取消 eglMakeCurrent 的绑定,销毁 EGLDisplay、EGLSurface、EGLContext 三个对象。
代码实现:
// 创建 GLES 环境
int BgRender::CreateGlesEnv()
{
// EGL config attributes
const EGLint confAttr[] =
{
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT_KHR,
EGL_SURFACE_TYPE,EGL_PBUFFER_BIT,//EGL_WINDOW_BIT EGL_PBUFFER_BIT we will create a pixelbuffer surface
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_ALPHA_SIZE, 8,// if you need the alpha channel
EGL_DEPTH_SIZE, 8,// if you need the depth buffer
EGL_STENCIL_SIZE,8,
EGL_NONE
};
// EGL context attributes
const EGLint ctxAttr[] = {
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE
};
// surface attributes
// the surface size is set to the input frame size
const EGLint surfaceAttr[] = {
EGL_WIDTH, 1,
EGL_HEIGHT,1,
EGL_NONE
};
EGLint eglMajVers, eglMinVers;
EGLint numConfigs;
int resultCode = 0;
do
{
//1. 获取 EGLDisplay 对象,建立与本地窗口系统的连接
m_eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if(m_eglDisplay == EGL_NO_DISPLAY)
{
//Unable to open connection to local windowing system
LOGCATE("BgRender::CreateGlesEnv Unable to open connection to local windowing system");
resultCode = -1;
break;
}
//2. 初始化 EGL 方法
if(!eglInitialize(m_eglDisplay, &eglMajVers, &eglMinVers))
{
// Unable to initialize EGL. Handle and recover
LOGCATE("BgRender::CreateGlesEnv Unable to initialize EGL");
resultCode = -1;
break;
}
LOGCATE("BgRender::CreateGlesEnv EGL init with version %d.%d", eglMajVers, eglMinVers);
//3. 获取 EGLConfig 对象,确定渲染表面的配置信息
if(!eglChooseConfig(m_eglDisplay, confAttr, &m_eglConf, 1, &numConfigs))
{
LOGCATE("BgRender::CreateGlesEnv some config is wrong");
resultCode = -1;
break;
}
//4. 创建渲染表面 EGLSurface, 使用 eglCreatePbufferSurface 创建屏幕外渲染区域
m_eglSurface = eglCreatePbufferSurface(m_eglDisplay, m_eglConf, surfaceAttr);
if(m_eglSurface == EGL_NO_SURFACE)
{
switch(eglGetError())
{
case EGL_BAD_ALLOC:
// Not enough resources available. Handle and recover
LOGCATE("BgRender::CreateGlesEnv Not enough resources available");
break;
case EGL_BAD_CONFIG:
// Verify that provided EGLConfig is valid
LOGCATE("BgRender::CreateGlesEnv provided EGLConfig is invalid");
break;
case EGL_BAD_PARAMETER:
// Verify that the EGL_WIDTH and EGL_HEIGHT are
// non-negative values
LOGCATE("BgRender::CreateGlesEnv provided EGL_WIDTH and EGL_HEIGHT is invalid");
break;
case EGL_BAD_MATCH:
// Check window and EGLConfig attributes to determine
// compatibility and pbuffer-texture parameters
LOGCATE("BgRender::CreateGlesEnv Check window and EGLConfig attributes");
break;
}
}
//5. 创建渲染上下文 EGLContext
m_eglCtx = eglCreateContext(m_eglDisplay, m_eglConf, EGL_NO_CONTEXT, ctxAttr);
if(m_eglCtx == EGL_NO_CONTEXT)
{
EGLint error = eglGetError();
if(error == EGL_BAD_CONFIG)
{
// Handle error and recover
LOGCATE("BgRender::CreateGlesEnv EGL_BAD_CONFIG");
resultCode = -1;
break;
}
}
//6. 绑定上下文
if(!eglMakeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglCtx))
{
LOGCATE("BgRender::CreateGlesEnv MakeCurrent failed");
resultCode = -1;
break;
}
LOGCATE("BgRender::CreateGlesEnv initialize success!");
}
while (false);
if (resultCode != 0)
{
LOGCATE("BgRender::CreateGlesEnv fail");
}
return resultCode;
}
//渲染
void BgRender::Draw()
{
LOGCATE("BgRender::Draw");
if (m_ProgramObj == GL_NONE) return;
glViewport(0, 0, m_RenderImage.width, m_RenderImage.height);
// Do FBO off screen rendering
glUseProgram(m_ProgramObj);
glBindFramebuffer(GL_FRAMEBUFFER, m_FboId);
glBindVertexArray(m_VaoIds[0]);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_ImageTextureId);
glUniform1i(m_SamplerLoc, 0);
if (m_TexSizeLoc != GL_NONE) {
GLfloat size[2];
size[0] = m_RenderImage.width;
size[1] = m_RenderImage.height;
glUniform2fv(m_TexSizeLoc, 1, &size[0]);
}
//7. 渲染
GO_CHECK_GL_ERROR();
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, (const void *)0);
GO_CHECK_GL_ERROR();
glBindVertexArray(GL_NONE);
glBindTexture(GL_TEXTURE_2D, GL_NONE);
//一旦解绑 FBO 后面就不能调用 readPixels
//glBindFramebuffer(GL_FRAMEBUFFER, GL_NONE);
}
//释放 GLES 环境
void BgRender::DestroyGlesEnv()
{
//8. 释放 EGL 环境
if (m_eglDisplay != EGL_NO_DISPLAY) {
eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglDestroyContext(m_eglDisplay, m_eglCtx);
eglDestroySurface(m_eglDisplay, m_eglSurface);
eglReleaseThread();
eglTerminate(m_eglDisplay);
}
m_eglDisplay = EGL_NO_DISPLAY;
m_eglSurface = EGL_NO_SURFACE;
m_eglCtx = EGL_NO_CONTEXT;
}
Java 层的代码,主要是一个 ImageView 用于展示渲染前后的图像。
// 创建渲染对象
NativeBgRender mBgRender = new NativeBgRender();
// 初始化创建 GLES 环境
mBgRender.native_BgRenderInit();
// 加载图片数据到纹理
loadRGBAImage(R.drawable.java, mBgRender);
// 离屏渲染
mBgRender.native_BgRenderDraw();
// 从缓冲区读出渲染后的图像数据,加载到 ImageView
mImageView.setImageBitmap(createBitmapFromGLSurface(0, 0, 421, 586));
// 释放 GLES 环境
mBgRender.native_BgRenderUnInit();
private void loadRGBAImage(int resId, NativeBgRender render) {
InputStream is = this.getResources().openRawResource(resId);
Bitmap bitmap;
try {
bitmap = BitmapFactory.decodeStream(is);
if (bitmap != null) {
int bytes = bitmap.getByteCount();
ByteBuffer buf = ByteBuffer.allocate(bytes);
bitmap.copyPixelsToBuffer(buf);
byte[] byteArray = buf.array();
render.native_BgRenderSetImageData(byteArray, bitmap.getWidth(), bitmap.getHeight());
}
}
finally
{
try
{
is.close();
}
catch(IOException e)
{
e.printStackTrace();
}
}
}
private Bitmap createBitmapFromGLSurface(int x, int y, int w, int h) {
int bitmapBuffer[] = new int[w * h];
int bitmapSource[] = new int[w * h];
IntBuffer intBuffer = IntBuffer.wrap(bitmapBuffer);
intBuffer.position(0);
try {
GLES20.glReadPixels(x, y, w, h, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE,
intBuffer);
int offset1, offset2;
for (int i = 0; i < h; i++) {
offset1 = i * w;
offset2 = (h - i - 1) * w;
for (int j = 0; j < w; j++) {
int texturePixel = bitmapBuffer[offset1 + j];
int blue = (texturePixel >> 16) & 0xff;
int red = (texturePixel << 16) & 0x00ff0000;
int pixel = (texturePixel & 0xff00ff00) | red | blue;
bitmapSource[offset2 + j] = pixel;
}
}
} catch (GLException e) {
return null;
}
return Bitmap.createBitmap(bitmapSource, w, h, Bitmap.Config.ARGB_8888);
}
EGL 后台渲染实现代码路径见阅读原文。
本文分享自微信公众号 - 字节流动(google_developer)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
Coder,我怀疑你并不会枚举
枚举是JDK1.5引入的新特性。被enum关键字修饰的类就是一个枚举类。 关于枚举,阿里巴巴开发手册有这样两条建议: 枚举类名带上 Enum 后缀,枚举成员名称需要全大写,单词间用下划线隔开。 如果变量值仅在一个固定范围内变化用 enum 类型来定义。 一 枚举类有哪些特点 创建一个ColorEnum的枚举类,通过编译,再反编译看看它发生了哪些变化。 publicenumColorEnum{RED,GREEN,BULE;} 使用命令javac ColorEnum.java进行编译生成class文件,然后再用命令javap -p ColorEnum.class进行反编译。 去掉包名,反编译后的内容如下: publicfinalclassColorEnumextendsEnum{publicstaticfinalColorEnumGREEN;publicstaticfinalColorEnumBULE;privatestaticfinalColorEnum[]$VALUES;publicstaticColorEnum[]values();publicstaticColorEnumvalue...
- 下一篇
SIYUCMS V6.1.4 版本发布,基于 ThinkPHP 的内容管理系统
SIYUCMS正式发布V6.1.4版本,此次更新的详细内容如下 1、框架:升级ThinkPHP框架版本至6.0.3 2、修复:修复升级框架导致的一些不兼容问题 3、修复:修复前台多级菜单调用失败的bug 4、修复:修复后台返回顶部失效的bug 5、修复:修复后台日期时间类型的字段默认为0时导致添加时候默认不为空的bug 6、修复:修复后台多文件上传字段 7、修复:修复redirect跳转导致重定向后页面刷新pjax失效的bug 8、修复:修复后台字段管理列表批量删除时只删除了第一个的BUG 9、修复:修复后台列表页面当字段为字典类型数据且键不为0,1,2,3时获取一直为'其他'的bug 10、修复:非管理员组人员不可修改所属角色组 11、其他细节优化和调整 欢迎下载体验
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Eclipse初始化配置,告别卡顿、闪退、编译时间过长
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- CentOS7设置SWAP分区,小内存服务器的救世主
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- SpringBoot2整合Thymeleaf,官方推荐html解决方案
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- MySQL8.0.19开启GTID主从同步CentOS8
- SpringBoot2整合Redis,开启缓存,提高访问速度
- Linux系统CentOS6、CentOS7手动修改IP地址
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题