找回密码
 立即注册
首页 业界区 业界 【OpenGL ES】在Android上手撕一个mini版的渲染框架 ...

【OpenGL ES】在Android上手撕一个mini版的渲染框架

咒卖箴 前天 21:15
1 前言

1.1 开发该框架的动机

​    OpenGL ES 是一个渲染指令接口集合,每渲染一帧图像都是一系列渲染指令的排列组合。常用的渲染指令约有 70 个,记住这些渲染指令及其排列组合方式,是一件痛苦的事情。另外,在图形开发中,经常因为功耗、丢帧等问题需要性能优化,如何从框架层面进行性能优化是一件有挑战的问题。
​    基于上述原因,笔者手撕了一个 nimi 版的渲染框架,将这些常用的渲染指令有条理地封装、组织、归类,方便愉快并高效地进行 OpenGL ES 渲染开发。笔者在 OpenGL ES 领域从业也有些时日,对现有碎片化的知识进行归纳凝练,形成系统的认知,是件势在必行的事。
1.2 为什么选择 native

​    之所以选择在 native 中开发该渲染框架,是为了使该框架具有更好的跨平台特性和渲染效率。目前大多数平台的 OpenGL ES API 基于 C++ 实现,因此只需更改少量代码就可以将该框架迁移到其他平台上;另外,C++ 代码相较于 Java 等代码具有更高的执行效率。Windows 上的实现详见 → 在Windows上手撕一个mini版的渲染框架。
1.3 一个 mini 版的渲染框架应该具备哪些能力

​    一个 mini 版的渲染框架需要对 OpenGL ES 的常用指令进行归类(如下图),封装 EGL、error check、Shader Program、Mesh、VAO、VBO、IBO、Texture、FBO 等类,方便开发者快速开发渲染程序,将更多的注意力聚焦在业务上,而不是如何去组织 OpenGL ES 指令上。
1.png

1.4 为什么强调 mini 版渲染框架

​    从渲染指令的角度来看,OpenGL ES 3.0 约有 300 个渲染指令,本文框架只封装其中最常用的 70 个,指令覆盖程度仍有较大提升空间。
​    从功能的角度来看,笔者深知一个成熟完备的渲染框架应该具备相机、光源、光照模型(Lambert、Phong、PBR 等)、阴影、射线拾取、重力、碰撞检测、粒子系统等功能。
​    鉴于上述原因,笔者审慎地保留了 "mini" 前缀。
1.5 本框架的优势

​    本框架具有以下优势。

  • 封装友好:对常用的 EGL 和 GL 指令(约 70 个)进行封装,提供了 EGL 环境搭建、着色器程序生成、网格构建、纹理贴图、离屏渲染、异常检测等基础能力,方便开发者快速开发渲染程序,将精力从繁杂的渲染指令中解放出来,将更多的注意力聚焦到业务上。
  • 代码规整:框架中多处设计了 bind 和 unbind 接口,用于绑定和解绑 OpenGL ES 状态机相关 “插槽”,如:VBO、IBO、VAO 中都设计了 bind 和 unbind 接口,ShaderProgram、Texture、FBO、TextureAction 中都设计了 bind 接口;另外,在 FBO 中设计了 begin 和 end 接口,很直观地告诉用户夹在这中间的内容将渲染到 FBO。接口规整简洁,方便用户记忆。
  • 易于扩展:定义了 TextureAction 接口,并提供 bind 函数,GLTexture、FBO 都继承了 TextureAction,用户自定义的渲染器或特效类也可以继承 TextureAction,将它们统一视为纹理活动(可绑定),这在特效叠加(或后处理)中非常有用,方便管理多渲染目标图层,易于扩展。
  • 性能高效:封装了 VBO、IBO、VAO,用于缓存顶点数据、索引、格式等信息到显存,减少 CPU 到 GPU 的数据传输,提高渲染效率;缓存了 attribute 和 uniform 变量的 location,避免 CPU 频繁向 GPU 查询 location,进一步提高渲染效率;基于 C++ 语言实现渲染框架,代码执行效率较高。
  • 跨平台:基于 C++ 语言实现,具有更好的跨平台特性;封装了 core_lib,使得平台相关头文件可以轻松替换;封装了 Application,使得平台相关 api 可以轻松替换。
  • 方便调试:设计了 EGL_CALL 和 GL_CALL 两个宏,对每个 EGL 和 GL 指令进行异常检测,方便调试渲染指令,并且通过预编译宏 DEBUG 开关动态控制是否生成异常检测的代码,Release 版本会自动屏蔽异常检测代码,避免带来额外功耗。
2 渲染框架

​    经过深思熟虑,笔者给该渲染框架命名为 glcore,命名空间也是 glcore。本文完整资源(包含 glcore 框架和第 4 节的应用)详见 → 【OpenGL ES】一个mini版的Android native渲染框架 。Windows 版本的 glcore 实现详见 → 在Windows上手撕一个mini版的渲染框架。
2.1 框架结构

2.png

2.2 CMakeLists

​    CMakeLists.txt
  1. # 设置库名
  2. set(LIB_NAME "glcore")
  3. # 递归添加源文件列表
  4. file(GLOB_RECURSE GL_CORE_SOURCES src *.cpp)
  5. # 添加预构建库
  6. add_library(${LIB_NAME} ${GL_CORE_SOURCES})
  7. # 将当前目录设为公共头文件目录 (任何链接glcore库的目标都能自动获得这个头文件路径)
  8. target_include_directories(${LIB_NAME} PUBLIC .)
  9. # 添加链接的三方库文件
  10. target_link_libraries(${LIB_NAME} PRIVATE
  11.         android
  12.         log
  13.         EGL
  14.         GLESv3)
复制代码
2.3 核心头文件

​    核心头文件分为对内和对外的,即内部依赖 core_lib,外部开放 core。
​    core_lib.h
  1. #pragma once
  2. /**
  3. * glcore 依赖的核心 GL 库, 便于将 glcore 移植到其他平台
  4. * Android: EGL + GLESv3
  5. * Windows: glfw / freeglut + glad / glew
  6. *
  7. * @author little fat sheep
  8. */
  9. #include <EGL/egl.h>
  10. #include <GLES3/gl3.h>
复制代码
​    之所以要单独拎出 core_lib.h,是为了方便将该框架迁移到其他平台,如 Windows 上依赖的三方渲染库是 glfw / freeglut + glad / glew,如果不抽出 core_lib.h,就需要将很多地方的 egl.h + gl3.h 改为 glfw3.h / freeglut.h + glad.h / glew.h,工作量大,也容易漏改。
​    core.h
  1. #pragma once
  2. /**
  3. * glcore核心头文件
  4. * 该头文件是留给外部使用的, glcore内部不能使用, 避免自己包含自己
  5. * @author little fat sheep
  6. */
  7. // OpenGL ES API
  8. #include "core_lib.h"
  9. // glcore 核心头文件
  10. #include "application.h"
  11. #include "elg_surface_view.h"
  12. #include "format.h"
  13. #include "frame_buffer_object.h"
  14. #include "gl_inspector.h"
  15. #include "gl_texture.h"
  16. #include "mesh.h"
  17. #include "mesh_utils.h"
  18. #include "shader_program.h"
  19. #include "texture_action.h"
  20. #include "vertex_attribute.h"
复制代码
​     core.h 只提供给外部使用,方便外部只需要包含一个头文件,就能获取 glcore 的基础能力。
2.4 Application

​    Application 主要用于管理全局环境,使用单例模式,方便获取一些全局的变量。它也是 glcore 中唯一一个依赖平台相关的接口(除日志 log 接口外),如:jniEnv、context、m_window 都是 Android 特有的,如果将 glcore 迁移到 Windows 中,这些变量全都要替换或删除,将这些平台相关变量都集中在 Application 中,迁移平台时修改起来也比较容易,避免太分散容易漏掉。
​    application.h
  1. #pragma once
  2. #include
  3. #include <jni.h>
  4. #define app Application::getInstance()
  5. namespace glcore
  6. {
  7. /**
  8. * 应用程序, 存储全局的参数, 便于访问
  9. * @author little fat sheep
  10. */
  11. class Application {
  12. private:
  13.     static Application* sInstance;
  14. public:
  15.     JNIEnv* jniEnv = nullptr;
  16.     jobject context = nullptr;
  17.     int width = 0;
  18.     int height = 0;
  19.     float aspect = 1.0f;
  20. private:
  21.     ANativeWindow* m_window = nullptr;
  22. public:
  23.     static Application* getInstance();
  24.     ~Application();
  25.     void resize(int width, int height);
  26.     ANativeWindow* getWindow() { return m_window; }
  27.     void setWindow(ANativeWindow* window);
  28.     void releaseWindow();
  29. private:
  30.     Application() {};
  31. };
  32. } // namespace glcore
复制代码
​    application.cpp
  1. #include "glcore/application.h"
  2. namespace glcore
  3. {
  4. Application* Application::sInstance = nullptr;
  5. Application *Application::getInstance()
  6. {
  7.     if (sInstance == nullptr)
  8.     {
  9.         sInstance = new Application();
  10.     }
  11.     return sInstance;
  12. }
  13. Application::~Application()
  14. {
  15.     jniEnv->DeleteGlobalRef(context);
  16.     releaseWindow();
  17. }
  18. void Application::resize(int width, int height)
  19. {
  20.     this->width = width;
  21.     this->height = height;
  22.     this->aspect = (float) width / (float) height;
  23. }
  24. void Application::setWindow(ANativeWindow* window)
  25. {
  26.     m_window = window;
  27.     resize(ANativeWindow_getWidth(window), ANativeWindow_getHeight(window));
  28. }
  29. void Application::releaseWindow()
  30. {
  31.     if (m_window)
  32.     {
  33.         ANativeWindow_release(m_window);
  34.         m_window = nullptr;
  35.     }
  36. }
  37. } // namespace glcore
复制代码
2.5 GLInspector

​    GLInspector 主要用于异常信息检测,另外设计了 EGL_CALL 和 GL_CALL 两个宏,分别对 EGL 和 GL 指令进行装饰。如果定义了 DEBUG 宏,就会对每个 EGL 和 GL 指令进行异常检测,方便调试代码;如果未定义了 DEBUG 宏,就不会进行异常检测。
​    用户可以在 CMakeLists.txt 中添加预编译宏 DEBUG,这样就可以根据 Release 和 Debug 版本自动构建不同的版本。
  1. if (CMAKE_BUILD_TYPE STREQUAL "Debug")
  2.     # 添加预编译宏
  3.     add_definitions(-DDEBUG)
  4. endif ()
复制代码
​    gl_inspector.h
  1. #pragma once
  2. #include "core_lib.h"
  3. #ifdef DEBUG
  4. #define EGL_CALL(func) func;GLInspector::checkEGLError();
  5. #define GL_CALL(func) func;GLInspector::checkGLError();
  6. #else
  7. #define EGL_CALL(func) func;
  8. #define GL_CALL(func) func;
  9. #endif
  10. namespace glcore
  11. {
  12. /**
  13. * OpenGL ES命令报错监视器
  14. * @author little fat sheep
  15. */
  16. class GLInspector
  17. {
  18. public:
  19.     static void checkEGLError(const char* tag); // 检查EGL报错信息
  20.     static void checkEGLError(); // 通用检查EGL错误
  21.     static void printShaderInfoLog(GLuint shader, const char* tag); // 打印Shader错误日志
  22.     static void printProgramInfoLog(GLuint program, const char* tag); // 打印Program错误日志
  23.     static void checkGLError(const char* tag); // 检查GL报错信息
  24.     static void checkGLError(); // 通用检查GL报错信息
  25. };
  26. } // namespace glcore
复制代码
​    gl_inspector.cpp
  1. #include
  2. #include
  3. #include <string>
  4. #include "glcore/gl_inspector.h"
  5. #define LOG_TAG "Native: GLInspector"
  6. #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
  7. using namespace std;
  8. namespace glcore
  9. {
  10. void GLInspector::checkEGLError(const char *tag)
  11. {
  12.     int error = eglGetError();
  13.     if (error != EGL_SUCCESS) {
  14.         LOGE("%s failed: 0x%x", tag, error);
  15.     }
  16. }
  17. void GLInspector::checkEGLError()
  18. {
  19.     GLenum errorCode = eglGetError();
  20.     if (errorCode != EGL_SUCCESS) {
  21.         string error;
  22.         switch (errorCode)
  23.         {
  24.             case EGL_BAD_DISPLAY:
  25.                 error = "EGL_BAD_DISPLAY";
  26.                 break;
  27.             case EGL_NOT_INITIALIZED:
  28.                 error = "EGL_NOT_INITIALIZED";
  29.                 break;
  30.             case EGL_BAD_CONFIG:
  31.                 error = "EGL_BAD_CONFIG";
  32.                 break;
  33.             case EGL_BAD_CONTEXT:
  34.                 error = "EGL_BAD_CONTEXT";
  35.                 break;
  36.             case EGL_BAD_NATIVE_WINDOW:
  37.                 error = "EGL_BAD_NATIVE_WINDOW";
  38.                 break;
  39.             case EGL_BAD_SURFACE:
  40.                 error = "EGL_BAD_SURFACE";
  41.                 break;
  42.             case EGL_BAD_CURRENT_SURFACE:
  43.                 error = "EGL_BAD_CURRENT_SURFACE";
  44.                 break;
  45.             case EGL_BAD_ACCESS:
  46.                 error = "EGL_BAD_ACCESS";
  47.                 break;
  48.             case EGL_BAD_ALLOC:
  49.                 error = "EGL_BAD_ALLOC";
  50.                 break;
  51.             case EGL_BAD_ATTRIBUTE:
  52.                 error = "EGL_BAD_ATTRIBUTE";
  53.                 break;
  54.             case EGL_BAD_PARAMETER:
  55.                 error = "EGL_BAD_PARAMETER";
  56.                 break;
  57.             case EGL_BAD_NATIVE_PIXMAP:
  58.                 error = "EGL_BAD_NATIVE_PIXMAP";
  59.                 break;
  60.             case EGL_BAD_MATCH:
  61.                 error = "EGL_BAD_MATCH";
  62.                 break;
  63.             case EGL_CONTEXT_LOST:
  64.                 error = "EGL_CONTEXT_LOST";
  65.                 break;
  66.             default:
  67.                 error = "UNKNOW";
  68.                 break;
  69.         }
  70.         LOGE("checkEGLError failed: %s, 0x%x", error.c_str(), errorCode);
  71.         assert(false);
  72.     }
  73. }
  74. void GLInspector::printShaderInfoLog(GLuint shader, const char* tag)
  75. {
  76.     char infoLog[512];
  77.     glGetShaderInfoLog(shader, 512, nullptr, infoLog);
  78.     LOGE("%s failed: %s", tag, infoLog);
  79. }
  80. void GLInspector::printProgramInfoLog(GLuint program, const char* tag)
  81. {
  82.     char infoLog[512];
  83.     glGetProgramInfoLog(program, 512, nullptr, infoLog);
  84.     LOGE("%s failed: %s", tag, infoLog);
  85. }
  86. void GLInspector::checkGLError(const char *tag) {
  87.     GLenum error = glGetError();
  88.     if(error != GL_NO_ERROR) {
  89.         LOGE("%s failed: 0x%x", tag, error);
  90.     }
  91. }
  92. void GLInspector::checkGLError()
  93. {
  94.     GLenum errorCode = glGetError();
  95.     if (errorCode != GL_NO_ERROR) {
  96.         string error;
  97.         switch (errorCode)
  98.         {
  99.             case GL_INVALID_ENUM:
  100.                 error = "GL_INVALID_ENUM";
  101.                 break;
  102.             case GL_INVALID_VALUE:
  103.                 error = "GL_INVALID_VALUE";
  104.                 break;
  105.             case GL_INVALID_OPERATION:
  106.                 error = "GL_INVALID_OPERATION";
  107.                 break;
  108.             case GL_INVALID_INDEX:
  109.                 error = "GL_INVALID_INDEX";
  110.                 break;
  111.             case GL_INVALID_FRAMEBUFFER_OPERATION:
  112.                 error = "GL_INVALID_FRAMEBUFFER_OPERATION";
  113.                 break;
  114.             case GL_OUT_OF_MEMORY:
  115.                 error = "GL_OUT_OF_MEMORY";
  116.                 break;
  117.             default:
  118.                 error = "UNKNOW";
  119.                 break;
  120.         }
  121.         LOGE("checkGLError failed: %s, 0x%x", error.c_str(), errorCode);
  122.         assert(false);
  123.     }
  124. }
  125. } // namespace glcore
