opengles,EGL以及OpenGLES命令-简书
前言:我们已经出版了一系列入门教程,如指针读写C系列,多线程的使用,JNI入门系列,ffmpeg入门系列等。感兴趣的童鞋可以关注一下自己回去咨询。
今天的话题依然是音视频开发的范畴。做过音视频开发的人都知道,Opengl也是音视频开发的重要技能,尤其是涉及视频录制、特效处理、画质渲染、细分等。所以笔者打算在后续做一系列Opengl ES的学习笔记,希望能和大家一起温故而知新。
因为前面已经介绍了NDK和C的一些教程,为了巩固,后续的一些demo以NDK的形式呈现给大家,使用的是Opengl ES3的版本。
我们今天的话题是Opengl ES - EGL的第一篇文章。
什么是EGL?众所周知,Opengl是跨平台的。那么,面对各种平台的差异,Opengl是如何抹平并实现跨平台的呢?也许这是EGL的功劳。简单来说,EGL是Opengl和平台之间的适配器,是一系列接口。具体实现由具体的设备厂商来实现。
EGL是渲染API(如OpenGL ES)和原生窗口系统之间的接口。一般来说,OpenGL是操作GPU的API,通过驱动程序向GPU发送相关指令,控制图形渲染流水线状态机的运行状态。但是,当涉及到与本地窗口系统交互时,就需要这样一个中间层,而且最好是平台无关的,所以EGL被设计成OpenGL和原生窗口系统之间的桥梁。
Egapi是一组独立于OpenGL标准的api,主要功能是为OpenGL指令创建上下文,绘制目标面,配置FrameBuffer属性,通过Swap提交绘制结果。EGL提供了以下机制:
与设备的原生窗口系统进行通信,查询绘制表面的可用类型和配置,创建绘制表面,在OpenGL ES和其他图形渲染API之间同步渲染,管理纹理贴图等渲染资源。下图可以简要显示EGL的接口能力:
EGL创建过程如果你想在Android上使用Opengl ES,我们可以直接使用GLSurfaceView来渲染Opengl,因为GLSurfaceView为我们封装了EGL环境和渲染线程。如果想要更高的扩展性,我们也使用SurfaceView,然后参考SurfaceView中的EGL环境构建和线程模型,自己构建Opengl ES渲染环境。
为了学习和探索,我们试图在NDK建立一个EGL环境。
下图是EGL在Android上的主要API:
需要注意的是,EGL是一个单线程模型,也就是说,EGL环境的创建、渲染和破坏都必须在同一个线程中完成,否则无效。当然,我们可以通过共享EGL上下文来进行多线程渲染,但这是后话了。
在开始渲染之前,任何OpenGL ES应用程序都必须使用EGL来执行以下任务:
并查询和初始化设备制造商可用的显示。创建呈现图面。
在EGL创建的曲面可以分为屏幕上的曲面和屏幕外的曲面。屏幕上的表面连接到本机窗口系统,而屏幕外的表面是一个像素缓冲区,它不显示,但可以用作呈现表面。这些表面可以用来渲染纹理,并且可以在多个Khronos API之间共享。创建渲染上下文。
EGL是创建OpenGL ES渲染上下文所必需的。此上下文必须连接到合适的表面才能开始渲染。以下是EGL环境创建的主要过程:
说完烦躁的基本理论,然后放代码!
使用Android Studio创建一个原生项目,然后配置CMakeLists.txt导入相关库:
cmake_minimum_required(版本3.18.1)
项目(“学习”)
#找到包含所有文件的cpp文件
文件(GLOB allCpp *。cpp **/**。cpp **/**/**。cpp **/**/**/**。cpp **/**/**/**/**。cpp)
add_library( #设置库的名称。
#库名
学习
共享的
${allCpp})
目标_链接_库(
学习
egl简介
欧洲珠宝实验室
#介绍gles 3
GLESv3
# Android相关库
机器人
# Android日志
Log)接下来,我们创建一个EGLHelper类,它与Native:
包com . fly . opengles . learn . egl;
导入安卓。查看。表面;
公共类EGLHelper {
受保护的long nativePtr
公共空表面已创建(表面表面){
nativePtrInit();
n_surfaceCreated(nativePtr,surface);
}
公共空表面已更改(int width,int height) {
nativePtrInit();
n_surfaceChanged(nativePtr,width,height);
}
public void surfaceDestroyed() {
if(nativePtr!=0){
n _表面破坏(原生ptr);
原生ptr=0;
}
}
私有void nativePtrInit(){
if(nativePtr==0){
native ptr=n _ nativePtrInit();
}
}
private native long n _ nativePtrInit();
private native void n _ Surface已创建(long native ptr,Surface Surface);
private native void n _ surface已更改(long native ptr,int width,int height);
私有原生void n _ surface销毁(长原生ptr);
}然后自定义一个MySurfaceView继承于SurfaceView,在它的回收回调方法中对欧洲珠宝实验室进行操作:
公共类MySurfaceView扩展表面视图实现表面固定器.回拨{
私人埃格尔赫尔珀
公共MySurfaceView(上下文上下文){
这(上下文,空);
}
公共MySurfaceView(上下文上下文,属性集属性){
超级(上下文,attrs);
egl helper=new egl helper();
getHolder().添加回调(this);
}
@覆盖
已创建公共空曲面(@非空曲面持有者曲面持有者){
egl助手。创建的曲面(曲面固定器。get surface());
}
@覆盖
公共空表面已更改(@非空表面持有者表面持有者,int i,int w,int h) {
eglHelper.surfaceChanged(w,h);
}
@覆盖
公共空表面已销毁(@非空表面持有者表面持有者){
egl助手。表面销毁();
}
}测试效果时,我们在布局中使用我们自定义好MySurfaceView即可,自此爪哇岛层代码编写完毕,在日本电波工业株式会社层我们将欧洲珠宝实验室环境创建完毕后即可通过MySurfaceView看到渲染结果。
为了方便调试和调试,我们定义Log.h日志工具:
# ifndef NDK _ OPENGLES _ LEARN _ LOG _ H
#定义NDK _开放_学习_日志_H
#包含" android/log.h "
#定义LOGD(格式、)_ _ ANDROID _ LOG _ print(ANDROID _ LOG _ DEBUG, fly_learn_opengl ,格式,# # _ _ VA _ ARGS _ _);
#定义日志(格式、)_ _ ANDROID _ LOG _ print(ANDROID _ LOG _ ERROR, fly_learn_opengl ,格式,# # _ _ VA _ ARGS _ _);
# endif//NDK _ OPENGLES _学习_日志_H将欧洲珠宝实验室的相关操作封装在类C的类EglHelper中:
EglHelper
# IFN def NDK _ OPENGLES _ LEARN _ EGLHELPER _ H
#定义NDK_OPENGLES_LEARN_EGLHELPER_H
#包括" EGL/egl.h "
EglHelper类{
公共:
EGLDisplay mEglDisplay
EGLSurface mEglSurface
mEglConfig
mEglContext
公共:
egl helper();
~ egl helper();
int init egl(EGLNativeWindowType win);
int交换缓冲区();
虚空毁灭者GL();
};
#endifEglHelper.cpp主要实现如下,EGL的主要创建过程在函数initEgl中,具体看注释:
#包含" EglHelper.h "
#包含./utils/Log.h
EglHelper:EglHelper() {
mEglDisplay=EGL _ NO _ DISPLAY;
mEglSurface=EGL _ NO _ SURFACE;
mEglContext=EGL _ NO _ CONTEXT;
mEglConfig=NULL
}
EglHelper:~EglHelper() {
扭曲GL();
}
int egl helper:init egl(EGLNativeWindowType window){
//1、获取显示设备
mEglDisplay=eglGetDisplay(EGL _默认_显示);
if(mEglDisplay==EGL_NO_DISPLAY)
{
日志( eglGetDisplay错误);
return-1;
}
//2、EGL初始化
EGLint * version=new EGLint[2];
如果(!eglInitialize(mEglDisplay,版本[0],版本[1])
{
LOGE( egl初始化错误);
return-1;
}
//3、 资源配置,例如颜色位数等
const EGLint attribs[]={
欧洲珠宝实验室红色尺码8号,
欧洲珠宝实验室绿色尺寸,8,
欧洲珠宝实验室蓝色尺寸,8,
欧洲珠宝实验室阿尔法大小,8,
EGL _深度_大小,8,
欧洲珠宝实验室模板尺寸,8,
ES2 _可渲染_类型,EGL _ OPENGL _ ES2 _位,
欧洲珠宝实验室没有
};
EGLint num _ config
如果(!eglChooseConfig(mEglDisplay,attribs,NULL,1,num_config))
{
LOGE( eglChooseConfig error 1 );
return-1;
}
//4、选择配置
如果(!eglChooseConfig(mEglDisplay,attribs,mEglConfig,num_config,num_config))
{
LOGE( eglChooseConfig error 2 );
return-1;
}
//5、创建上下文
int attrib_list[]={
欧洲珠宝实验室上下文客户端版本,2,
欧洲珠宝实验室没有
};
mEglContext=eglCreateContext(megl display,mEglConfig,EGL_NO_CONTEXT,attrib _ list);
if(mEglContext==EGL无上下文)
{
LOGE("eglCreateContext错误");
return-1;
}
//6、创建渲染的表面
megl surface=eglCreateWindowSurface(megl display,mEglConfig,window,NULL);
if(mEglSurface==EGL _诺_表面)
{
LOGE(eglCreateWindowSurface错误);
return-1;
}
//7、使用
如果(!eglMakeCurrent(mEglDisplay,mEglSurface,mEglSurface,mEglContext))
{
LOGE(eglMakeCurrent错误);
return-1;
}
LOGD(egl初始化成功!);
返回0;
}
int EglHelper:swapBuffers() {
if(mEglDisplay!=EGL_NO_DISPLAY mEglSurface!=EGL无表面)
{
if(eglSwapBuffers(mEglDisplay,mEglSurface))
{
返回0;
}
}
return-1;
}
void egl helper:destroyer GL(){
if(mEglDisplay!=EGL _无_显示)
{
eglMakeCurrent(mEglDisplay,EGL_NO_SURFACE,EGL_NO_SURFACE,EGL _ NO _ CONTEXT);
}
if(mEglDisplay!=EGL_NO_DISPLAY mEglSurface!=EGL无表面)
{
egldestroyssurface(mEglDisplay,mEglSurface);
mEglSurface=EGL _ NO _ SURFACE;
}
if(mEglDisplay!=EGL_NO_DISPLAY mEglContext!=EGL _无_上下文){
eglDestroyContext(mEglDisplay,megl context);
mEglContext=EGL _ NO _ CONTEXT;
}
if(mEglDisplay!=EGL _无_显示)
{
egl终止(megl显示);
mEglDisplay=EGL _ NO _ DISPLAY;
}
}自己欧洲珠宝实验室环境创建完毕,我们通过调用调用起来看看效果,native-lib.cpp:
#包含jni.h
#包含字符串
#包含 eglhelper/EglHelper.h
#包含cstdint
# include Android/native _ window。 h
# include Android/native _ window _ JNI。 h
#包含 GLES3/gl3.h
jlong eglHelperNativePtrInit(JNIEnv * env,job object thiz){
egl helper * egl helper=new egl helper();
返回reinterpret _ cast uintptr _ t(eglHelper);
}
void eglSurfaceCreated(JNIEnv * env,jobject thiz,jlong native_ptr,job object surface){
if(native_ptr!=0){
egl helper * egl helper=reinterpret _ cast egl helper *(native _ ptr);
a tive window * native window=a tive window _ from surface(env,surface);
egl helper-init egl(原生窗口);
}
}
void eglSurfaceChanged(JNIEnv * env,jobject thiz,jlong native_ptr,jint width,jint height) {
if(native_ptr!=0){
//设置视口大小
glViewport(0,0,宽度,高度);
//绿色清屏
//glClearColor(0.0f,1.0f,0.0f,1.0f);
//蓝色清屏
glClearColor(0.0f,0.0f,1.0f,1.0f);
GL清零(GL _ COLOR _ BUFFER _ BIT);
egl helper * egl helper=reinterpret _ cast egl helper *(native _ ptr);
egl助手-交换缓冲区();
}
}
void eglsurfacedestated(JNIEnv * env,jobject thiz,jlong native_ptr) {
if(native_ptr!=0){
egl helper * egl helper=reinterpret _ cast egl helper *(native _ ptr);
删除eglHelper
}
}
静态jnitivemethod native method _ egl helper[]={
//Java中的函数名
{n_nativePtrInit ,
//函数签名信息
() J ,
//本机的函数指针
(jlong *)(eglHelperNativePtrInit)},
{n_surfaceCreated ,
//函数签名信息
(JL Android/view/Surface;)V’,
//本机的函数指针
(void *) (eglSurfaceCreated)},
{n_surfaceChanged ,
//函数签名信息
(JII)V ,
//本机的函数指针
(void *) (eglSurfaceChanged)},
{n_surfaceDestroyed ,
//函数签名信息
(J)五,
//本机的函数指针
(void *)(eglsurfacedestated)},
};
静态int注册方法(JNIEnv * env,const char *className,JNINativeMethod * methods,int methodNum)
{
jclass clazz=env-find class(类名);
if (clazz==NULL)
{
返回JNI _假;
}
if (env- RegisterNatives(clazz,methods,methodNum) 0)
{
返回JNI _假;
}
返回JNI _真;
}
//类库加载时自动调用
JNI导出jint JNI调用JNI _ OnLoad(Java VM * VM,void *reversed)
{
JNIEnv * env=NULL
//初始化JNIEnv
if(VM-GetEnv(reinterpret _ cast void * *(env),JNI _版本_1_6)!=JNI_OK){
返回JNI _假;
}
//动态注册
RegisterNativeMethods(env, com/fly/opengles/learn/egl/EGLHelper ,nativeMethod_EGLHelper,sizeof(native method _ EGLHelper)/sizeof(jnitativemethod));
//返回调用使用的版本
返回JNI _版本_ 1 _ 6;
}以上native-lib.cpp涉及到之前介绍的JNI函数签名、动态注册等相关知识点。对于被遗忘的童鞋,回头看看之前的记录。
如果没有意外,您在操作中看到的是一个蓝屏,这意味着EGL环境已经构建成功。那就开始你的Opengl炫酷之旅吧!
阅读JNI的基本介绍,推荐使用JNI的数组和字符串。
JNI的动态注册和静态注册
JNI的access java属性和方法
JNI的收藏与参考
JNI的异常处理
JNI的常用技巧和陷阱
关注我,共同进步,生活不止编码!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。