前言
后面咱们公布了一系列的入门教程,例如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 和原生窗口零碎之间的桥梁。
EGL API 是独立于 OpenGL 各版本规范的独立的一套 API,其次要作用是为 OpenGL 指令 创立上下文 Context 、绘制指标 Surface 、配置 FrameBuffer 属性、Swap 提交绘制后果等。EGL提供如下机制:
- 与设施的原生窗口零碎通信
- 查问绘图外表的可用类型和配置
- 创立绘图外表
- 在 OpenGL ES 和其余图形渲染 API 之间同步渲染
- 治理纹理贴图等渲染资源
上面这张图可简要看出EGL的接口能力:
EGL创立流程
想要在安卓上应用Opengl ES咱们能够间接应用GLSurfaceView来进行Opengl的渲染,因为GLSurfaceView外部给咱们封装好了EGL环境和渲染线程。如果咱们想要更高的拓展性,咱们也应用SurfaceView,而后参考SurfaceView中的EGL环境搭建、线程模型来
自行搭建Opengl ES的渲染环境。
本着学习摸索的目标,咱们尝试在NDK搭建EGL环境。
上面这张图展现的是安卓零碎上EGL的次要应用API:
须要阐明的一点是EGL是单线程模型的,也就说说EGL环境的创立、渲染操作、EGL环境的销毁都必须在同一个线程内实现,否则是有效的。当然咱们能够通过共享EGL上下文来做多多线程渲染,但这些都是后话了...
任何OpenGL ES应用程序都必须在开始渲染之前应用EGL执行如下工作:
- 查问并初始化设施商可用的显示器。
- 创立渲染外表。
EGL中创立的外表能够分类为屏幕上的外表或者屏幕外的外表。屏幕上的外表连贯到原生窗口零碎,而屏幕外的外表是不显示然而能够用作渲染外表的像素缓冲区。这些外表能够用来渲染纹理,并能够在多个Khronos API之间共享。 - 创立渲染上下文。
EGL是创立OpenGL ES渲染上下文所必须的。这个上下文必须连贯到适合的外表能力开始渲染。
上面是EGL环境创立的次要流程:
说完焦躁的基础理论,那就放码过去吧!!!
应用Android Studio创立一个Native工程,而后配置好CMakeLists.txt
引入相干库:
cmake_minimum_required(VERSION 3.18.1)project("learn")#找到蕴含所有的cpp文件file(GLOB allCpp *.cpp **/**.cpp **/**/**.cpp **/**/**/**.cpp **/**/**/**/**.cpp)add_library( # Sets the name of the library. # 库名称 learn SHARED ${allCpp})target_link_libraries( learn # 引入egl egl # 引入gles 3 GLESv3 # 安卓相干库 android # 安卓log log)
上面咱们创立一个与Native映射的EGLHelper类:
package com.fly.opengles.learn.egl;import android.view.Surface;public class EGLHelper { protected long nativePtr; public void surfaceCreated(Surface surface) { nativePtrInit(); n_surfaceCreated(nativePtr,surface); } public void surfaceChanged(int width, int height) { nativePtrInit(); n_surfaceChanged(nativePtr,width,height); } public void surfaceDestroyed() { if(nativePtr != 0){ n_surfaceDestroyed(nativePtr); nativePtr = 0; } } private void nativePtrInit(){ if(nativePtr == 0){ nativePtr = n_nativePtrInit(); } } private native long n_nativePtrInit(); private native void n_surfaceCreated(long nativePtr,Surface surface); private native void n_surfaceChanged(long nativePtr,int width, int height); private native void n_surfaceDestroyed(long nativePtr);}
而后自定义一个MySurfaceView继承于SurfaceView,在它的Callback回调办法中对EGL进行操作:
public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback { private EGLHelper eglHelper; public MySurfaceView(Context context) { this(context,null); } public MySurfaceView(Context context, AttributeSet attrs) { super(context, attrs); eglHelper = new EGLHelper(); getHolder().addCallback(this); } @Override public void surfaceCreated(@NonNull SurfaceHolder surfaceHolder) { eglHelper.surfaceCreated(surfaceHolder.getSurface()); } @Override public void surfaceChanged(@NonNull SurfaceHolder surfaceHolder, int i, int w, int h) { eglHelper.surfaceChanged(w,h); } @Override public void surfaceDestroyed(@NonNull SurfaceHolder surfaceHolder) { eglHelper.surfaceDestroyed(); }}
测试成果时,咱们在布局中应用咱们自定义好MySurfaceView即可,自此java层代码编写结束,在NDK层咱们将EGL环境创立结束后即可通过MySurfaceView看到渲染后果。
为了不便调试和debug,咱们定义Log.h
日志工具:
#ifndef NDK_OPENGLES_LEARN_LOG_H#define NDK_OPENGLES_LEARN_LOG_H#include "android/log.h"#define LOGD(FORMAT, ...) __android_log_print(ANDROID_LOG_DEBUG, "fly_learn_opengl", FORMAT, ##__VA_ARGS__);#define LOGE(FORMAT, ...) __android_log_print(ANDROID_LOG_ERROR, "fly_learn_opengl", FORMAT, ##__VA_ARGS__);#endif //NDK_OPENGLES_LEARN_LOG_H
将EGL的相干操作封装在类C++的类EglHelper中:
EglHelper.h#ifndef NDK_OPENGLES_LEARN_EGLHELPER_H#define NDK_OPENGLES_LEARN_EGLHELPER_H#include "EGL/egl.h"class EglHelper {public: EGLDisplay mEglDisplay; EGLSurface mEglSurface; EGLConfig mEglConfig; EGLContext mEglContext;public: EglHelper(); ~EglHelper(); int initEgl(EGLNativeWindowType win); int swapBuffers(); void destroyEgl();};#endif
EglHelper.cpp次要实现如下,EGL的次要创立过程在函数initEgl
中,具体看正文:
#include "EglHelper.h"#include "../utils/Log.h"EglHelper::EglHelper() { mEglDisplay = EGL_NO_DISPLAY; mEglSurface = EGL_NO_SURFACE; mEglContext = EGL_NO_CONTEXT; mEglConfig = NULL;}EglHelper::~EglHelper() { destroyEgl();}int EglHelper::initEgl(EGLNativeWindowType window) { //1、获取显示设施 mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); if(mEglDisplay == EGL_NO_DISPLAY) { LOGE("eglGetDisplay error"); return -1; } // 2、 EGL初始化 EGLint *version = new EGLint[2]; if(!eglInitialize(mEglDisplay, &version[0], &version[1])) { LOGE("eglInitialize error"); return -1; } //3、 资源配置,例如色彩位数等 const EGLint attribs[] = { EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, EGL_DEPTH_SIZE, 8, EGL_STENCIL_SIZE, 8, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_NONE }; EGLint num_config; if(!eglChooseConfig(mEglDisplay, attribs, NULL, 1, &num_config)) { LOGE("eglChooseConfig error 1"); return -1; } //4、ChooseConfig if(!eglChooseConfig(mEglDisplay, attribs, &mEglConfig, num_config, &num_config)) { LOGE("eglChooseConfig error 2"); return -1; } // 5、创立上下文 int attrib_list[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, attrib_list); if(mEglContext == EGL_NO_CONTEXT) { LOGE("eglCreateContext error"); return -1; } //6、创立渲染的Surface mEglSurface = eglCreateWindowSurface(mEglDisplay, mEglConfig, window, NULL); if(mEglSurface == EGL_NO_SURFACE) { LOGE("eglCreateWindowSurface error"); return -1; } // 7、应用 if(!eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) { LOGE("eglMakeCurrent error"); return -1; } LOGD("egl init success! "); return 0;}int EglHelper::swapBuffers() { if(mEglDisplay != EGL_NO_DISPLAY && mEglSurface != EGL_NO_SURFACE) { if(eglSwapBuffers(mEglDisplay, mEglSurface)) { return 0; } } return -1;}void EglHelper::destroyEgl() { if(mEglDisplay != EGL_NO_DISPLAY) { eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); } if(mEglDisplay != EGL_NO_DISPLAY && mEglSurface != EGL_NO_SURFACE) { eglDestroySurface(mEglDisplay, mEglSurface); mEglSurface = EGL_NO_SURFACE; } if(mEglDisplay != EGL_NO_DISPLAY && mEglContext != EGL_NO_CONTEXT){ eglDestroyContext(mEglDisplay, mEglContext); mEglContext = EGL_NO_CONTEXT; } if(mEglDisplay != EGL_NO_DISPLAY) { eglTerminate(mEglDisplay); mEglDisplay = EGL_NO_DISPLAY; }}
本人EGL环境创立结束,咱们通过JNI调用起来看看成果,native-lib.cpp:
#include <jni.h>#include <string>#include "eglhelper/EglHelper.h"#include <cstdint>#include "android/native_window.h"#include "android/native_window_jni.h"#include "GLES3/gl3.h"jlong eglHelperNativePtrInit(JNIEnv *env, jobject thiz) { EglHelper *eglHelper = new EglHelper(); return reinterpret_cast<uintptr_t>(eglHelper);}void eglSurfaceCreated(JNIEnv *env, jobject thiz,jlong native_ptr, jobject surface) { if(native_ptr != 0){ EglHelper *eglHelper = reinterpret_cast<EglHelper *>(native_ptr); ANativeWindow *nativeWindow = ANativeWindow_fromSurface(env, surface); eglHelper->initEgl(nativeWindow); }}void eglSurfaceChanged(JNIEnv *env, jobject thiz,jlong native_ptr, jint width,jint height) { if(native_ptr != 0){ //设置视口大小 glViewport(0, 0, width, height); // 绿色清屏// glClearColor(0.0f, 1.0f, 0.0f, 1.0f); // 蓝色清屏 glClearColor(0.0f, 0.0f, 1.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); EglHelper *eglHelper = reinterpret_cast<EglHelper *>(native_ptr); eglHelper->swapBuffers(); }}void eglSurfaceDestroyed(JNIEnv *env, jobject thiz,jlong native_ptr) { if(native_ptr != 0){ EglHelper *eglHelper = reinterpret_cast<EglHelper *>(native_ptr); delete eglHelper; }}static JNINativeMethod nativeMethod_EGLHelper[] = { // Java中的函数名 {"n_nativePtrInit", // 函数签名信息 "()J", // native的函数指针 (jlong *) (eglHelperNativePtrInit)}, {"n_surfaceCreated", // 函数签名信息 "(JLandroid/view/Surface;)V", // native的函数指针 (void *) (eglSurfaceCreated)}, {"n_surfaceChanged", // 函数签名信息 "(JII)V", // native的函数指针 (void *) (eglSurfaceChanged)}, {"n_surfaceDestroyed", // 函数签名信息 "(J)V", // native的函数指针 (void *) (eglSurfaceDestroyed)},};static int RegisterNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *methods, int methodNum){ jclass clazz = env->FindClass(className); if (clazz == NULL) { return JNI_FALSE; } if (env->RegisterNatives(clazz, methods, methodNum) < 0) { return JNI_FALSE; } return JNI_TRUE;}// 类库加载时主动调用JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reversed){ JNIEnv *env = NULL; // 初始化JNIEnv if(vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6) != JNI_OK){ return JNI_FALSE; } // 动静注册 RegisterNativeMethods(env,"com/fly/opengles/learn/egl/EGLHelper",nativeMethod_EGLHelper,sizeof(nativeMethod_EGLHelper) / sizeof(JNINativeMethod) ); // 返回JNI应用的版本 return JNI_VERSION_1_6;}
上述native-lib.cpp
波及到了之前介绍过的JNI函数签名、动静注册等相干知识点,遗记了的童鞋可往回看之前的记录。
如无意外,运行看到的是一个蓝屏画面则阐明EGL环境搭建胜利了,后续开启你的Opengl炫酷之旅吧!!!
举荐浏览
JNI根底简介
JNI之数组与字符串的应用
JNI之动静注册与动态注册
JNI之拜访java属性和办法
JNI之缓存与援用
JNI之异样解决
JNI之罕用技巧与陷阱
关注我,一起提高,人生不止coding!!!