复制代码
2.6 EGLSurfaceView

​    EGLSurfaceView 主要承载了 EGL 环境搭建。EGL 详细介绍见 → 【OpenGL ES】EGL+FBO离屏渲染。
​    elg_surface_view.h
  1. #include
  2. #include "glcore/application.h"
  3. #include "glcore/elg_surface_view.h"
  4. #include "glcore/gl_inspector.h"
  5. #define LOG_TAG "Native: EGLSurfaceView"
  6. #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
  7. namespace glcore
  8. {
  9. EGLSurfaceView::EGLSurfaceView()
  10. {
  11.     LOGI("init");
  12.     createDisplay();
  13.     createConfig();
  14.     createContext();
  15. }
  16. EGLSurfaceView::~EGLSurfaceView()
  17. {
  18.     LOGI("destroy");
  19.     if (m_renderer)
  20.     {
  21.         delete m_renderer;
  22.         m_renderer = nullptr;
  23.     }
  24.     if (m_eglDisplay && m_eglDisplay != EGL_NO_DISPLAY)
  25.     {
  26.         // 与显示设备解绑
  27.         EGL_CALL(eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
  28.         // 销毁 EGLSurface
  29.         if (m_eglSurface && m_eglSurface != EGL_NO_SURFACE)
  30.         {
  31.             EGL_CALL(eglDestroySurface(m_eglDisplay, m_eglSurface));
  32.             delete &m_eglSurface;
  33.         }
  34.         // 销毁 EGLContext
  35.         if (m_eglContext && m_eglContext != EGL_NO_CONTEXT)
  36.         {
  37.             EGL_CALL(eglDestroyContext(m_eglDisplay, m_eglContext));
  38.             delete &m_eglContext;
  39.         }
  40.         // 销毁 EGLDisplay (显示设备)
  41.         EGL_CALL(eglTerminate(m_eglDisplay));
  42.         delete &m_eglDisplay;
  43.     }
  44.     delete app;
  45. }
  46. void EGLSurfaceView::setRenderer(Renderer *renderer)
  47. {
  48.     LOGI("setRenderer");
  49.     m_renderer = renderer;
  50. }
  51. bool EGLSurfaceView::surfaceCreated()
  52. {
  53.     LOGI("createSurface");
  54.     createSurface();
  55.     makeCurrent();
  56.     if (m_renderer && m_firstCreateSurface)
  57.     {
  58.         m_renderer->onSurfaceCreated();
  59.         m_firstCreateSurface = false;
  60.     }
  61.     return true;
  62. }
  63. void EGLSurfaceView::surfaceChanged(int width, int height)
  64. {
  65.     LOGI("surfaceChanged, width: %d, height: %d", width, height);
  66.     app->resize(width, height);
  67.     if (m_renderer)
  68.     {
  69.         m_renderer->onSurfaceChanged(width, height);
  70.     }
  71. }
  72. void EGLSurfaceView::drawFrame()
  73. {
  74.     if (!m_eglSurface || m_eglSurface == EGL_NO_SURFACE || !m_renderer)
  75.     {
  76.         return;
  77.     }
  78.     m_renderer->onDrawFrame();
  79.     EGL_CALL(eglSwapBuffers(m_eglDisplay, m_eglSurface));
  80. }
  81. void EGLSurfaceView::surfaceDestroy()
  82. {
  83.     LOGI("surfaceDestroy");
  84.     if (m_eglDisplay && m_eglDisplay != EGL_NO_DISPLAY)
  85.     {
  86.         // 与显示设备解绑
  87.         EGL_CALL(eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
  88.         // 销毁 EGLSurface
  89.         if (m_eglSurface && m_eglSurface != EGL_NO_SURFACE)
  90.         {
  91.             EGL_CALL(eglDestroySurface(m_eglDisplay, m_eglSurface));
  92.             m_eglSurface = nullptr;
  93.         }
  94.     }
  95.     app->releaseWindow();
  96. }
  97. // 1.创建EGLDisplay
  98. void EGLSurfaceView::createDisplay()
  99. {
  100.     EGL_CALL(m_eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY));
  101.     EGL_CALL(eglInitialize(m_eglDisplay, nullptr, nullptr));
  102. }
  103. // 2.创建EGLConfig
  104. void EGLSurfaceView::createConfig()
  105. {
  106.     if (m_eglDisplay && m_eglDisplay != EGL_NO_DISPLAY)
  107.     {
  108.         const EGLint configAttrs[] = {
  109.                 EGL_RED_SIZE, 8,
  110.                 EGL_GREEN_SIZE, 8,
  111.                 EGL_BLUE_SIZE, 8,
  112.                 EGL_ALPHA_SIZE, 8,
  113.                 EGL_DEPTH_SIZE, 8,
  114.                 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT,
  115.                 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
  116.                 EGL_NONE
  117.         };
  118.         EGLint numConfigs;
  119.         EGL_CALL(eglChooseConfig(m_eglDisplay, configAttrs, &m_eglConfig, 1, &numConfigs));
  120.     }
  121. }
  122. // 3.创建EGLContext
  123. void EGLSurfaceView::createContext()
  124. {
  125.     if (m_eglConfig)
  126.     {
  127.         const EGLint contextAttrs[] = {
  128.                 EGL_CONTEXT_CLIENT_VERSION, 3,
  129.                 EGL_NONE
  130.         };
  131.         EGL_CALL(m_eglContext = eglCreateContext(m_eglDisplay, m_eglConfig, EGL_NO_CONTEXT, contextAttrs));
  132.     }
  133. }
  134. // 4.创建EGLSurface
  135. void EGLSurfaceView::createSurface()
  136. {
  137.     if (m_eglContext && m_eglContext != EGL_NO_CONTEXT)
  138.     {
  139.         EGL_CALL(m_eglSurface = eglCreateWindowSurface(m_eglDisplay, m_eglConfig, app->getWindow(), nullptr));
  140.     }
  141. }
  142. // 5.绑定EGLSurface和EGLContext到显示设备(EGLDisplay)
  143. void EGLSurfaceView::makeCurrent()
  144. {
  145.     if (m_eglSurface && m_eglSurface != EGL_NO_SURFACE)
  146.     {
  147.         EGL_CALL(eglMakeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext));
  148.     }
  149. }
  150. } // namespace glcore
复制代码
​    elg_surface_view.cpp
  1. #include
  2. #include "glcore/application.h"
  3. #include "glcore/elg_surface_view.h"
  4. #include "glcore/gl_inspector.h"
  5. #define LOG_TAG "Native: EGLSurfaceView"
  6. #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
  7. namespace glcore
  8. {
  9. EGLSurfaceView::EGLSurfaceView()
  10. {
  11.     LOGI("init");
  12.     createDisplay();
  13.     createConfig();
  14.     createContext();
  15. }
  16. EGLSurfaceView::~EGLSurfaceView()
  17. {
  18.     LOGI("destroy");
  19.     if (m_renderer)
  20.     {
  21.         delete m_renderer;
  22.         m_renderer = nullptr;
  23.     }
  24.     if (m_eglDisplay && m_eglDisplay != EGL_NO_DISPLAY)
  25.     {
  26.         // 与显示设备解绑
  27.         EGL_CALL(eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
  28.         // 销毁 EGLSurface
  29.         if (m_eglSurface && m_eglSurface != EGL_NO_SURFACE)
  30.         {
  31.             EGL_CALL(eglDestroySurface(m_eglDisplay, m_eglSurface));
  32.             //GLInspector::checkEGLConfig("eglDestroySurface");
  33.             delete &m_eglSurface;
  34.         }
  35.         // 销毁 EGLContext
  36.         if (m_eglContext && m_eglContext != EGL_NO_CONTEXT)
  37.         {
  38.             EGL_CALL(eglDestroyContext(m_eglDisplay, m_eglContext));
  39.             //GLInspector::checkEGLConfig("eglDestroyContext");
  40.             delete &m_eglContext;
  41.         }
  42.         // 销毁 EGLDisplay (显示设备)
  43.         EGL_CALL(eglTerminate(m_eglDisplay));
  44.         //GLInspector::checkEGLConfig("eglTerminate");
  45.         delete &m_eglDisplay;
  46.     }
  47.     delete app;
  48. }
  49. void EGLSurfaceView::setRenderer(Renderer *renderer)
  50. {
  51.     LOGI("setRenderer");
  52.     m_renderer = renderer;
  53. }
  54. bool EGLSurfaceView::surfaceCreated()
  55. {
  56.     LOGI("createSurface");
  57.     createSurface();
  58.     makeCurrent();
  59.     if (m_renderer && m_firstCreateSurface)
  60.     {
  61.         m_renderer->onSurfaceCreated();
  62.         m_firstCreateSurface = false;
  63.     }
  64.     return true;
  65. }
  66. void EGLSurfaceView::surfaceChanged(int width, int height)
  67. {
  68.     LOGI("surfaceChanged, width: %d, height: %d", width, height);
  69.     app->resize(width, height);
  70.     if (m_renderer)
  71.     {
  72.         m_renderer->onSurfaceChanged(width, height);
  73.     }
  74. }
  75. void EGLSurfaceView::drawFrame()
  76. {
  77.     if (!m_eglSurface || m_eglSurface == EGL_NO_SURFACE || !m_renderer)
  78.     {
  79.         return;
  80.     }
  81.     m_renderer->onDrawFrame();
  82.     EGL_CALL(eglSwapBuffers(m_eglDisplay, m_eglSurface));
  83.     //GLInspector::checkEGLConfig("eglSwapBuffers");
  84. }
  85. void EGLSurfaceView::surfaceDestroy()
  86. {
  87.     LOGI("surfaceDestroy");
  88.     if (m_eglDisplay && m_eglDisplay != EGL_NO_DISPLAY)
  89.     {
  90.         // 与显示设备解绑
  91.         EGL_CALL(eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
  92.         // 销毁 EGLSurface
  93.         if (m_eglSurface && m_eglSurface != EGL_NO_SURFACE)
  94.         {
  95.             EGL_CALL(eglDestroySurface(m_eglDisplay, m_eglSurface));
  96.             //GLInspector::checkEGLConfig("eglDestroySurface");
  97.             m_eglSurface = nullptr;
  98.         }
  99.     }
  100.     app->releaseWindow();
  101. }
  102. // 1.创建EGLDisplay
  103. void EGLSurfaceView::createDisplay()
  104. {
  105.     EGL_CALL(m_eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY));
  106.     EGL_CALL(eglInitialize(m_eglDisplay, nullptr, nullptr));
  107.     //GLInspector::checkEGLConfig("eglInitialize");
  108. }
  109. // 2.创建EGLConfig
  110. void EGLSurfaceView::createConfig()
  111. {
  112.     if (m_eglDisplay && m_eglDisplay != EGL_NO_DISPLAY)
  113.     {
  114.         const EGLint configAttrs[] = {
  115.                 EGL_RED_SIZE, 8,
  116.                 EGL_GREEN_SIZE, 8,
  117.                 EGL_BLUE_SIZE, 8,
  118.                 EGL_ALPHA_SIZE, 8,
  119.                 EGL_DEPTH_SIZE, 8,
  120.                 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT,
  121.                 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
  122.                 EGL_NONE
  123.         };
  124.         EGLint numConfigs;
  125.         EGL_CALL(eglChooseConfig(m_eglDisplay, configAttrs, &m_eglConfig, 1, &numConfigs));
  126.         //GLInspector::checkEGLConfig("eglChooseConfig");
  127.     }
  128. }
  129. // 3.创建EGLContext
  130. void EGLSurfaceView::createContext()
  131. {
  132.     if (m_eglConfig)
  133.     {
  134.         const EGLint contextAttrs[] = {
  135.                 EGL_CONTEXT_CLIENT_VERSION, 3,
  136.                 EGL_NONE
  137.         };
  138.         EGL_CALL(m_eglContext = eglCreateContext(m_eglDisplay, m_eglConfig, EGL_NO_CONTEXT, contextAttrs));
  139.         //GLInspector::checkEGLConfig("eglCreateContext");
  140.     }
  141. }
  142. // 4.创建EGLSurface
  143. void EGLSurfaceView::createSurface()
  144. {
  145.     if (m_eglContext && m_eglContext != EGL_NO_CONTEXT)
  146.     {
  147.         EGL_CALL(m_eglSurface = eglCreateWindowSurface(m_eglDisplay, m_eglConfig, app->getWindow(), nullptr));
  148.         //GLInspector::checkEGLConfig("eglCreateWindowSurface");
  149.     }
  150. }
  151. // 5.绑定EGLSurface和EGLContext到显示设备(EGLDisplay)
  152. void EGLSurfaceView::makeCurrent()
  153. {
  154.     if (m_eglSurface && m_eglSurface != EGL_NO_SURFACE)
  155.     {
  156.         EGL_CALL(eglMakeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext));
  157.         //GLInspector::checkEGLConfig("eglMakeCurrent");
  158.     }
  159. }
  160. } // namespace glcore
复制代码
2.7 ShaderProgram

​    ShaderProgram 主要用于编译 Shader、链接 Program、设置 attribute 属性、更新 uniform 属性。
​    glGetAttribLocation、glGetUniformLocation 两个接口需要 CPU 向 GPU 查询 location 信息,并且会频繁调用,为提高性能,笔者设计了 m_attributes 和 m_uniforms 两个 map 存储 name 到 location 的映射,方便快速获取 location,避免 CPU 频繁与 GPU 交互,以提高渲染性能。
​    shader_program.h
  1. #pragma once
  2. #include <map>
  3. #include "core_lib.h"
  4. using namespace std;
  5. namespace glcore
  6. {
  7. /**
  8. * 着色器程序
  9. * @author little fat sheep
  10. */
  11. class ShaderProgram
  12. {
  13. public:
  14.     static constexpr char* ATTRIBUTE_POSITION = "a_position"; // 着色器中位置属性名
  15.     static constexpr char* ATTRIBUTE_NORMAL = "a_normal"; // 着色器中位法线性名
  16.     static constexpr char* ATTRIBUTE_COLOR = "a_color"; // 着色器中颜色属性名
  17.     static constexpr char* ATTRIBUTE_TEXCOORD = "a_texCoord"; // 着色器中纹理坐标属性名
  18.     static constexpr char* ATTRIBUTE_TANGENT = "a_tangent"; // 着色器中切线属性名
  19.     static constexpr char* ATTRIBUTE_BINORMAL = "a_binormal"; // 着色器中副切线属性名
  20.     static constexpr char* UNIFORM_TEXTURE = "u_texture"; // 着色器中纹理名
  21.     static constexpr char* UNIFORM_VP = "u_projectionViewMatrix"; // 着色器中VP名
  22. private:
  23.     GLuint m_program;
  24.     map<const char*, int> m_attributes;
  25.     map<const char*, int> m_uniforms;
  26. public:
  27.     ShaderProgram(const char* vertexCode, const char* fragmentCode);
  28.     ~ShaderProgram();
  29.     void bind();
  30.     GLuint getHandle() { return m_program; }
  31.     // 操作attribute属性
  32.     void enableVertexAttribArray(const char* name);
  33.     void enableVertexAttribArray(int location);
  34.     void setVertexAttribPointer(const char* name, int size, int type, bool normalize, int stride, int offset);
  35.     void setVertexAttribPointer(int location, int size, int type, bool normalize, int stride, int offset);
  36.     void disableVertexAttribArray(const char* name);
  37.     void disableVertexAttribArray(int location);
  38.     // 操作uniform属性
  39.     void setUniformi(const char* name, int value);
  40.     void setUniformi(int location, int value);
  41.     void setUniformi(const char* name, int value1, int value2);
  42.     void setUniformi(int location, int value1, int value2);
  43.     void setUniformi(const char* name, int value1, int value2, int value3);
  44.     void setUniformi(int location, int value1, int value2, int value3);
  45.     void setUniformi(const char* name, int value1, int value2, int value3, int value4);
  46.     void setUniformi(int location, int value1, int value2, int value3, int value4);
  47.     void setUniformf(const char* name, float value);
  48.     void setUniformf(int location, float value);
  49.     void setUniformf(const char* name, float value1, float value2);
  50.     void setUniformf(int location, float value1, float value2);
  51.     void setUniformf(const char* name, float value1, float value2, int value3);
  52.     void setUniformf(int location, float value1, float value2, int value3);
  53.     void setUniformf(const char* name, float value1, float value2, int value3, int value4);
  54.     void setUniformf(int location, float value1, float value2, int value3, int value4);
  55.     void setUniform1fv(const char* name, int length, const float values[]);
  56.     void setUniform1fv(int location, int count, float const values[]);
  57.     void setUniform2fv(const char* name, int count, const float values[]);
  58.     void setUniform2fv(int location, int count, const float values[]);
  59.     void setUniform3fv(const char* name, int count, const float values[]);
  60.     void setUniform3fv(int location, int count, const float values[]);
  61.     void setUniform4fv(const char* name, int count, const float values[]);
  62.     void setUniform4fv(int location, int count, const float values[]);
  63.     void setUniformMatrix2fv(const char* name, int count, bool transpose, const float *value);
  64.     void setUniformMatrix2fv(int location, int count, bool transpose, const float *value);
  65.     void setUniformMatrix3fv(const char* name, int count, bool transpose, const float *value);
  66.     void setUniformMatrix3fv(int location, int count, bool transpose, const float *value);
  67.     void setUniformMatrix4fv(const char* name, int count, bool transpose, const float *value);
  68.     void setUniformMatrix4fv(int location, int count, bool transpose, const float *value);
  69.     int fetchAttributeLocation(const char* name);
  70.     int fetchUniformLocation(const char* name);
  71. private:
  72.     void compileShaders(const char* vertexCode, const char* fragmentCode);
  73.     GLuint loadShader(GLenum type, const char* source);
  74.     GLuint linkProgram(GLuint vertexShader, GLuint fragmentShader);
  75. };
  76. } // namespace glcore
复制代码
​    shader_program.cpp
  1. #include
  2. #include "glcore/gl_inspector.h"
  3. #include "glcore/shader_program.h"
  4. #define LOG_TAG "Native: ShaderProgram"
  5. #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
  6. namespace glcore
  7. {
  8. ShaderProgram::ShaderProgram(const char* vertexCode, const char* fragmentCode)
  9. {
  10.     compileShaders(vertexCode, fragmentCode);
  11. }
  12. ShaderProgram::~ShaderProgram()
  13. {
  14.     if (m_program)
  15.     {
  16.         GL_CALL(glUseProgram(0));
  17.         GL_CALL(glDeleteProgram(m_program));
  18.         m_program = 0;
  19.     }
  20.     m_attributes.clear();
  21.     m_uniforms.clear();
  22. }
  23. void ShaderProgram::bind()
  24. {
  25.     GL_CALL(glUseProgram(m_program));
  26. }
  27. void ShaderProgram::enableVertexAttribArray(const char* name)
  28. {
  29.     int location = fetchAttributeLocation(name);
  30.     enableVertexAttribArray(location);
  31. }
  32. void ShaderProgram::enableVertexAttribArray(int location)
  33. {
  34.     GL_CALL(glEnableVertexAttribArray(location));
  35. }
  36. void ShaderProgram::setVertexAttribPointer(const char *name, int size, int type, bool normalize, int stride, int offset)
  37. {
  38.     int location = fetchAttributeLocation(name);
  39.     setVertexAttribPointer(location, size, type, normalize, stride, offset);
  40. }
  41. void ShaderProgram::setVertexAttribPointer(int location, int size, int type, bool normalize, int stride, int offset)
  42. {
  43.     GL_CALL(glVertexAttribPointer(location, size, type, normalize, stride, (void*) offset));
  44. }
  45. void ShaderProgram::disableVertexAttribArray(const char* name)
  46. {
  47.     int location = fetchAttributeLocation(name);
  48.     disableVertexAttribArray(location);
  49. }
  50. void ShaderProgram::disableVertexAttribArray(int location)
  51. {
  52.     GL_CALL(glDisableVertexAttribArray(location));
  53. }
  54. void ShaderProgram::setUniformi(const char* name, int value)
  55. {
  56.     int location = fetchUniformLocation(name);
  57.     GL_CALL(glUniform1i(location, value));
  58. }
  59. void ShaderProgram::setUniformi(int location, int value)
  60. {
  61.     GL_CALL(glUniform1i(location, value));
  62. }
  63. void ShaderProgram::setUniformi(const char* name, int value1, int value2)
  64. {
  65.     int location = fetchUniformLocation(name);
  66.     GL_CALL(glUniform2i(location, value1, value2));
  67. }
  68. void ShaderProgram::setUniformi(int location, int value1, int value2)
  69. {
  70.     GL_CALL(glUniform2i(location, value1, value2));
  71. }
  72. void ShaderProgram::setUniformi(const char* name, int value1, int value2, int value3)
  73. {
  74.     int location = fetchUniformLocation(name);
  75.     GL_CALL(glUniform3i(location, value1, value2, value3));
  76. }
  77. void ShaderProgram::setUniformi(int location, int value1, int value2, int value3)
  78. {
  79.     GL_CALL(glUniform3i(location, value1, value2, value3));
  80. }
  81. void ShaderProgram::setUniformi(const char* name, int value1, int value2, int value3, int value4)
  82. {
  83.     int location = fetchUniformLocation(name);
  84.     GL_CALL(glUniform4i(location, value1, value2, value3, value4));
  85. }
  86. void ShaderProgram::setUniformi(int location, int value1, int value2, int value3, int value4)
  87. {
  88.     GL_CALL(glUniform4i(location, value1, value2, value3, value4));
  89. }
  90. void ShaderProgram::setUniformf(const char* name, float value)
  91. {
  92.     int location = fetchUniformLocation(name);
  93.     GL_CALL(glUniform1f(location, value));
  94. }
  95. void ShaderProgram::setUniformf(int location, float value)
  96. {
  97.     GL_CALL(glUniform1f(location, value));
  98. }
  99. void ShaderProgram::setUniformf(const char* name, float value1, float value2)
  100. {
  101.     int location = fetchUniformLocation(name);
  102.     GL_CALL(glUniform2f(location, value1, value2));
  103. }
  104. void ShaderProgram::setUniformf(int location, float value1, float value2)
  105. {
  106.     GL_CALL(glUniform2f(location, value1, value2));
  107. }
  108. void ShaderProgram::setUniformf(const char* name, float value1, float value2, int value3)
  109. {
  110.     int location = fetchUniformLocation(name);
  111.     GL_CALL(glUniform3f(location, value1, value2, value3));
  112. }
  113. void ShaderProgram::setUniformf(int location, float value1, float value2, int value3)
  114. {
  115.     GL_CALL(glUniform3f(location, value1, value2, value3));
  116. }
  117. void ShaderProgram::setUniformf(const char* name, float value1, float value2, int value3, int value4)
  118. {
  119.     int location = fetchUniformLocation(name);
  120.     GL_CALL(glUniform4f(location, value1, value2, value3, value4));
  121. }
  122. void ShaderProgram::setUniformf(int location, float value1, float value2, int value3, int value4)
  123. {
  124.     GL_CALL(glUniform4f(location, value1, value2, value3, value4));
  125. }
  126. void ShaderProgram::setUniform1fv(const char* name, int count, const float values[])
  127. {
  128.     int location = fetchUniformLocation(name);
  129.     GL_CALL(glUniform1fv(location, count, values));
  130. }
  131. void ShaderProgram::setUniform1fv(int location, int count, const float values[])
  132. {
  133.     GL_CALL(glUniform1fv(location, count, values));
  134. }
  135. void ShaderProgram::setUniform2fv(const char* name, int count, const float values[])
  136. {
  137.     int location = fetchUniformLocation(name);
  138.     GL_CALL(glUniform2fv(location, count / 2, values));
  139. }
  140. void ShaderProgram::setUniform2fv(int location, int count, const float values[])
  141. {
  142.     GL_CALL(glUniform2fv(location, count / 2, values));
  143. }
  144. void ShaderProgram::setUniform3fv(const char* name, int count, const float values[])
  145. {
  146.     int location = fetchUniformLocation(name);
  147.     GL_CALL(glUniform3fv(location, count / 3, values));
  148. }
  149. void ShaderProgram::setUniform3fv(int location, int count, const float values[])
  150. {
  151.     GL_CALL(glUniform3fv(location, count / 3, values));
  152. }
  153. void ShaderProgram::setUniform4fv(const char* name, int count, const float values[])
  154. {
  155.     int location = fetchUniformLocation(name);
  156.     GL_CALL(glUniform4fv(location, count / 4, values));
  157. }
  158. void ShaderProgram::setUniform4fv(int location, int count, const float values[])
  159. {
  160.     GL_CALL(glUniform4fv(location, count / 4, values));
  161. }
  162. void ShaderProgram::setUniformMatrix2fv(const char* name, int count, bool transpose, const float *value)
  163. {
  164.     int location = fetchUniformLocation(name);
  165.     GL_CALL(glUniformMatrix2fv(location, count, transpose, value));
  166. }
  167. void ShaderProgram::setUniformMatrix2fv(int location, int count, bool transpose, const float *value)
  168. {
  169.     GL_CALL(glUniformMatrix2fv(location, count, transpose, value));
  170. }
  171. void ShaderProgram::setUniformMatrix3fv(const char* name, int count, bool transpose, const float *value)
  172. {
  173.     int location = fetchUniformLocation(name);
  174.     GL_CALL(glUniformMatrix3fv(location, count, transpose, value));
  175. }
  176. void ShaderProgram::setUniformMatrix3fv(int location, int count, bool transpose, const float *value)
  177. {
  178.     GL_CALL(glUniformMatrix3fv(location, count, transpose, value));
  179. }
  180. void ShaderProgram::setUniformMatrix4fv(const char* name, int count, bool transpose, const float *value)
  181. {
  182.     int location = fetchUniformLocation(name);
  183.     GL_CALL(glUniformMatrix4fv(location, count, transpose, value));
  184. }
  185. void ShaderProgram::setUniformMatrix4fv(int location, int count, bool transpose, const float *value)
  186. {
  187.     GL_CALL(glUniformMatrix4fv(location, count, transpose, value));
  188. }
  189. int ShaderProgram::fetchAttributeLocation(const char* name)
  190. {
  191.     int location;
  192.     auto it = m_attributes.find(name);
  193.     if (it == m_attributes.end())
  194.     {
  195.         GL_CALL(location = glGetAttribLocation(m_program, name));
  196.         if (location == -1) {
  197.             LOGI("no attribute: %s", name);
  198.             //GLInspector::printProgramInfoLog(m_program, "fetchAttributeLocation");
  199.             return -1;
  200.         }
  201.         m_attributes[name] = location;
  202.     }
  203.     else
  204.     {
  205.         location = it->second;
  206.     }
  207.     return location;
  208. }
  209. int ShaderProgram::fetchUniformLocation(const char* name)
  210. {
  211.     int location;
  212.     auto it = m_uniforms.find(name);
  213.     if (it == m_uniforms.end())
  214.     {
  215.         GL_CALL(location = glGetUniformLocation(m_program, name));
  216.         if (location == -1) {
  217.             LOGI("no uniform: %s", name);
  218.             //GLInspector::printProgramInfoLog(m_program, "fetchUniformLocation");
  219.             return -1;
  220.         }
  221.         m_uniforms[name] = location;
  222.     }
  223.     else
  224.     {
  225.         location = it->second;
  226.     }
  227.     return location;
  228. }
  229. void ShaderProgram::compileShaders(const char* vertexCode, const char* fragmentCode)
  230. {
  231.     GLuint vertexShader = loadShader(GL_VERTEX_SHADER, vertexCode);
  232.     GLuint fragmentShader = loadShader(GL_FRAGMENT_SHADER, fragmentCode);
  233.     m_program = linkProgram(vertexShader, fragmentShader);
  234. }
  235. GLuint ShaderProgram::loadShader(GLenum type, const char* source)
  236. {
  237.     GL_CALL(GLuint shader = glCreateShader(type));
  238.     GL_CALL(glShaderSource(shader, 1, &source, nullptr));
  239.     GL_CALL(glCompileShader(shader));
  240.     GLint success;
  241.     glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
  242.     if (!success) {
  243.         GLInspector::printShaderInfoLog(shader, "loadShader");
  244.         return 0;
  245.     }
  246.     return shader;
  247. }
  248. GLuint ShaderProgram::linkProgram(GLuint vertexShader, GLuint fragmentShader)
  249. {
  250.     GL_CALL(GLuint program = glCreateProgram());
  251.     GL_CALL(glAttachShader(program, vertexShader));
  252.     GL_CALL(glAttachShader(program, fragmentShader));
  253.     GL_CALL(glLinkProgram(program));
  254.     GLint success;
  255.     glGetProgramiv(program, GL_LINK_STATUS, &success);
  256.     if (!success) {
  257.         GLInspector::printProgramInfoLog(m_program, "linkProgram");
  258.     }
  259.     GL_CALL(glDeleteShader(vertexShader));
  260.     GL_CALL(glDeleteShader(fragmentShader));
  261.     return program;
  262. }
  263. } // namespace glcore
复制代码
2.8 VBO

​    VBO 是 Vertex Buffer Object 的简称,即顶点缓冲对象,作用是缓存顶点数据到显存中,避免频繁调用 glVertexAttribPointer 传输顶点数据,减少 CPU 到 GPU 的数据传输,提高渲染效率。
​    顶点属性主要有位置、颜色、纹理坐标、法线、切线、副切线等,每个属性又有属性标识、维数、是否已标准化、数据类型、偏移、别名、纹理单元等。
​    由于 VBO 中有多个属性数据,每个属性有多个字段,笔者除了封装 VertexBufferObject 类,还封装了 VertexAttributes 和 VertexAttribute 两个类。VertexAttribute 是属性描述类,VertexAttributes 是属性描述集合。
​    vertex_buffer_object.h
  1. #pragma once
  2. #include <initializer_list>
  3. #include <vector>
  4. #include "core_lib.h"
  5. #include "shader_program.h"
  6. #include "vertex_attributes.h"
  7. #include "vertex_attribute.h"
  8. #include "vertex_attributes.h"
  9. using namespace std;
  10. namespace glcore
  11. {
  12. /**
  13. * 顶点属性缓冲对象 (简称VBO)
  14. * @author little fat sheep
  15. */
  16. class VertexBufferObject
  17. {
  18. protected:
  19.     bool m_isBound = false; // 是否已绑定到VBO (或VAO)
  20.     bool m_isDirty = false; // 是否有脏数据 (缓存的数据需要更新)
  21. private:
  22.     GLuint m_vboHandle; // VBO句柄
  23.     VertexAttributes* m_attributes; // 顶点属性
  24.     GLuint m_usage; // GL_STATIC_DRAW 或 GL_DYNAMIC_DRAW
  25.     const float* m_vertices; // 顶点属性数据
  26.     int m_vertexNum = 0; // 顶点个数
  27.     int m_bytes = 0; // 顶点属性字节数
  28. public:
  29.     VertexBufferObject(bool isStatic, initializer_list<VertexAttribute*> attributes);
  30.     VertexBufferObject(bool isStatic, VertexAttributes* attributes);
  31.     virtual ~VertexBufferObject();
  32.     void setVertices(float* vertices, int bytes);
  33.     void bind(ShaderProgram* shader);
  34.     virtual void bind(ShaderProgram* shader, int* locations);
  35.     void unbind(ShaderProgram* shader);
  36.     virtual void unbind(ShaderProgram* shader, int* locations);
  37.     int getNumVertices() { return m_vertexNum; }
  38. private:
  39.     void applyBufferData(); // 缓存数据
  40. };
  41. } // namespace glcore
复制代码
​    vertex_buffer_object.cpp
  1. #include
  2. #include "glcore/gl_inspector.h"
  3. #include "glcore/vertex_buffer_object.h"
  4. #define LOG_TAG "Native: VertexBufferObject"
  5. #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
  6. namespace glcore
  7. {
  8. VertexBufferObject::VertexBufferObject(bool isStatic, initializer_list<VertexAttribute*> attributes):
  9.     VertexBufferObject(isStatic, new VertexAttributes(attributes))
  10. {
  11. }
  12. VertexBufferObject::VertexBufferObject(bool isStatic, VertexAttributes* attributes):
  13.     m_attributes(attributes)
  14. {
  15.     m_usage = isStatic ? GL_STATIC_DRAW : GL_DYNAMIC_DRAW;
  16.     GL_CALL(glGenBuffers(1, &m_vboHandle));
  17.     LOGI("init: %d", m_vboHandle);
  18. }
  19. VertexBufferObject::~VertexBufferObject()
  20. {
  21.     LOGI("destroy");
  22.     GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, 0));
  23.     GL_CALL(glDeleteBuffers(1, &m_vboHandle));
  24.     m_vboHandle = 0;
  25.     delete m_attributes;
  26.     delete[] m_vertices;
  27. }
  28. void VertexBufferObject::setVertices(float* vertices, int bytes)
  29. {
  30.     m_vertices = vertices;
  31.     m_vertexNum = bytes / m_attributes->vertexSize;
  32.     m_bytes = bytes;
  33.     m_isDirty = true;
  34.     if (m_isBound)
  35.     {
  36.         applyBufferData();
  37.     }
  38. }
  39. void VertexBufferObject::bind(ShaderProgram* shader)
  40. {
  41.     bind(shader, nullptr);
  42. }
  43. void VertexBufferObject::bind(ShaderProgram* shader, int* locations)
  44. {
  45.     GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, m_vboHandle));
  46.     if (m_isDirty)
  47.     {
  48.         applyBufferData();
  49.     }
  50.     if (locations == nullptr)
  51.     {
  52.         for (int i = 0; i < m_attributes->size(); i++)
  53.         {
  54.             VertexAttribute* attribute = m_attributes->get(i);
  55.             shader->enableVertexAttribArray(attribute->alias);
  56.             shader->setVertexAttribPointer(attribute->alias, attribute->numComponents,
  57.                attribute->type, attribute->normalized, m_attributes->vertexSize,
  58.                attribute->offset);
  59.         }
  60.     }
  61.     else
  62.     {
  63.         for (int i = 0; i < m_attributes->size(); i++)
  64.         {
  65.             VertexAttribute* attribute = m_attributes->get(i);
  66.             shader->enableVertexAttribArray(locations[i]);
  67.             shader->setVertexAttribPointer(locations[i], attribute->numComponents,
  68.                attribute->type, attribute->normalized, m_attributes->vertexSize,
  69.                attribute->offset);
  70.         }
  71.     }
  72.     m_isBound = true;
  73. }
  74. void VertexBufferObject::unbind(ShaderProgram* shader)
  75. {
  76.     unbind(shader, nullptr);
  77. }
  78. void VertexBufferObject::unbind(ShaderProgram* shader, int* locations)
  79. {
  80.     if (locations == nullptr)
  81.     {
  82.         for (int i = 0; i < m_attributes->size(); i++)
  83.         {
  84.             shader->disableVertexAttribArray(m_attributes->get(i)->alias);
  85.         }
  86.     }
  87.     else
  88.     {
  89.         for (int i = 0; i < m_attributes->size(); i++)
  90.         {
  91.             shader->disableVertexAttribArray(locations[i]);
  92.         }
  93.     }
  94.     m_isBound = false;
  95. }
  96. void VertexBufferObject::applyBufferData()
  97. {
  98.     GL_CALL(glBufferData(GL_ARRAY_BUFFER, m_bytes, m_vertices, m_usage));
  99.     //GLInspector::checkGLError("vbo: applyBufferData");
  100.     m_isDirty = false;
  101. }
  102. } // namespace glcore
复制代码
​    vertex_attributes.h
  1. #pragma once
  2. #include <initializer_list>
  3. #include <vector>
  4. #include "vertex_attribute.h"
  5. using namespace std;
  6. namespace glcore
  7. {
  8. /**
  9. * 顶点属性集(位置、颜色、纹理坐标、法线、切线、副切线等中的一部分)
  10. * 每个顶点属性可以看作一个通道, 这个通道可能是多维的, 每个维度可能是多字节的
  11. * @author little fat sheep
  12. */
  13. class VertexAttributes
  14. {
  15. public:
  16.     int vertexSize; // 所有顶点属性的字节数
  17. private:
  18.     vector<VertexAttribute*> m_attributes; // 顶点属性列表
  19. public:
  20.     VertexAttributes(initializer_list<VertexAttribute*> attributes);
  21.     ~VertexAttributes();
  22.     VertexAttribute* get(int index); // 根据索引获取属性
  23.     int size(); // 获取属性个数
  24. private:
  25.     int calculateOffsets(); // 计算偏移
  26. };
  27. /**
  28. * 顶点属性标识
  29. * @author little fat sheep
  30. */
  31. class Usage {
  32. public:
  33.     static const int Position = 1;
  34.     static const int ColorUnpacked = 2;
  35.     static const int ColorPacked = 4;
  36.     static const int Normal = 8;
  37.     static const int TextureCoordinates = 16;
  38.     static const int Tangent = 32;
  39.     static const int BiNormal = 64;
  40. };
  41. } // namespace glcore
复制代码
​    vertex_attributes.cpp
  1. #include "glcore/vertex_attributes.h"
  2. namespace glcore
  3. {
  4. VertexAttributes::VertexAttributes(initializer_list<VertexAttribute*> attributes):
  5.         m_attributes(attributes)
  6. {
  7.     vertexSize = calculateOffsets();
  8. }
  9. VertexAttributes::~VertexAttributes()
  10. {
  11.     m_attributes.clear();
  12. }
  13. VertexAttribute* VertexAttributes::get(int index)
  14. {
  15.     if (index >= 0 && index < m_attributes.size())
  16.     {
  17.         return m_attributes[index];
  18.     }
  19.     return nullptr;
  20. }
  21. int VertexAttributes::size()
  22. {
  23.     return m_attributes.size();
  24. }
  25. int VertexAttributes::calculateOffsets() {
  26.     int count = 0;
  27.     for (VertexAttribute* attribute : m_attributes) {
  28.         attribute->offset = count;
  29.         count += attribute->getSizeInBytes();
  30.     }
  31.     return count;
  32. }
  33. } // namespace glcore
复制代码
​    vertex_attribute.h
  1. #pragma once
  2. namespace glcore
  3. {
  4. /**
  5. * 单个顶点属性(位置、颜色、纹理坐标、法线、切线、副切线等中的一个)
  6. * 每个顶点属性可以看作一个通道, 这个通道可能是多维的, 每个维度可能是多字节的
  7. * @author little fat sheep
  8. */
  9. class VertexAttribute
  10. {
  11. public:
  12.     int usage; // 顶点属性标识
  13.     int numComponents; // 顶点属性维数 (如顶点坐标属性是3维的, 纹理坐标是2维的)
  14.     bool normalized; // 顶点属性是否已经标准化 (有符号: -1~1, 无符号: 0~1)
  15.     int type; // 顶点属性的变量类型 (GL_FLOAT、GL_UNSIGNED_BYTE等)
  16.     int offset; // 顶点属性在字节上的偏移
  17.     const char* alias; // 顶点属性别名 (着色器中变量名)
  18.     int unit; // 纹理单元 (可能有多个纹理, 可选)
  19. public:
  20.     VertexAttribute(int usage, int numComponents, const char* alias);
  21.     VertexAttribute(int usage, int numComponents, const char* alias, int unit);
  22.     VertexAttribute(int usage, int numComponents, int type, bool normalized, const char* alias);
  23.     VertexAttribute(int usage, int numComponents, int type, bool normalized, const char* alias, int unit);
  24.     ~VertexAttribute();
  25.     static VertexAttribute* Position(); // 位置参数信息
  26.     static VertexAttribute* TexCoords(int unit); // 纹理坐标参数信息
  27.     static VertexAttribute* Normal(); // 法线参数信息
  28.     static VertexAttribute* ColorPacked(); // 颜色参数信息
  29.     static VertexAttribute* ColorUnpacked(); // 颜色参数信息
  30.     static VertexAttribute* Tangent(); // 切线参数信息
  31.     static VertexAttribute* Binormal(); // 副切线参数信息
  32.     int getSizeInBytes(); // 属性对应的字节数
  33. private:
  34.     void create(int usage, int numComponents, int type, bool normalized, const char* alias, int unit);
  35. };
  36. } // namespace glcore
复制代码
​    vertex_attribute.cpp
  1. #include
  2. #include <string>
  3. #include "glcore/shader_program.h"
  4. #include "glcore/vertex_attribute.h"
  5. #include "glcore/vertex_attributes.h"
  6. #define LOG_TAG "Native: VertexAttribute"
  7. #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
  8. using namespace std;
  9. namespace glcore
  10. {
  11. VertexAttribute::VertexAttribute(int usage, int numComponents, const char* alias):
  12.         VertexAttribute(usage, numComponents, alias, 0)
  13. {
  14. }
  15. VertexAttribute::VertexAttribute(int usage, int numComponents, const char* alias, int unit)
  16. {
  17.     int type = usage == Usage::ColorPacked ? GL_UNSIGNED_BYTE : GL_FLOAT;
  18.     bool normalized = usage == Usage::ColorPacked;
  19.     create(usage, numComponents, type, normalized, alias, unit);
  20. }
  21. VertexAttribute::VertexAttribute(int usage, int numComponents, int type, bool normalized, const char* alias)
  22. {
  23.     create(usage, numComponents, type, normalized, alias, 0);
  24. }
  25. VertexAttribute::VertexAttribute(int usage, int numComponents, int type, bool normalized, const char* alias, int unit)
  26. {
  27.     create(usage, numComponents, type, normalized, alias, unit);
  28. }
  29. VertexAttribute::~VertexAttribute()
  30. {
  31.     free((void*)alias);
  32. }
  33. VertexAttribute* VertexAttribute::Position() {
  34.     return new VertexAttribute(Usage::Position, 3, ShaderProgram::ATTRIBUTE_POSITION);
  35. }
  36. VertexAttribute* VertexAttribute::TexCoords(int unit) {
  37.     string str = string(ShaderProgram::ATTRIBUTE_TEXCOORD) + to_string(unit);
  38.     // 复制字符串, 避免str被回收导致悬垂指针问题, 通过free((void*)alias)释放内存
  39.     const char* combined = strdup(str.c_str());
  40.     return new VertexAttribute(Usage::TextureCoordinates, 2, combined, unit);
  41. }
  42. VertexAttribute* VertexAttribute::Normal() {
  43.     return new VertexAttribute(Usage::Normal, 3, ShaderProgram::ATTRIBUTE_NORMAL);
  44. }
  45. VertexAttribute* VertexAttribute::ColorPacked() {
  46.     return new VertexAttribute(Usage::ColorPacked, 4, GL_UNSIGNED_BYTE, true, ShaderProgram::ATTRIBUTE_COLOR);
  47. }
  48. VertexAttribute* VertexAttribute::ColorUnpacked() {
  49.     return new VertexAttribute(Usage::ColorUnpacked, 4, GL_FLOAT, false, ShaderProgram::ATTRIBUTE_COLOR);
  50. }
  51. VertexAttribute* VertexAttribute::Tangent() {
  52.     return new VertexAttribute(Usage::Tangent, 3, ShaderProgram::ATTRIBUTE_TANGENT);
  53. }
  54. VertexAttribute* VertexAttribute::Binormal() {
  55.     return new VertexAttribute(Usage::BiNormal, 3, ShaderProgram::ATTRIBUTE_BINORMAL);
  56. }
  57. int VertexAttribute::getSizeInBytes()
  58. {
  59.     switch (type) {
  60.         case GL_FLOAT:
  61.         case GL_FIXED:
  62.             return 4 * numComponents;
  63.         case GL_UNSIGNED_BYTE:
  64.         case GL_BYTE:
  65.             return numComponents;
  66.         case GL_UNSIGNED_SHORT:
  67.         case GL_SHORT:
  68.             return 2 * numComponents;
  69.     }
  70.     return 0;
  71. }
  72. void VertexAttribute::create(int usage, int numComponents, int type, bool normalized, const char* alias, int unit)
  73. {
  74.     this->usage = usage;
  75.     this->numComponents = numComponents;
  76.     this->type = type;
  77.     this->normalized = normalized;
  78.     this->alias = alias;
  79.     this->unit = unit;
  80.     LOGI("create, alias: %s", alias);
  81. }
  82. } // namespace glcore
复制代码
2.9 VAO

​    VAO 是 Vertex Array Object 的简称,即顶点数组对象,作用是缓存顶点属性的指针和描述(或格式)信息,简化顶点属性设置的流程,避免频繁调用 glVertexAttribPointer 设置属性描述(或格式)信息,减少 CPU 与 GPU 的交互,提高渲染效率。
​    vertex_buffer_object_with_vao.h
  1. #pragma once
  2. #include <initializer_list>
  3. #include "core_lib.h"
  4. #include "vertex_buffer_object.h"
  5. namespace glcore
  6. {
  7. /**
  8. * 携带VAO的顶点属性缓冲对象
  9. * @author little fat sheep
  10. */
  11. class VertexBufferObjectWithVAO : public VertexBufferObject
  12. {
  13. private:
  14.     GLuint m_vaoHandle; // VAO句柄
  15. public:
  16.     VertexBufferObjectWithVAO(bool isStatic, initializer_list<VertexAttribute*> attributes);
  17.     VertexBufferObjectWithVAO(bool isStatic, VertexAttributes* attributes);
  18.     ~VertexBufferObjectWithVAO() override;
  19.     void bind(ShaderProgram* shader, int* locations) override;
  20.     void unbind(ShaderProgram* shader, int* locations) override;
  21. };
  22. } // namespace glcore
复制代码
​    vertex_buffer_object_with_vao.cpp
  1. #include
  2. #include "glcore/gl_inspector.h"
  3. #include "glcore/vertex_buffer_object_with_vao.h"
  4. #define LOG_TAG "Native: VertexBufferObjectWithVAO"
  5. #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
  6. namespace glcore
  7. {
  8. VertexBufferObjectWithVAO::VertexBufferObjectWithVAO(bool isStatic,
  9.     initializer_list<VertexAttribute*> attributes):
  10.         VertexBufferObjectWithVAO(isStatic, new VertexAttributes(attributes))
  11. {
  12. }
  13. VertexBufferObjectWithVAO::VertexBufferObjectWithVAO(bool isStatic, VertexAttributes* attributes):
  14.     VertexBufferObject(isStatic, attributes)
  15. {
  16.     GL_CALL(glGenVertexArrays(1, &m_vaoHandle));
  17.     LOGI("init: %d", m_vaoHandle);
  18. }
  19. VertexBufferObjectWithVAO::~VertexBufferObjectWithVAO()
  20. {
  21.     LOGI("destroy");
  22.     GL_CALL(glDeleteVertexArrays(1, &m_vaoHandle));
  23. }
  24. void VertexBufferObjectWithVAO::bind(ShaderProgram* shader, int* locations)
  25. {
  26.     GL_CALL(glBindVertexArray(m_vaoHandle));
  27.     if (m_isDirty)
  28.     {
  29.         VertexBufferObject::bind(shader, locations);
  30.     }
  31.     m_isBound = true;
  32. }
  33. void VertexBufferObjectWithVAO::unbind(ShaderProgram* shader, int* locations)
  34. {
  35.     GL_CALL(glBindVertexArray(0));
  36.     m_isBound = false;
  37. }
  38. } // namespace glcore
复制代码
2.10 IBO

​    IBO 是 Index Buffer Object 的简称,即索引缓冲对象,作用是缓存顶点索引到显存中,避免频繁调用 glDrawElements 传输顶点索引,减少 CPU 到 GPU 的数据传输,提高渲染效率。由于 IBO 绑定的是 OpenGL ES 状态机的 GL_ELEMENT_ARRAY_BUFFER “插槽”,并且对应的绘制指令又是 glDrawElements (都有 Element),因此 IBO 也被称为 EBO。
​    index_buffer_object.h
  1. #pragma once
  2. #include "core_lib.h"
  3. namespace glcore
  4. {
  5. /**
  6. * 顶点索引缓冲对象 (简称IBO)
  7. * @author little fat sheep
  8. */
  9. class IndexBufferObject
  10. {
  11. private:
  12.     GLuint m_iboHandle; // IBO句柄
  13.     GLuint m_usage; // GL_STATIC_DRAW 或 GL_DYNAMIC_DRAW
  14.     GLenum m_type = GL_UNSIGNED_SHORT; // 索引数据类型 (GL_UNSIGNED_SHORT 或 GL_UNSIGNED_INT)
  15.     const void* m_indices; // 顶点索引数据(short*或int*类型)
  16.     int m_indexNum = 0; // 索引个数
  17.     int m_bytes = 0; // 顶点索引字节数
  18.     bool m_isDirty = false; // 是否有脏数据 (缓存的数据需要更新)
  19.     bool m_isBound = false; // 是否已绑定到IBO
  20. public:
  21.     IndexBufferObject(bool isStatic);
  22.     IndexBufferObject(bool isStatic, GLenum type);
  23.     ~IndexBufferObject();
  24.     void setIndices (void* indices, int bytes);
  25.     void setIndices (void* indices, int bytes, GLenum type);
  26.     void bind();
  27.     void unbind();
  28.     int getNumIndices() { return m_indexNum; }
  29.     GLenum getType() { return m_type; }
  30. private:
  31.     void applyBufferData(); // 缓存数据
  32.     int getTypeSize(); // 获取type对应的字节数
  33. };
  34. } // namespace glcore
复制代码
​    index_buffer_object.cpp
  1. #include
  2. #include "glcore/gl_inspector.h"
  3. #include "glcore/index_buffer_object.h"
  4. #define LOG_TAG "Native: IndexBufferObject"
  5. #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
  6. namespace glcore
  7. {
  8. IndexBufferObject::IndexBufferObject(bool isStatic):
  9.         IndexBufferObject(isStatic, GL_UNSIGNED_SHORT)
  10. {
  11. }
  12. IndexBufferObject::IndexBufferObject(bool isStatic, GLenum type)
  13. {
  14.     m_usage = isStatic ? GL_STATIC_DRAW : GL_DYNAMIC_DRAW;
  15.     m_type = type;
  16.     GL_CALL(glGenBuffers(1, &m_iboHandle));
  17.     LOGI("init: %d", m_iboHandle);
  18. }
  19. IndexBufferObject::~IndexBufferObject()
  20. {
  21.     LOGI("destroy");
  22.     GL_CALL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
  23.     GL_CALL(glDeleteBuffers(1, &m_iboHandle));
  24.     m_iboHandle = 0;
  25.     delete[] m_indices;
  26. }
  27. void IndexBufferObject::setIndices(void* indices, int bytes)
  28. {
  29.     setIndices(indices, bytes, m_type);
  30. }
  31. void IndexBufferObject::setIndices(void* indices, int bytes, GLenum type)
  32. {
  33.     m_indices = indices;
  34.     m_type = type;
  35.     m_indexNum = bytes > 0 ? bytes / getTypeSize() : 0;
  36.     m_bytes = bytes;
  37.     m_isDirty = true;
  38.     if (m_isBound)
  39.     {
  40.         applyBufferData();
  41.     }
  42. }
  43. void IndexBufferObject::bind()
  44. {
  45.     GL_CALL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_iboHandle));
  46.     if (m_isDirty)
  47.     {
  48.         applyBufferData();
  49.     }
  50.     m_isBound = true;
  51. }
  52. void IndexBufferObject::unbind()
  53. {
  54.     GL_CALL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
  55.     m_isBound = false;
  56. }
  57. void IndexBufferObject::applyBufferData()
  58. {
  59.     GL_CALL(glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_bytes, m_indices, m_usage));
  60.     //GLInspector::checkGLError("ibo: applyBufferData");
  61.     m_isDirty = false;
  62. }
  63. int IndexBufferObject::getTypeSize() {
  64.     switch (m_type) {
  65.         case GL_UNSIGNED_SHORT:
  66.             return 2;
  67.         case GL_UNSIGNED_INT:
  68.             return 4;
  69.     }
  70.     return 2;
  71. }
  72. } // namespace glcore
复制代码
2.11 Mesh

​    Mesh 是网格类,用于管理顶点数据、索引、描述(或格式)等信息,由于 VBO 管理了顶点数据、IBO 管理了顶点索引、VAO 管理了顶点描述(或格式),因此 Mesh 只需管理 VBO、IBO、VAO。另外 IBO 和 VAO 是可选的,Mesh 中需要根据用户的行为调整渲染指令。
​    为方便用户快速创建平面网格,笔者提供了 MeshUtils 类,用户也可以根据该类提供的模板创建自己的网格。
​    mesh.h
  1. #pragma once
  2. #include <initializer_list>
  3. #include "core_lib.h"
  4. #include "index_buffer_object.h"
  5. #include "shader_program.h"
  6. #include "vertex_buffer_object.h"
  7. #include "vertex_attribute.h"
  8. #include "vertex_attributes.h"
  9. using namespace std;
  10. namespace glcore
  11. {
  12. /**
  13. * 网格
  14. * @author little fat sheep
  15. */
  16. class Mesh
  17. {
  18. private:
  19.     VertexBufferObject* m_vbo; // 顶点属性缓冲对象
  20.     IndexBufferObject* m_ibo; // 顶点索引缓冲对象
  21.     GLenum m_mode = GL_TRIANGLES; // 渲染模式 (GL_TRIANGLES、GL_TRIANGLE_STRIP、GL_TRIANGLE_FAN等)
  22. public:
  23.     Mesh(bool isStatic, initializer_list<VertexAttribute*> attributes);
  24.     Mesh(bool isStatic, VertexAttributes* attributes);
  25.     Mesh(bool useVao, bool isStatic, initializer_list<VertexAttribute*> attributes);
  26.     Mesh(bool useVao, bool isStatic, VertexAttributes* attributes);
  27.     ~Mesh();
  28.     void setVertices(float* vertices, int bytes); // 设置顶点属性
  29.     void setIndices(void* indices, int bytes); // 设置顶点索引
  30.     void setIndices(void* indices, int bytes, GLenum type); // 设置顶点索引
  31.     void setMode(GLenum mode); // 设置渲染模式
  32.     void render(ShaderProgram* shader); // 渲染
  33. };
  34. } // namespace glcore
复制代码
​    mesh.cpp
  1. #include "glcore/gl_inspector.h"
  2. #include "glcore/mesh.h"
  3. #include "glcore/vertex_buffer_object_with_vao.h"
  4. namespace glcore
  5. {
  6. Mesh::Mesh(bool isStatic, initializer_list<VertexAttribute*> attributes):
  7.     Mesh(true, isStatic, new VertexAttributes(attributes))
  8. {
  9. }
  10. Mesh::Mesh(bool isStatic, VertexAttributes* attributes):
  11.     Mesh(true, isStatic, attributes)
  12. {
  13. }
  14. Mesh::Mesh(bool useVao, bool isStatic, initializer_list<VertexAttribute*> attributes):
  15.     Mesh(useVao, isStatic, new VertexAttributes(attributes))
  16. {
  17. }
  18. Mesh::Mesh(bool useVao, bool isStatic, VertexAttributes* attributes)
  19. {
  20.     m_vbo = useVao ? new VertexBufferObjectWithVAO(isStatic, attributes) :
  21.             new VertexBufferObject(isStatic, attributes);
  22.     m_ibo = new IndexBufferObject(isStatic);
  23. }
  24. Mesh::~Mesh()
  25. {
  26.     delete m_vbo;
  27.     delete m_ibo;
  28. }
  29. void Mesh::setVertices(float* vertices, int bytes)
  30. {
  31.     m_vbo->setVertices(vertices, bytes);
  32. }
  33. void Mesh::setIndices(void* indices, int bytes)
  34. {
  35.     m_ibo->setIndices(indices, bytes);
  36. }
  37. void Mesh::setIndices(void* indices, int bytes, GLenum type)
  38. {
  39.     m_ibo->setIndices(indices, bytes, type);
  40. }
  41. void Mesh::setMode(GLenum mode)
  42. {
  43.     m_mode = mode;
  44. }
  45. void Mesh::render(ShaderProgram* shader)
  46. {
  47.     m_vbo->bind(shader);
  48.     if (m_ibo->getNumIndices() > 0) {
  49.         m_ibo->bind();
  50.         GL_CALL(glDrawElements(m_mode, m_ibo->getNumIndices(), m_ibo->getType(), nullptr));
  51.         m_ibo->unbind();
  52.     } else {
  53.         GL_CALL(glDrawArrays(m_mode, 0, m_vbo->getNumVertices()));
  54.     }
  55.     m_vbo->unbind(shader);
  56. }
  57. } // namespace glcore
复制代码
​    mesh_utils.h
  1. #pragma once
  2. #include "mesh.h"
  3. namespace glcore
  4. {
  5. /**
  6. * 网格工具类
  7. * @author little fat sheep
  8. */
  9. class MeshUtils
  10. {
  11. public:
  12.     static Mesh* createRect(bool reverse);
  13. private:
  14.     static float* getRectVertices(bool reverse);
  15. };
  16. } // namespace glcore
复制代码
​    mesh_utils.cpp
  1. #include "glcore/mesh_utils.h"
  2. namespace glcore
  3. {
  4. Mesh* MeshUtils::createRect(bool reverse)
  5. {
  6.     Mesh* mesh = new Mesh(true, {
  7.             VertexAttribute::Position(),
  8.             VertexAttribute::TexCoords(0)
  9.     });
  10.     float* vertices = getRectVertices(reverse);
  11.     mesh->setVertices(vertices, 4 * 5 * sizeof(float));
  12.     void* indices = new short[] { 0, 1, 2, 2, 3, 0 };
  13.     mesh->setIndices(indices, 6 * sizeof(short));
  14.     return mesh;
  15. }
  16. float* MeshUtils::getRectVertices(bool reverse)
  17. {
  18.     if (reverse) {
  19.         return new float[] { // 中间渲染(FBO)使用
  20.                 -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, // 左下
  21.                 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, // 右下
  22.                 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, // 右上
  23.                 -1.0f, 1.0f, 0.0f, 0.0f, 1.0f // 左上
  24.         };
  25.     }
  26.     return new float[] { // 终端渲染使用
  27.             -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, // 左下
  28.             1.0f, -1.0f, 0.0f, 1.0f, 1.0f, // 右下
  29.             1.0f, 1.0f, 0.0f, 1.0f, 0.0f, // 右上
  30.             -1.0f, 1.0f, 0.0f, 0.0f, 0.0f // 左上
  31.     };
  32. }
  33. } // namespace glcore
复制代码
2.12 GLTexture

​    封装 GLTexture 类是了方便用户进行纹理贴图。为了方便管理多渲染目标图层,定义了 TextureAction 接口,并提供 bind 函数,GLTexture、FBO 都继承了 TextureAction,用户自定义的渲染器或特效类也可以继承 TextureAction,将它们统一视为纹理活动(可绑定),这在特效叠加(或后处理)中非常有用,易于扩展。
​    texture_action.h
  1. #pragma once
  2. #include "core_lib.h"
  3. #include "shader_program.h"
  4. namespace glcore
  5. {
  6. /**
  7. * 纹理活动 (纹理绑定、FBO绑定)
  8. * @author little fat sheep
  9. */
  10. class TextureAction
  11. {
  12. public:
  13.     virtual ~TextureAction() = default;
  14.     virtual void setTexParameter(GLint filter, GLint wrap) {}
  15.     virtual void setBindParameter(char* alias, GLenum unit) {}
  16.     virtual void bind(ShaderProgram* shader) = 0;
  17. };
  18. } // namespace glcore
复制代码
​    gl_texture.h
  1. #pragma once
  2. #include "core_lib.h"
  3. #include "shader_program.h"
  4. #include "texture_action.h"
  5. namespace glcore
  6. {
  7. /**
  8. * 纹理贴图
  9. * @author little fat sheep
  10. */
  11. class GLTexture: public TextureAction
  12. {
  13. private:
  14.     GLuint m_textureHandle = 0; // 纹理句柄
  15.     int m_width = 0; // 纹理宽度
  16.     int m_height = 0; // 纹理高度
  17.     GLint m_filter = GL_LINEAR; // 滤波方式
  18.     GLint m_wrap = GL_CLAMP_TO_EDGE; // 环绕方式
  19.     const char* m_alias = ShaderProgram::UNIFORM_TEXTURE; // 纹理别名(着色器中变量名)
  20.     GLenum m_unit = 0; // 纹理单元 (可能有多个纹理)
  21.     bool m_isDirty = false; // 是否有脏数据 (纹理参数需要更新)
  22. public:
  23.     GLTexture(int width, int height);
  24.     GLTexture(void *buffer, int width, int height);
  25.     ~GLTexture() override;
  26.     void setTexture(const void *buffer);
  27.     void setTexParameter(GLint filter, GLint wrap) override;
  28.     void setBindParameter(char* alias, GLenum unit) override;
  29.     void bind(ShaderProgram* shader) override;
  30.     int getWidth() { return m_width; }
  31.     int getHeight() { return m_height; }
  32. private:
  33.     void applyTexParameter();
  34. };
  35. } // namespace glcore
复制代码
​    gl_texture.cpp
  1. #include "glcore/gl_inspector.h"
  2. #include "glcore/gl_texture.h"
  3. namespace glcore
  4. {
  5. GLTexture::GLTexture(int width, int height):
  6.     m_width(width),
  7.     m_height(height)
  8. {
  9. }
  10. GLTexture::GLTexture(void *buffer, int width, int height): GLTexture(width, height)
  11. {
  12.     setTexture(buffer);
  13. }
  14. GLTexture::~GLTexture()
  15. {
  16.     GL_CALL(glBindTexture(GL_TEXTURE_2D, 0));
  17.     if (m_textureHandle != 0) {
  18.         GL_CALL(glDeleteTextures(1, &m_textureHandle));
  19.         m_textureHandle = 0;
  20.     }
  21. }
  22. /**
  23. * buffer 可以通过以下两种方式得到
  24. * 1) bitmap.copyPixelsToBuffer(bytebuffer);
  25. *    void* buffer = env->GetDirectBufferAddress(bytebuffer);
  26. * 2) AndroidBitmap_lockPixels(env, bitmap, &buffer)
  27. */
  28. void GLTexture::setTexture(const void *buffer)
  29. {
  30.     GL_CALL(glGenTextures(1, &m_textureHandle));
  31.     GL_CALL(glBindTexture(GL_TEXTURE_2D, m_textureHandle));
  32.     applyTexParameter();
  33.     GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_width, m_height, 0,
  34.                  GL_RGBA, GL_UNSIGNED_BYTE, buffer));
  35.     GL_CALL(glGenerateMipmap(GL_TEXTURE_2D));
  36.     GL_CALL(glBindTexture(GL_TEXTURE_2D, 0));
  37.     //GLInspector::checkGLError("setTexture");
  38. }
  39. void GLTexture::setTexParameter(GLint filter, GLint wrap)
  40. {
  41.     m_filter = filter;
  42.     m_wrap = wrap;
  43.     m_isDirty = true;
  44. }
  45. void GLTexture::setBindParameter(char *alias, GLenum unit)
  46. {
  47.     m_alias = alias;
  48.     m_unit = unit;
  49. }
  50. void GLTexture::bind(ShaderProgram *shader)
  51. {
  52.     shader->setUniformi(m_alias, m_unit);
  53.     GL_CALL(glActiveTexture(GL_TEXTURE0 + m_unit));
  54.     GL_CALL(glBindTexture(GL_TEXTURE_2D, m_textureHandle));
  55.     if (m_isDirty)
  56.     {
  57.         applyTexParameter();
  58.     }
  59. }
  60. void GLTexture::applyTexParameter()
  61. {
  62.     GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, m_filter));
  63.     GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, m_filter));
  64.     GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, m_wrap));
  65.     GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, m_wrap));
  66.     m_isDirty = false;
  67. }
  68. } // namespace glcore
复制代码
2.13 FBO

​    FBO 是 Frame Buffer Object 的简称,即帧缓冲对象,主要用于离屏渲染、特效叠加,
​    frame_buffer_object.h
  1. #pragma once
  2. #include "core_lib.h"
  3. #include "format.h"
  4. #include "shader_program.h"
  5. #include "texture_action.h"
  6. namespace glcore
  7. {
  8. /**
  9. * 帧缓冲对象 (简称FBO, 用于离屏渲染)
  10. * @author little fat sheep
  11. */
  12. class FrameBufferObject: public TextureAction
  13. {
  14. private:
  15.     Format* m_format; // 颜色格式
  16.     int m_width; // 缓冲区宽度
  17.     int m_height; // 缓冲区高度
  18.     bool m_hasDepth; // 是否有深度缓冲区
  19.     bool m_hasStencil; // 是否有模板缓冲区
  20.     GLuint m_frameBufferHandle; // 帧缓冲区句柄
  21.     GLuint m_depthBufferHandle; // 深度缓冲区句柄
  22.     GLuint m_stencilBufferHandle; // 模板缓冲区句柄
  23.     GLuint m_colorTextureHandle; // 颜色缓冲区句柄
  24.     GLint m_preFramebufferHandle; // 前一个帧缓冲区句柄
  25.     int m_preFramebufferViewPort[4]; // 前一个帧缓冲区视口
  26.     GLint m_filter = GL_LINEAR; // 滤波方式
  27.     GLint m_wrap = GL_CLAMP_TO_EDGE; // 环绕方式
  28.     const char* m_alias = ShaderProgram::UNIFORM_TEXTURE; // 纹理别名(着色器中变量名)
  29.     GLenum m_unit = 0; // 纹理单元 (可能有多个纹理)
  30.     bool m_isDirty = true; // 是否有脏数据 (纹理参数需要更新)
  31. public:
  32.     FrameBufferObject(Format* format, int width, int height, bool hasDepth, bool hasStencil);
  33.     ~FrameBufferObject() override;
  34.     void setTexParameter(GLint filter, GLint wrap) override;
  35.     void setBindParameter(char* alias, GLenum unit) override;
  36.     void begin();
  37.     void end();
  38.     void bind(ShaderProgram* shader) override;
  39. private:
  40.     void applyTexParameter();
  41. };
  42. } // namespace glcore
复制代码
​    frame_buffer_object.cpp
  1. #include "glcore/frame_buffer_object.h"
  2. #include "glcore/gl_inspector.h"
  3. namespace glcore
  4. {
  5. FrameBufferObject::FrameBufferObject(Format* format, int width, int height, bool hasDepth, bool hasStencil)
  6. {
  7.     m_format = format;
  8.     m_width = width;
  9.     m_height = height;
  10.     m_hasDepth = hasDepth;
  11.     m_hasStencil = hasStencil;
  12.     GL_CALL(glGenFramebuffers(1, &m_frameBufferHandle));
  13.     begin();
  14.     if (m_hasDepth)
  15.     {
  16.         GL_CALL(glGenRenderbuffers(1, &m_depthBufferHandle));
  17.         GL_CALL(glBindRenderbuffer(GL_RENDERBUFFER, m_depthBufferHandle));
  18.         GL_CALL(glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height));
  19.         GL_CALL(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
  20.             GL_RENDERBUFFER, m_depthBufferHandle));
  21.         GL_CALL(glBindRenderbuffer(GL_RENDERBUFFER, 0));
  22.     }
  23.     if (m_hasStencil)
  24.     {
  25.         GL_CALL(glGenRenderbuffers(1, &m_stencilBufferHandle));
  26.         GL_CALL(glBindRenderbuffer(GL_RENDERBUFFER, m_stencilBufferHandle));
  27.         GL_CALL(glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, width, height));
  28.         GL_CALL(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
  29.             GL_RENDERBUFFER, m_stencilBufferHandle));
  30.         GL_CALL(glBindRenderbuffer(GL_RENDERBUFFER, 0));
  31.     }
  32.     GL_CALL(glGenTextures(1, &m_colorTextureHandle));
  33.     GL_CALL(glBindTexture(GL_TEXTURE_2D, m_colorTextureHandle));
  34.     GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, m_format->getFormat(), m_width,
  35.     m_height, 0, m_format->getFormat(), m_format->getType(), nullptr));
  36.     applyTexParameter();
  37.     GL_CALL(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
  38.         GL_TEXTURE_2D, m_colorTextureHandle, 0));
  39.     end();
  40. }
  41. FrameBufferObject::~FrameBufferObject()
  42. {
  43.     GL_CALL(glBindTexture(GL_TEXTURE_2D, 0));
  44.     GL_CALL(glDeleteTextures(1, &m_colorTextureHandle));
  45.     if (m_hasDepth) {
  46.         GL_CALL(glDeleteRenderbuffers(1, &m_depthBufferHandle));
  47.     }
  48.     if (m_hasStencil) {
  49.         GL_CALL(glDeleteRenderbuffers(1, &m_stencilBufferHandle));
  50.     }
  51.     GL_CALL(glDeleteFramebuffers(1, &m_frameBufferHandle));
  52. }
  53. void FrameBufferObject::setTexParameter(GLint filter, GLint wrap)
  54. {
  55.     m_filter = filter;
  56.     m_wrap = wrap;
  57.     m_isDirty = true;
  58. }
  59. void FrameBufferObject::setBindParameter(char* alias, GLenum unit)
  60. {
  61.     m_alias = alias;
  62.     m_unit = unit;
  63. }
  64. void FrameBufferObject::begin()
  65. {
  66.     GL_CALL(glGetIntegerv(GL_FRAMEBUFFER_BINDING, &m_preFramebufferHandle));
  67.     GL_CALL(glGetIntegerv(GL_VIEWPORT, m_preFramebufferViewPort));
  68.     GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, m_frameBufferHandle));
  69.     GL_CALL(glViewport(0, 0, m_width, m_height));
  70. }
  71. void FrameBufferObject::end()
  72. {
  73.     GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, m_preFramebufferHandle));
  74.     GL_CALL(glViewport(m_preFramebufferViewPort[0], m_preFramebufferViewPort[1],
  75.         m_preFramebufferViewPort[2], m_preFramebufferViewPort[3]));
  76. }
  77. void FrameBufferObject::bind(ShaderProgram* shader)
  78. {
  79.     shader->setUniformi(m_alias, m_unit);
  80.     GL_CALL(glActiveTexture(GL_TEXTURE0 + m_unit));
  81.     GL_CALL(glBindTexture(GL_TEXTURE_2D, m_colorTextureHandle));
  82.     if (m_isDirty)
  83.     {
  84.         applyTexParameter();
  85.     }
  86. }
  87. void FrameBufferObject::applyTexParameter()
  88. {
  89.     GL_CALL(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, m_filter));
  90.     GL_CALL(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, m_filter));
  91.     GL_CALL(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, m_wrap));
  92.     GL_CALL(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, m_wrap));
  93.     m_isDirty = false;
  94. }
  95. } // namespace glcore
复制代码
​    format.h
  1. #pragma once
  2. #include "core_lib.h"
  3. namespace glcore
  4. {
  5. /**
  6. * 纹理格式
  7. * @author little fat sheep
  8. */
  9. class Format
  10. {
  11. private:
  12.     GLint format;
  13.     GLenum type;
  14. public:
  15.     Format(GLint format, GLenum type);
  16.     GLint getFormat() { return format; }
  17.     GLenum getType() { return type; }
  18.     static Format* Alpha();
  19.     static Format* LuminanceAlpha();
  20.     static Format* RGB565();
  21.     static Format* RGBA4444();
  22.     static Format* RGB888();
  23.     static Format* RGBA8888();
  24. };
  25. } // namespace glcore
复制代码
​    format.cpp
  1. #include "glcore/format.h"
  2. namespace glcore
  3. {
  4. Format::Format(GLint format, GLenum type):
  5.     format(format),
  6.     type(type)
  7. {
  8. }
  9. Format *Format::Alpha()
  10. {
  11.     return new Format(GL_ALPHA, GL_UNSIGNED_BYTE);
  12. }
  13. Format *Format::LuminanceAlpha()
  14. {
  15.     return new Format(GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE);
  16. }
  17. Format *Format::RGB565()
  18. {
  19.     return new Format(GL_RGB, GL_UNSIGNED_SHORT_5_6_5);
  20. }
  21. Format *Format::RGBA4444()
  22. {
  23.     return new Format(GL_RGB, GL_UNSIGNED_SHORT_4_4_4_4);
  24. }
  25. Format *Format::RGB888()
  26. {
  27.     return new Format(GL_RGB, GL_UNSIGNED_BYTE);
  28. }
  29. Format *Format::RGBA8888()
  30. {
  31.     return new Format(GL_RGBA, GL_UNSIGNED_BYTE);
  32. }
  33. } // namespace glcore
复制代码
3 JNI 相关

​    本节主要介绍 glcore 框架在初始化过程中所依附的工具类,如 View 载体、字符串加载工具、图片加载工具等,它们与 JNI 密切相关,不便于进行跨平台迁移,因此不能将它们归入 glcore 框架中。
​    如果读者对 JNI 不太熟悉,推荐阅读 → JNI环境搭建、JNI基础语法。
3.1 EGLSurfaceView

​    Android 中渲染内容需要 View 容器承载,有以下常用方案,详见 → 【OpenGL ES】不用GLSurfaceView,如何渲染图像。

  • SurfaceView + SurfaceHolder.Callback
  • TextureView + TextureView.SurfaceTextureListener
​    本框架采用 TextureView + TextureView.SurfaceTextureListener 方案,因为它在退后台后不会销毁 Surface,避免反复销毁和创建 Surface,稳定性更好。
​    Java 和 Native 中都有 EGLSurfaceView,它们是相互绑定的,前者为后者提供了 Surface、宽高、Renderer、Context 等属性,并管理了其生命周期。
​    EGLSurfaceView.java
  1. package com.zhyan8.egldemo;
  2. import android.content.Context;
  3. import android.graphics.SurfaceTexture;
  4. import android.util.Log;
  5. import android.view.Choreographer;
  6. import android.view.Surface;
  7. import android.view.TextureView;
  8. import androidx.annotation.NonNull;
  9. /**
  10. * @author little fat sheep
  11. * 承载EGL环境的View, 类比GLSurfaceView
  12. */
  13. public class EGLSurfaceView extends TextureView implements TextureView.SurfaceTextureListener {
  14.     private static final String TAG = "EGLSurfaceView";
  15.     private long mNativeHandle;
  16.     protected Surface mSurface;
  17.     private Choreographer mChoreographer = Choreographer.getInstance();
  18.     static {
  19.         System.loadLibrary("egl-native");
  20.     }
  21.     public EGLSurfaceView(Context context) {
  22.         super(context);
  23.         setSurfaceTextureListener(this);
  24.         mNativeHandle = nativeCreate();
  25.     }
  26.     public void setRenderer(long handle) {
  27.         Log.i(TAG, "setRenderer");
  28.         nativeSetRenderer(mNativeHandle, handle);
  29.     }
  30.     public void startRender() {
  31.         Log.i(TAG, "startRender");
  32.         mChoreographer.removeFrameCallback(mFrameCallback);
  33.         mChoreographer.postFrameCallback(mFrameCallback);
  34.     }
  35.     public void stopRender() {
  36.         Log.i(TAG, "stopRender");
  37.         mChoreographer.removeFrameCallback(mFrameCallback);
  38.     }
  39.     public void requestRender() {
  40.         mFrameCallback.doFrame(System.nanoTime());
  41.     }
  42.     @Override
  43.     public void onSurfaceTextureAvailable(@NonNull SurfaceTexture surface, int width, int height) {
  44.         Log.i(TAG, "onSurfaceTextureAvailable");
  45.         mSurface = new Surface(surface);
  46.         nativeSurfaceCreated(mNativeHandle, mSurface);
  47.         nativeSurfaceChanged(mNativeHandle, width, height);
  48.     }
  49.     @Override
  50.     public void onSurfaceTextureSizeChanged(@NonNull SurfaceTexture surface, int width, int height) {
  51.         Log.i(TAG, "onSurfaceTextureSizeChanged, width=" + width + ", height=" + height);
  52.         nativeSurfaceChanged(mNativeHandle, width, height);
  53.     }
  54.     @Override
  55.     public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surface) {
  56.         Log.i(TAG, "onSurfaceTextureDestroyed");
  57.         nativeSurfaceDestroyed(mNativeHandle);
  58.         return false;
  59.     }
  60.     @Override
  61.     public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface) {
  62.     }
  63.     @Override
  64.     protected void onDetachedFromWindow() {
  65.         super.onDetachedFromWindow();
  66.         Log.i(TAG, "onDetachedFromWindow");
  67.         stopRender();
  68.         setSurfaceTextureListener(null);
  69.         mSurface.release();
  70.         nativeDestroy(mNativeHandle);
  71.         mNativeHandle = 0;
  72.     }
  73.     private Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
  74.         @Override
  75.         public void doFrame(long frameTimeNanos) {
  76.             mChoreographer.postFrameCallback(this);
  77.             nativeDrawFrame(mNativeHandle);
  78.         }
  79.     };
  80.     private native long nativeCreate();
  81.     private native void nativeSetRenderer(long viewHandle, long rendererHandle);
  82.     private native void nativeSurfaceCreated(long handle, Object surface);
  83.     private native void nativeSurfaceChanged(long handle, int width, int height);
  84.     private native void nativeDrawFrame(long handle);
  85.     private native void nativeSurfaceDestroyed(long handle);
  86.     private native void nativeDestroy(long handle);
  87. }
复制代码
​    jin_egl_surface_view.cpp
  1. #include
  2. #include
  3. #include
  4. #include <jni.h>
  5. #include "glcore/core.h"
  6. #define LOG_TAG "JNIBrige_EGLSurfaceView"
  7. #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
  8. using namespace glcore;
  9. static jlong nativeCreate(JNIEnv *env, jobject thiz)
  10. {
  11.     LOGI("nativeCreate");
  12.     EGLSurfaceView* view = new EGLSurfaceView();
  13.     return reinterpret_cast<jlong>(view);
  14. }
  15. static void nativeSetRenderer(JNIEnv *env, jobject thiz, jlong viewHandle, jlong rendererHandle)
  16. {
  17.     LOGI("nativeSetRenderer");
  18.     EGLSurfaceView* view = reinterpret_cast<EGLSurfaceView*>(viewHandle);
  19.     EGLSurfaceView::Renderer* renderer = reinterpret_cast<EGLSurfaceView::Renderer*>(rendererHandle);
  20.     view->setRenderer(renderer);
  21. }
  22. static void nativeSurfaceCreated(JNIEnv* env, jobject thiz, jlong handle, jobject surface)
  23. {
  24.     LOGI("nativeSurfaceCreated");
  25.     EGLSurfaceView* view = reinterpret_cast<EGLSurfaceView*>(handle);
  26.     ANativeWindow* window = ANativeWindow_fromSurface(env, surface);
  27.     app->setWindow(window);
  28.     view->surfaceCreated();
  29. }
  30. static void nativeSurfaceChanged(JNIEnv* env, jobject thiz, jlong handle, jint width, jint height)
  31. {
  32.     LOGI("nativeSurfaceChanged");
  33.     EGLSurfaceView* view = reinterpret_cast<EGLSurfaceView*>(handle);
  34.     view->surfaceChanged(width, height);
  35. }
  36. static void nativeDrawFrame(JNIEnv* env, jobject thiz, jlong handle)
  37. {
  38.     EGLSurfaceView* view = reinterpret_cast<EGLSurfaceView*>(handle);
  39.     view->drawFrame();
  40. }
  41. static void nativeSurfaceDestroyed(JNIEnv* env, jobject thiz, jlong handle)
  42. {
  43.     LOGI("nativeSurfaceDestroyed");
  44.     EGLSurfaceView* view = reinterpret_cast<EGLSurfaceView*>(handle);
  45.     view->surfaceDestroy();
  46. }
  47. static void nativeDestroy(JNIEnv* env, jobject thiz, jlong handle)
  48. {
  49.     LOGI("nativeDestroy");
  50.     EGLSurfaceView* view = reinterpret_cast<EGLSurfaceView*>(handle);
  51.     delete view;
  52. }
  53. static JNINativeMethod methods[] = {
  54.         { "nativeCreate", "()J", (void*) nativeCreate },
  55.         { "nativeSetRenderer", "(JJ)V", (void*) nativeSetRenderer },
  56.         { "nativeSurfaceCreated", "(JLjava/lang/Object;)V", (void*) nativeSurfaceCreated },
  57.         { "nativeSurfaceChanged", "(JII)V", (void*) nativeSurfaceChanged },
  58.         { "nativeDrawFrame", "(J)V", (void*) nativeDrawFrame },
  59.         { "nativeSurfaceDestroyed", "(J)V", (void*) nativeSurfaceDestroyed },
  60.         { "nativeDestroy", "(J)V", (void*) nativeDestroy },
  61. };
  62. static int registerNativeMethods(JNIEnv* env) {
  63.     int result = -1;
  64.     jclass clazz = env->FindClass("com/zhyan8/egldemo/EGLSurfaceView");
  65.     if (clazz != NULL) {
  66.         jint len = sizeof(methods) / sizeof(methods[0]);
  67.         if (env->RegisterNatives(clazz, methods, len) == JNI_OK) {
  68.             result = 0;
  69.         }
  70.     }
  71.     return result;
  72. }
  73. jint JNI_OnLoad(JavaVM* vm, void* reserved) {
  74.     JNIEnv* env = NULL;
  75.     jint result = -1;
  76.     if (vm->GetEnv((void**) &env, JNI_VERSION_1_6) == JNI_OK) {
  77.         if (NULL != env && registerNativeMethods(env) == 0) {
  78.             result = JNI_VERSION_1_6;
  79.         }
  80.     }
  81.     return result;
  82. }
复制代码
3.2 StringUtils

​    StringUtils 用于加载顶点和片元着色器资源为字符串。
​    StringUtils.java
  1. package com.zhyan8.egldemo;
  2. import android.content.Context;
  3. import android.util.Log;
  4. import java.io.BufferedReader;
  5. import java.io.IOException;
  6. import java.io.InputStream;
  7. import java.io.InputStreamReader;
  8. /**
  9. * 字符串工具类
  10. * @author little fat sheep
  11. */
  12. public class StringUtils {
  13.     private static final String TAG = "BitmapUtils";
  14.     /**
  15.      * 根据资源路径读取字符串
  16.      * @param assetPath 资源路径, 如: "jelly_vert.glsl"
  17.      */
  18.     public static String loadStringFromAsset(Context context, String assetPath) {
  19.         String str = "";
  20.         try (InputStream inputStream = context.getAssets().open(assetPath)) {
  21.             str = loadString(inputStream);
  22.         } catch (IOException e) {
  23.             Log.w(TAG, "loadString error, message=" + e.getMessage());
  24.         }
  25.         return str;
  26.     }
  27.     /**
  28.      * 根据资源id读取字符串
  29.      * @param rawId 资源id, 如: "R.raw.vertex_shader"
  30.      */
  31.     public static String loadStringFromRaw(Context context, String rawId) {
  32.         if (rawId.startsWith("R.raw.")) {
  33.             rawId = rawId.substring(6); // Remove "R.raw."
  34.         }
  35.         int id = context.getResources().getIdentifier(rawId, "raw", context.getPackageName());
  36.         if (id == 0) {
  37.             Log.e(TAG, "loadBitmapFromRaw, resource is not found, rawId=" + rawId);
  38.             return null;
  39.         }
  40.         return loadStringFromRaw(context, id);
  41.     }
  42.     /**
  43.      * 根据资源id读取字符串
  44.      * @param rawId 资源id, 如: R.raw.vertex_shader
  45.      */
  46.     public static String loadStringFromRaw(Context context, int rawId) {
  47.         String str = "";
  48.         try (InputStream inputStream = context.getResources().openRawResource(rawId)) {
  49.             str = loadString(inputStream);
  50.         } catch (IOException e) {
  51.             Log.w(TAG, "loadString error, message=" + e.getMessage());
  52.         }
  53.         return str;
  54.     }
  55.     private static String loadString(InputStream inputStream) {
  56.         StringBuilder sb = new StringBuilder();
  57.         try (BufferedReader br = new BufferedReader(new InputStreamReader(inputStream))) {
  58.             String line;
  59.             while ((line = br.readLine()) != null) {
  60.                 sb.append(line).append("\n");
  61.             }
  62.         } catch (IOException e) {
  63.             Log.w(TAG, "loadString error, message=" + e.getMessage());
  64.         }
  65.         return sb.toString();
  66.     }
  67. }
复制代码
​    string_utils.h
  1. #pragma once
  2. /**
  3. * String工具类
  4. * @author little fat sheep
  5. */
  6. class StringUtils
  7. {
  8. public:
  9.     /**
  10.      * 根据资源路径读取字符串
  11.      * @param asset 资源路径, 如: "vertex_shader.glsl"
  12.      */
  13.     static const char* loadStringFromAsset(const char* asset);
  14.     /**
  15.      * 根据资源id读取字符串
  16.      * @param rawId 资源id, 如: "R.raw.vertex_shader"
  17.      */
  18.     static const char* loadStringFromRaw(const char* rawId);
  19. };
复制代码
​    string_utils.cpp
  1. #include <jni.h>
  2. #include "glcore/core.h"
  3. #include "jni/jni_refs.h"
  4. #include "jni/string_utils.h"
  5. using namespace glcore;
  6. const char* StringUtils::loadStringFromAsset(const char* asset)
  7. {
  8.     JNIEnv* env = app->jniEnv;
  9.     jobject context = app->context;
  10.     jstring assetStr = env->NewStringUTF(asset);
  11.     jclass clazz = env->FindClass("com/zhyan8/egldemo/StringUtils");
  12.     jmethodID method = LoadStringFromAssetMethodId(env);
  13.     jstring jstr = (jstring) env->CallStaticObjectMethod(clazz, method, context, assetStr);
  14.     const char* str = env->GetStringUTFChars(jstr, nullptr);
  15.     return str;
  16. }
  17. const char* StringUtils::loadStringFromRaw(const char* rawId)
  18. {
  19.     JNIEnv* env = app->jniEnv;
  20.     jobject context = app->context;
  21.     jstring rawIdStr = env->NewStringUTF(rawId);
  22.     jclass clazz = env->FindClass("com/zhyan8/egldemo/StringUtils");
  23.     jmethodID method = LoadStringFromRawMethodId(env);
  24.     jstring jstr = (jstring) env->CallStaticObjectMethod(clazz, method, context, rawIdStr);
  25.     const char* str = env->GetStringUTFChars(jstr, nullptr);
  26.     return str;
  27. }
复制代码
3.3 BitmapUtils

​    BitmapUtils 用于加载图片资源为位图。
​    BitmapUtils.java
  1. package com.zhyan8.egldemo;
  2. import android.content.Context;
  3. import android.graphics.Bitmap;
  4. import android.graphics.BitmapFactory;
  5. import android.util.Log;
  6. import java.io.IOException;
  7. import java.io.InputStream;
  8. /**
  9. * Bitmap工具类
  10. * @author little fat sheep
  11. */
  12. public class BitmapUtils {
  13.     private static final String TAG = "BitmapUtils";
  14.     /**
  15.      * 根据资源路径读取bitmap
  16.      * @param assetPath 资源路径, 如: "textures/xxx.jpg"
  17.      */
  18.     public static Bitmap loadBitmapFromAsset(Context context, String assetPath) {
  19.         Bitmap bitmap = null;
  20.         try (InputStream inputStream = context.getAssets().open(assetPath)) {
  21.             BitmapFactory.Options options = getOptions();
  22.             bitmap = BitmapFactory.decodeStream(inputStream, null, options);
  23.         } catch (IOException e) {
  24.             Log.e(TAG, "loadBitmapFromAsset error, message=" + e.getMessage());
  25.         }
  26.         return bitmap;
  27.     }
  28.     /**
  29.      * 根据资源id读取bitmap
  30.      * @param rawId 资源id, 如: "R.raw.xxx"
  31.      */
  32.     public static Bitmap loadBitmapFromRaw(Context context, String rawId) {
  33.         if (rawId.startsWith("R.raw.")) {
  34.             rawId = rawId.substring(6); // Remove "R.raw."
  35.         }
  36.         int id = context.getResources().getIdentifier(rawId, "raw", context.getPackageName());
  37.         if (id == 0) {
  38.             Log.e(TAG, "loadBitmapFromRaw, resource is not found, rawId=" + rawId);
  39.             return null;
  40.         }
  41.         return loadBitmapFromRaw(context, id);
  42.     }
  43.     /**
  44.      * 根据资源id读取bitmap
  45.      * @param rawId 资源id, 如: R.raw.xxx
  46.      */
  47.     public static Bitmap loadBitmapFromRaw(Context context, int rawId) {
  48.         Bitmap bitmap = null;
  49.         try (InputStream inputStream = context.getResources().openRawResource(rawId)) {
  50.             BitmapFactory.Options options = getOptions();
  51.             bitmap = BitmapFactory.decodeStream(inputStream, null, options);
  52.         } catch (IOException e) {
  53.             Log.e(TAG, "loadBitmapFromRaw error, message=" + e.getMessage());
  54.         }
  55.         return bitmap;
  56.     }
  57.     private static BitmapFactory.Options getOptions() {
  58.         BitmapFactory.Options options = new BitmapFactory.Options();
  59.         options.inScaled = false;
  60.         return options;
  61.     }
  62. }
复制代码
​    bitmap_utils.h
  1. #pragma once
  2. #include <jni.h>
  3. struct BitmapData
  4. {
  5.     void* buffer;
  6.     int width;
  7.     int height;
  8. };
  9. /**
  10. * Bitmap工具类
  11. * @author little fat sheep
  12. */
  13. class BitmapUtils
  14. {
  15. public:
  16.     /**
  17.     * 根据资源路径读取bitmap
  18.     * @param asset 资源路径, 如: "textures/xxx.jpg"
  19.     */
  20.     static BitmapData* loadBitmapDataFromAsset(const char* asset);
  21.     /**
  22.      * 根据资源id读取bitmap
  23.      * @param rawId 资源id, 如: "R.raw.xxx"
  24.      */
  25.     static BitmapData* loadBitmapDataFromRaw(const char* rawId);
  26. private:
  27.     static jobject loadBitmapFromAsset(JNIEnv* env, jobject context, const char* asset);
  28.     static jobject loadBitmapFromRaw(JNIEnv* env, jobject context, const char* rawId);
  29.     static BitmapData* getBitmapData(JNIEnv* env, jobject bitmap);
  30. };
复制代码
​    bitmap_utils.cpp
  1. #include
  2. #include
  3. #include <string>
  4. #include "glcore/core.h"
  5. #include "jni/bitmap_utils.h"
  6. #include "jni/jni_refs.h"
  7. #define LOG_TAG "Native: BitmapUtils"
  8. #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
  9. using namespace glcore;
  10. BitmapData* BitmapUtils::loadBitmapDataFromAsset(const char* asset) {
  11.     JNIEnv* env = app->jniEnv;
  12.     jobject context = app->context;
  13.     jobject bitmap = loadBitmapFromAsset(env, context, asset);
  14.     if (!bitmap) {
  15.         LOGI("loadBitmapDataFromAsset, bitmap is null: %s", asset);
  16.         return nullptr;
  17.     }
  18.     return getBitmapData(env, bitmap);
  19. }
  20. BitmapData* BitmapUtils::loadBitmapDataFromRaw(const char* rawId)
  21. {
  22.     JNIEnv* env = app->jniEnv;
  23.     jobject context = app->context;
  24.     jobject bitmap = loadBitmapFromRaw(env, context, rawId);
  25.     if (!bitmap) {
  26.         LOGI("loadBitmapDataFromRaw, bitmap is null: %s", rawId);
  27.         return nullptr;
  28.     }
  29.     return getBitmapData(env, bitmap);
  30. }
  31. jobject BitmapUtils::loadBitmapFromAsset(JNIEnv* env, jobject context, const char* asset)
  32. {
  33.     jstring assetStr = env->NewStringUTF(asset);
  34.     jclass clazz = env->FindClass("com/zhyan8/egldemo/BitmapUtils");
  35.     jmethodID method = LoadBitmapFromAssetMethodId(env);
  36.     jobject bitmap = env->CallStaticObjectMethod(clazz, method, context, assetStr);
  37.     return bitmap;
  38. }
  39. jobject BitmapUtils::loadBitmapFromRaw(JNIEnv* env, jobject context, const char* rawId)
  40. {
  41.     jstring rawIdStr = env->NewStringUTF(rawId);
  42.     jclass clazz = env->FindClass("com/zhyan8/egldemo/BitmapUtils");
  43.     jmethodID method = LoadBitmapFromRawMethodId(env);
  44.     jobject bitmap = env->CallStaticObjectMethod(clazz, method, context, rawIdStr);
  45.     return bitmap;
  46. }
  47. BitmapData* BitmapUtils::getBitmapData(JNIEnv* env, jobject bitmap)
  48. {
  49.     AndroidBitmapInfo info;
  50.     if (AndroidBitmap_getInfo(env, bitmap, &info))
  51.     {
  52.         LOGI("getBitmapData, failed to get bitmap info");
  53.         return nullptr;
  54.     }
  55.     void* buffer;
  56.     if (AndroidBitmap_lockPixels(env, bitmap, &buffer)) {
  57.         LOGI("getBitmapData, failed to lock bitmap pixels");
  58.         return nullptr;
  59.     }
  60.     BitmapData* data = new BitmapData();
  61.     data->buffer = buffer;
  62.     data->width = info.width;
  63.     data->height = info.height;
  64.     return data;
  65. }
复制代码
3.4 jin_ref

​    jni_ref 提供了 StringUtils 和 BitmaUtils 的类路径、函数名、函数签名等信息。
​    jni_ref.h
  1. #pragma once
  2. #include <jni.h>
  3. jmethodID LoadBitmapFromAssetMethodId(JNIEnv* env);
  4. jmethodID LoadBitmapFromRawMethodId(JNIEnv* env);
  5. jmethodID LoadStringFromAssetMethodId(JNIEnv* env);
  6. jmethodID LoadStringFromRawMethodId(JNIEnv* env);
  7. jmethodID GetMethodId(JNIEnv* env, const char* method[]);
  8. jmethodID GetStaticMethodId(JNIEnv* env, const char* method[]);
复制代码
​    jni_ref.c
  1. #include "jni/jni_refs.h"
  2. const char* loadBitmapFromAssetTab[] = {
  3.         "com/zhyan8/egldemo/BitmapUtils",
  4.         "loadBitmapFromAsset",
  5.         "(Landroid/content/Context;Ljava/lang/String;)Landroid/graphics/Bitmap;"
  6. };
  7. const char* loadBitmapFromRawTab[] = {
  8.         "com/zhyan8/egldemo/BitmapUtils",
  9.         "loadBitmapFromRaw",
  10.         "(Landroid/content/Context;Ljava/lang/String;)Landroid/graphics/Bitmap;"
  11. };
  12. const char* loadStringFromAssetTab[] = {
  13.         "com/zhyan8/egldemo/StringUtils",
  14.         "loadStringFromAsset",
  15.         "(Landroid/content/Context;Ljava/lang/String;)Ljava/lang/String;"
  16. };
  17. const char* loadStringFromRawTab[] = {
  18.         "com/zhyan8/egldemo/StringUtils",
  19.         "loadStringFromRaw",
  20.         "(Landroid/content/Context;Ljava/lang/String;)Ljava/lang/String;"
  21. };
  22. jmethodID LoadBitmapFromAssetMethodId(JNIEnv* env)
  23. {
  24.     return GetStaticMethodId(env, loadBitmapFromAssetTab);
  25. }
  26. jmethodID LoadBitmapFromRawMethodId(JNIEnv* env)
  27. {
  28.     return GetStaticMethodId(env, loadBitmapFromRawTab);
  29. }
  30. jmethodID LoadStringFromAssetMethodId(JNIEnv* env)
  31. {
  32.     return GetStaticMethodId(env, loadStringFromAssetTab);
  33. }
  34. jmethodID LoadStringFromRawMethodId(JNIEnv* env)
  35. {
  36.     return GetStaticMethodId(env, loadStringFromRawTab);
  37. }
  38. jmethodID GetMethodId(JNIEnv* env, const char* method[])
  39. {
  40.     jclass clazz = env->FindClass(method[0]);
  41.     return env->GetMethodID(clazz, method[1], method[2]);
  42. }
  43. jmethodID GetStaticMethodId(JNIEnv* env, const char* method[])
  44. {
  45.     jclass clazz = env->FindClass(method[0]);
  46.     return env->GetStaticMethodID(clazz, method[1], method[2]);
  47. }
复制代码
4 应用

​    本节将基于 glcore 框架写一个色散特效叠加果冻特效的 Demo,体验一下 glcore 的便捷之处。
4.1 MyRenderer

​    my_renderer.h
  1. #pragma once
  2. #include "glcore/core.h"
  3. #include "dispersion_effect.h"
  4. #include "jelly_effect.h"
  5. using namespace glcore;
  6. /**
  7. * 自定义渲染器
  8. * @author little fat sheep
  9. */
  10. class MyRenderer : public EGLSurfaceView::Renderer
  11. {
  12. private:
  13.     DispersionEffect* m_dispersionEffect;
  14.     JellyEffect* m_jellyEffect;
  15.     long m_startTime = 0;
  16.     float m_runTime = 0.0f;
  17. public:
  18.     MyRenderer();
  19.     ~MyRenderer() override;
  20.     void onSurfaceCreated() override;
  21.     void onSurfaceChanged(int width, int height) override;
  22.     void onDrawFrame() override;
  23. private:
  24.     long getTimestamp();
  25. };
复制代码
​    my_renderer.cpp
  1. #include
  2. #include <chrono>
  3. #include "custom/my_renderer.h"
  4. #define LOG_TAG "Native: MyRenderer"
  5. #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
  6. using namespace glcore;
  7. using namespace std::chrono;
  8. MyRenderer::MyRenderer()
  9. {
  10.     LOGI("init");
  11.     m_dispersionEffect = new DispersionEffect();
  12.     m_jellyEffect = new JellyEffect();
  13.     m_jellyEffect->setTexAction(m_dispersionEffect);
  14. }
  15. MyRenderer::~MyRenderer()
  16. {
  17.     LOGI("destroy");
  18.     delete m_dispersionEffect;
  19.     delete m_jellyEffect;
  20. }
  21. void MyRenderer::onSurfaceCreated()
  22. {
  23.     LOGI("onSurfaceCreated");
  24.     m_dispersionEffect->onCreate();
  25.     m_jellyEffect->onCreate();
  26.     GL_CALL(glClearColor(0.1f, 0.2f, 0.3f, 0.4f));
  27.     m_startTime = getTimestamp();
  28. }
  29. void MyRenderer::onSurfaceChanged(int width, int height)
  30. {
  31.     LOGI("onSurfaceChanged, width: %d, height: %d", width, height);
  32.     GL_CALL(glViewport(0, 0, width, height));
  33.     m_dispersionEffect->onResize(width, height);
  34.     m_jellyEffect->onResize(width, height);
  35. }
  36. void MyRenderer::onDrawFrame()
  37. {
  38.     m_runTime = (getTimestamp() - m_startTime) / 1000.0f;
  39.     GL_CALL(glClear(GL_COLOR_BUFFER_BIT));
  40.     m_dispersionEffect->onDraw(m_runTime);
  41.     m_jellyEffect->onDraw(m_runTime);
  42. }
  43. long MyRenderer::getTimestamp()
  44. {
  45.     auto now = std::chrono::system_clock::now(); // 获取当前时间
  46.     auto duration = now.time_since_epoch(); // 转换为自纪元以来的时间
  47.     return duration_cast<milliseconds>(duration).count();
  48. }
复制代码
4.2 DispersionEffect

​    DispersionEffect 是色散特效。
​    dispersion_effect.h
  1. #pragma once
  2. #include "glcore/core.h"
  3. using namespace glcore;
  4. /**
  5. * 色散特效
  6. * @author little fat sheep
  7. */
  8. class DispersionEffect: public TextureAction
  9. {
  10. private:
  11.     ShaderProgram* m_program;
  12.     Mesh* m_mesh;
  13.     GLTexture* m_glTexture;
  14.     FrameBufferObject* m_fbo;
  15. public:
  16.     DispersionEffect();
  17.     ~DispersionEffect() override;
  18.     void onCreate();
  19.     void onResize(int width, int height);
  20.     void onDraw(float runtime);
  21.     void bind(ShaderProgram* shader) override;
  22. private:
  23.     void createProgram();
  24.     void createTexture();
  25. };
复制代码
​    dispersion_effect.cpp
  1. #include
  2. #include "custom/dispersion_effect.h"
  3. #include "jni/bitmap_utils.h"
  4. #include "jni/string_utils.h"
  5. #define LOG_TAG "Native: DispersionEffect"
  6. #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
  7. using namespace glcore;
  8. DispersionEffect::DispersionEffect()
  9. {
  10.     LOGI("init");
  11. }
  12. DispersionEffect::~DispersionEffect()
  13. {
  14.     LOGI("destroy");
  15.     delete m_program;
  16.     delete m_mesh;
  17.     delete m_glTexture;
  18.     delete m_fbo;
  19. }
  20. void DispersionEffect::onCreate()
  21. {
  22.     LOGI("onCreate");
  23.     createProgram();
  24.     createTexture();
  25.     m_mesh = MeshUtils::createRect(true);
  26.     m_fbo = new FrameBufferObject(Format::RGBA8888(), app->width, app->height, false, false);
  27. }
  28. void DispersionEffect::onResize(int width, int height)
  29. {
  30.     LOGI("onResize, width: %d, height: %d", width, height);
  31. }
  32. void DispersionEffect::onDraw(float runtime)
  33. {
  34.     m_fbo->begin();
  35.     m_program->bind();
  36.     m_program->setUniformf("u_time", runtime);
  37.     m_program->setUniformf("u_aspect", app->aspect);
  38.     m_glTexture->bind(m_program);
  39.     m_mesh->render(m_program);
  40.     m_fbo->end();
  41. }
  42. void DispersionEffect::bind(ShaderProgram* shader)
  43. {
  44.     m_fbo->bind(shader);
  45. }
  46. void DispersionEffect::createProgram()
  47. {
  48.     LOGI("createProgram");
  49.     const char* vertexCode = StringUtils::loadStringFromAsset("dispersion_vert.glsl");
  50.     const char* fragmentCode = StringUtils::loadStringFromAsset("dispersion_frag.glsl");
  51.     m_program = new ShaderProgram(vertexCode, fragmentCode);
  52. }
  53. void DispersionEffect::createTexture()
  54. {
  55.     LOGI("createTexture");
  56.     BitmapData* data = BitmapUtils::loadBitmapDataFromAsset("girl.jpg");
  57.     m_glTexture = new GLTexture(data->buffer, data->width, data->height);
  58. }
复制代码
​    dispersion_vert.glsl
  1. attribute vec4 a_position;
  2. attribute vec2 a_texCoord0;
  3. varying vec2 v_texCoord;
  4. void main() {
  5.      gl_Position = a_position;
  6.      v_texCoord = a_texCoord0;
  7. }
复制代码
​    dispersion_frag.glsl
  1. precision highp float;
  2. uniform float u_aspect;
  3. uniform float u_time;
  4. uniform sampler2D u_texture;
  5. varying vec2 v_texCoord;
  6. vec2 getOffset() { // 偏移函数
  7.      float time = u_time * 1.5;
  8.      vec2 dire = vec2(sin(time), cos(time));
  9.      float strength = sin(u_time * 2.0) * 0.004;
  10.      return dire * strength * vec2(1.0, 1.0 / u_aspect);
  11. }
  12. void main() {
  13.      vec2 offset = getOffset();
  14.      vec4 color = texture2D(u_texture, v_texCoord);
  15.      color.r = texture2D(u_texture, v_texCoord + offset).r;
  16.      color.b = texture2D(u_texture, v_texCoord - offset).b;
  17.      gl_FragColor = color;
  18. }
复制代码
4.3 JellyEffect

​    JellyEffect 是果冻特效。
​    jelly_effect.h
  1. #pragma once
  2. #include "glcore/core.h"
  3. using namespace glcore;
  4. /**
  5. * 果冻特效
  6. * @author little fat sheep
  7. */
  8. class JellyEffect
  9. {
  10. private:
  11.     ShaderProgram* m_program;
  12.     Mesh* m_mesh;
  13.     TextureAction* m_texAction;
  14. public:
  15.     JellyEffect();
  16.     ~JellyEffect();
  17.     void setTexAction(TextureAction* texAction);
  18.     void onCreate();
  19.     void onResize(int width, int height);
  20.     void onDraw(float runtime);
  21. private:
  22.     void createProgram();
  23. };
复制代码
​    jelly_effect.cpp
  1. #include
  2. #include "custom/jelly_effect.h"
  3. #include "jni/bitmap_utils.h"
  4. #include "jni/string_utils.h"
  5. #define LOG_TAG "Native: JellyEffect"
  6. #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
  7. using namespace glcore;
  8. JellyEffect::JellyEffect()
  9. {
  10.     LOGI("init");
  11. }
  12. JellyEffect::~JellyEffect()
  13. {
  14.     LOGI("destroy");
  15.     delete m_program;
  16.     delete m_mesh;
  17. }
  18. void JellyEffect::setTexAction(TextureAction* texAction)
  19. {
  20.     m_texAction = texAction;
  21. }
  22. void JellyEffect::onCreate()
  23. {
  24.     LOGI("onCreate");
  25.     createProgram();
  26.     m_mesh = MeshUtils::createRect(false);
  27. }
  28. void JellyEffect::onResize(int width, int height)
  29. {
  30.     LOGI("onResize, width: %d, height: %d", width, height);
  31. }
  32. void JellyEffect::onDraw(float runtime)
  33. {
  34.     m_program->bind();
  35.     m_program->setUniformf("u_time", runtime);
  36.     m_program->setUniformf("u_aspect", app->aspect);
  37.     m_texAction->bind(m_program);
  38.     m_mesh->render(m_program);
  39. }
  40. void JellyEffect::createProgram()
  41. {
  42.     LOGI("createProgram");
  43.     const char* vertexCode = StringUtils::loadStringFromAsset("jelly_vert.glsl");
  44.     const char* fragmentCode = StringUtils::loadStringFromAsset("jelly_frag.glsl");
  45.     m_program = new ShaderProgram(vertexCode, fragmentCode);
  46. }
复制代码
​    jelly_vert.glsl
  1. attribute vec4 a_position;
  2. attribute vec2 a_texCoord0;
  3. varying vec2 v_texCoord;
  4. void main() {
  5.      gl_Position = a_position;
  6.      v_texCoord = a_texCoord0;
  7. }
复制代码
​    jelly_frag.glsl
  1. precision highp float;
  2. uniform float u_aspect;
  3. uniform float u_time;
  4. uniform sampler2D u_texture;
  5. varying vec2 v_texCoord;
  6. vec2 fun(vec2 center, vec2 uv) { // 畸变函数
  7.      vec2 dire = normalize(uv - center);
  8.      float dist = distance(uv, center);
  9.      vec2 uv1 = uv + sin(dist * 2.2 + u_time * 3.5) * 0.025;
  10.      return uv1;
  11. }
  12. void main() {
  13.      vec2 uv = vec2(v_texCoord.x, v_texCoord.y / u_aspect);
  14.      vec2 center = vec2(0.5, 0.5 / u_aspect);
  15.      vec2 uv1 = fun(center, uv);
  16.      uv1.y *= u_aspect;
  17.      gl_FragColor = texture2D(u_texture, uv1);
  18. }
复制代码
4.4 运行效果

​    运行效果如下,可以看到叠加了色散和果冻特效。
3.gif

​    声明:本文转自【OpenGL ES】在Android上手撕一个mini版的渲染框架。

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册