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.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.2 CMakeLists
CMakeLists.txt- # 设置库名
- set(LIB_NAME "glcore")
- # 递归添加源文件列表
- file(GLOB_RECURSE GL_CORE_SOURCES src *.cpp)
- # 添加预构建库
- add_library(${LIB_NAME} ${GL_CORE_SOURCES})
- # 将当前目录设为公共头文件目录 (任何链接glcore库的目标都能自动获得这个头文件路径)
- target_include_directories(${LIB_NAME} PUBLIC .)
- # 添加链接的三方库文件
- target_link_libraries(${LIB_NAME} PRIVATE
- android
- log
- EGL
- GLESv3)
复制代码 2.3 核心头文件
核心头文件分为对内和对外的,即内部依赖 core_lib,外部开放 core。
core_lib.h- #pragma once
- /**
- * glcore 依赖的核心 GL 库, 便于将 glcore 移植到其他平台
- * Android: EGL + GLESv3
- * Windows: glfw / freeglut + glad / glew
- *
- * @author little fat sheep
- */
- #include <EGL/egl.h>
- #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- #pragma once
- /**
- * glcore核心头文件
- * 该头文件是留给外部使用的, glcore内部不能使用, 避免自己包含自己
- * @author little fat sheep
- */
- // OpenGL ES API
- #include "core_lib.h"
- // glcore 核心头文件
- #include "application.h"
- #include "elg_surface_view.h"
- #include "format.h"
- #include "frame_buffer_object.h"
- #include "gl_inspector.h"
- #include "gl_texture.h"
- #include "mesh.h"
- #include "mesh_utils.h"
- #include "shader_program.h"
- #include "texture_action.h"
- #include "vertex_attribute.h"
复制代码 core.h 只提供给外部使用,方便外部只需要包含一个头文件,就能获取 glcore 的基础能力。
2.4 Application
Application 主要用于管理全局环境,使用单例模式,方便获取一些全局的变量。它也是 glcore 中唯一一个依赖平台相关的接口(除日志 log 接口外),如:jniEnv、context、m_window 都是 Android 特有的,如果将 glcore 迁移到 Windows 中,这些变量全都要替换或删除,将这些平台相关变量都集中在 Application 中,迁移平台时修改起来也比较容易,避免太分散容易漏掉。
application.h- #pragma once
- #include
- #include <jni.h>
- #define app Application::getInstance()
- namespace glcore
- {
- /**
- * 应用程序, 存储全局的参数, 便于访问
- * @author little fat sheep
- */
- class Application {
- private:
- static Application* sInstance;
- public:
- JNIEnv* jniEnv = nullptr;
- jobject context = nullptr;
- int width = 0;
- int height = 0;
- float aspect = 1.0f;
- private:
- ANativeWindow* m_window = nullptr;
- public:
- static Application* getInstance();
- ~Application();
- void resize(int width, int height);
- ANativeWindow* getWindow() { return m_window; }
- void setWindow(ANativeWindow* window);
- void releaseWindow();
- private:
- Application() {};
- };
- } // namespace glcore
复制代码 application.cpp- #include "glcore/application.h"
- namespace glcore
- {
- Application* Application::sInstance = nullptr;
- Application *Application::getInstance()
- {
- if (sInstance == nullptr)
- {
- sInstance = new Application();
- }
- return sInstance;
- }
- Application::~Application()
- {
- jniEnv->DeleteGlobalRef(context);
- releaseWindow();
- }
- void Application::resize(int width, int height)
- {
- this->width = width;
- this->height = height;
- this->aspect = (float) width / (float) height;
- }
- void Application::setWindow(ANativeWindow* window)
- {
- m_window = window;
- resize(ANativeWindow_getWidth(window), ANativeWindow_getHeight(window));
- }
- void Application::releaseWindow()
- {
- if (m_window)
- {
- ANativeWindow_release(m_window);
- m_window = nullptr;
- }
- }
- } // namespace glcore
复制代码 2.5 GLInspector
GLInspector 主要用于异常信息检测,另外设计了 EGL_CALL 和 GL_CALL 两个宏,分别对 EGL 和 GL 指令进行装饰。如果定义了 DEBUG 宏,就会对每个 EGL 和 GL 指令进行异常检测,方便调试代码;如果未定义了 DEBUG 宏,就不会进行异常检测。
用户可以在 CMakeLists.txt 中添加预编译宏 DEBUG,这样就可以根据 Release 和 Debug 版本自动构建不同的版本。- if (CMAKE_BUILD_TYPE STREQUAL "Debug")
- # 添加预编译宏
- add_definitions(-DDEBUG)
- endif ()
复制代码 gl_inspector.h- #pragma once
- #include "core_lib.h"
- #ifdef DEBUG
- #define EGL_CALL(func) func;GLInspector::checkEGLError();
- #define GL_CALL(func) func;GLInspector::checkGLError();
- #else
- #define EGL_CALL(func) func;
- #define GL_CALL(func) func;
- #endif
- namespace glcore
- {
- /**
- * OpenGL ES命令报错监视器
- * @author little fat sheep
- */
- class GLInspector
- {
- public:
- static void checkEGLError(const char* tag); // 检查EGL报错信息
- static void checkEGLError(); // 通用检查EGL错误
- static void printShaderInfoLog(GLuint shader, const char* tag); // 打印Shader错误日志
- static void printProgramInfoLog(GLuint program, const char* tag); // 打印Program错误日志
- static void checkGLError(const char* tag); // 检查GL报错信息
- static void checkGLError(); // 通用检查GL报错信息
- };
- } // namespace glcore
复制代码 gl_inspector.cpp- #include
- #include
- #include <string>
- #include "glcore/gl_inspector.h"
- #define LOG_TAG "Native: GLInspector"
- #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
- using namespace std;
- namespace glcore
- {
- void GLInspector::checkEGLError(const char *tag)
- {
- int error = eglGetError();
- if (error != EGL_SUCCESS) {
- LOGE("%s failed: 0x%x", tag, error);
- }
- }
- void GLInspector::checkEGLError()
- {
- GLenum errorCode = eglGetError();
- if (errorCode != EGL_SUCCESS) {
- string error;
- switch (errorCode)
- {
- case EGL_BAD_DISPLAY:
- error = "EGL_BAD_DISPLAY";
- break;
- case EGL_NOT_INITIALIZED:
- error = "EGL_NOT_INITIALIZED";
- break;
- case EGL_BAD_CONFIG:
- error = "EGL_BAD_CONFIG";
- break;
- case EGL_BAD_CONTEXT:
- error = "EGL_BAD_CONTEXT";
- break;
- case EGL_BAD_NATIVE_WINDOW:
- error = "EGL_BAD_NATIVE_WINDOW";
- break;
- case EGL_BAD_SURFACE:
- error = "EGL_BAD_SURFACE";
- break;
- case EGL_BAD_CURRENT_SURFACE:
- error = "EGL_BAD_CURRENT_SURFACE";
- break;
- case EGL_BAD_ACCESS:
- error = "EGL_BAD_ACCESS";
- break;
- case EGL_BAD_ALLOC:
- error = "EGL_BAD_ALLOC";
- break;
- case EGL_BAD_ATTRIBUTE:
- error = "EGL_BAD_ATTRIBUTE";
- break;
- case EGL_BAD_PARAMETER:
- error = "EGL_BAD_PARAMETER";
- break;
- case EGL_BAD_NATIVE_PIXMAP:
- error = "EGL_BAD_NATIVE_PIXMAP";
- break;
- case EGL_BAD_MATCH:
- error = "EGL_BAD_MATCH";
- break;
- case EGL_CONTEXT_LOST:
- error = "EGL_CONTEXT_LOST";
- break;
- default:
- error = "UNKNOW";
- break;
- }
- LOGE("checkEGLError failed: %s, 0x%x", error.c_str(), errorCode);
- assert(false);
- }
- }
- void GLInspector::printShaderInfoLog(GLuint shader, const char* tag)
- {
- char infoLog[512];
- glGetShaderInfoLog(shader, 512, nullptr, infoLog);
- LOGE("%s failed: %s", tag, infoLog);
- }
- void GLInspector::printProgramInfoLog(GLuint program, const char* tag)
- {
- char infoLog[512];
- glGetProgramInfoLog(program, 512, nullptr, infoLog);
- LOGE("%s failed: %s", tag, infoLog);
- }
- void GLInspector::checkGLError(const char *tag) {
- GLenum error = glGetError();
- if(error != GL_NO_ERROR) {
- LOGE("%s failed: 0x%x", tag, error);
- }
- }
- void GLInspector::checkGLError()
- {
- GLenum errorCode = glGetError();
- if (errorCode != GL_NO_ERROR) {
- string error;
- switch (errorCode)
- {
- case GL_INVALID_ENUM:
- error = "GL_INVALID_ENUM";
- break;
- case GL_INVALID_VALUE:
- error = "GL_INVALID_VALUE";
- break;
- case GL_INVALID_OPERATION:
- error = "GL_INVALID_OPERATION";
- break;
- case GL_INVALID_INDEX:
- error = "GL_INVALID_INDEX";
- break;
- case GL_INVALID_FRAMEBUFFER_OPERATION:
- error = "GL_INVALID_FRAMEBUFFER_OPERATION";
- break;
- case GL_OUT_OF_MEMORY:
- error = "GL_OUT_OF_MEMORY";
- break;
- default:
- error = "UNKNOW";
- break;
- }
- LOGE("checkGLError failed: %s, 0x%x", error.c_str(), errorCode);
- assert(false);
- }
- }
- } // namespace glcore
复制代码 2.6 EGLSurfaceView
EGLSurfaceView 主要承载了 EGL 环境搭建。EGL 详细介绍见 → 【OpenGL ES】EGL+FBO离屏渲染。
elg_surface_view.h- #include
- #include "glcore/application.h"
- #include "glcore/elg_surface_view.h"
- #include "glcore/gl_inspector.h"
- #define LOG_TAG "Native: EGLSurfaceView"
- #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
- namespace glcore
- {
- EGLSurfaceView::EGLSurfaceView()
- {
- LOGI("init");
- createDisplay();
- createConfig();
- createContext();
- }
- EGLSurfaceView::~EGLSurfaceView()
- {
- LOGI("destroy");
- if (m_renderer)
- {
- delete m_renderer;
- m_renderer = nullptr;
- }
- if (m_eglDisplay && m_eglDisplay != EGL_NO_DISPLAY)
- {
- // 与显示设备解绑
- EGL_CALL(eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
- // 销毁 EGLSurface
- if (m_eglSurface && m_eglSurface != EGL_NO_SURFACE)
- {
- EGL_CALL(eglDestroySurface(m_eglDisplay, m_eglSurface));
- delete &m_eglSurface;
- }
- // 销毁 EGLContext
- if (m_eglContext && m_eglContext != EGL_NO_CONTEXT)
- {
- EGL_CALL(eglDestroyContext(m_eglDisplay, m_eglContext));
- delete &m_eglContext;
- }
- // 销毁 EGLDisplay (显示设备)
- EGL_CALL(eglTerminate(m_eglDisplay));
- delete &m_eglDisplay;
- }
- delete app;
- }
- void EGLSurfaceView::setRenderer(Renderer *renderer)
- {
- LOGI("setRenderer");
- m_renderer = renderer;
- }
- bool EGLSurfaceView::surfaceCreated()
- {
- LOGI("createSurface");
- createSurface();
- makeCurrent();
- if (m_renderer && m_firstCreateSurface)
- {
- m_renderer->onSurfaceCreated();
- m_firstCreateSurface = false;
- }
- return true;
- }
- void EGLSurfaceView::surfaceChanged(int width, int height)
- {
- LOGI("surfaceChanged, width: %d, height: %d", width, height);
- app->resize(width, height);
- if (m_renderer)
- {
- m_renderer->onSurfaceChanged(width, height);
- }
- }
- void EGLSurfaceView::drawFrame()
- {
- if (!m_eglSurface || m_eglSurface == EGL_NO_SURFACE || !m_renderer)
- {
- return;
- }
- m_renderer->onDrawFrame();
- EGL_CALL(eglSwapBuffers(m_eglDisplay, m_eglSurface));
- }
- void EGLSurfaceView::surfaceDestroy()
- {
- LOGI("surfaceDestroy");
- if (m_eglDisplay && m_eglDisplay != EGL_NO_DISPLAY)
- {
- // 与显示设备解绑
- EGL_CALL(eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
- // 销毁 EGLSurface
- if (m_eglSurface && m_eglSurface != EGL_NO_SURFACE)
- {
- EGL_CALL(eglDestroySurface(m_eglDisplay, m_eglSurface));
- m_eglSurface = nullptr;
- }
- }
- app->releaseWindow();
- }
- // 1.创建EGLDisplay
- void EGLSurfaceView::createDisplay()
- {
- EGL_CALL(m_eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY));
- EGL_CALL(eglInitialize(m_eglDisplay, nullptr, nullptr));
- }
- // 2.创建EGLConfig
- void EGLSurfaceView::createConfig()
- {
- if (m_eglDisplay && m_eglDisplay != EGL_NO_DISPLAY)
- {
- const EGLint configAttrs[] = {
- EGL_RED_SIZE, 8,
- EGL_GREEN_SIZE, 8,
- EGL_BLUE_SIZE, 8,
- EGL_ALPHA_SIZE, 8,
- EGL_DEPTH_SIZE, 8,
- EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT,
- EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
- EGL_NONE
- };
- EGLint numConfigs;
- EGL_CALL(eglChooseConfig(m_eglDisplay, configAttrs, &m_eglConfig, 1, &numConfigs));
- }
- }
- // 3.创建EGLContext
- void EGLSurfaceView::createContext()
- {
- if (m_eglConfig)
- {
- const EGLint contextAttrs[] = {
- EGL_CONTEXT_CLIENT_VERSION, 3,
- EGL_NONE
- };
- EGL_CALL(m_eglContext = eglCreateContext(m_eglDisplay, m_eglConfig, EGL_NO_CONTEXT, contextAttrs));
- }
- }
- // 4.创建EGLSurface
- void EGLSurfaceView::createSurface()
- {
- if (m_eglContext && m_eglContext != EGL_NO_CONTEXT)
- {
- EGL_CALL(m_eglSurface = eglCreateWindowSurface(m_eglDisplay, m_eglConfig, app->getWindow(), nullptr));
- }
- }
- // 5.绑定EGLSurface和EGLContext到显示设备(EGLDisplay)
- void EGLSurfaceView::makeCurrent()
- {
- if (m_eglSurface && m_eglSurface != EGL_NO_SURFACE)
- {
- EGL_CALL(eglMakeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext));
- }
- }
- } // namespace glcore
复制代码 elg_surface_view.cpp- #include
- #include "glcore/application.h"
- #include "glcore/elg_surface_view.h"
- #include "glcore/gl_inspector.h"
- #define LOG_TAG "Native: EGLSurfaceView"
- #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
- namespace glcore
- {
- EGLSurfaceView::EGLSurfaceView()
- {
- LOGI("init");
- createDisplay();
- createConfig();
- createContext();
- }
- EGLSurfaceView::~EGLSurfaceView()
- {
- LOGI("destroy");
- if (m_renderer)
- {
- delete m_renderer;
- m_renderer = nullptr;
- }
- if (m_eglDisplay && m_eglDisplay != EGL_NO_DISPLAY)
- {
- // 与显示设备解绑
- EGL_CALL(eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
- // 销毁 EGLSurface
- if (m_eglSurface && m_eglSurface != EGL_NO_SURFACE)
- {
- EGL_CALL(eglDestroySurface(m_eglDisplay, m_eglSurface));
- //GLInspector::checkEGLConfig("eglDestroySurface");
- delete &m_eglSurface;
- }
- // 销毁 EGLContext
- if (m_eglContext && m_eglContext != EGL_NO_CONTEXT)
- {
- EGL_CALL(eglDestroyContext(m_eglDisplay, m_eglContext));
- //GLInspector::checkEGLConfig("eglDestroyContext");
- delete &m_eglContext;
- }
- // 销毁 EGLDisplay (显示设备)
- EGL_CALL(eglTerminate(m_eglDisplay));
- //GLInspector::checkEGLConfig("eglTerminate");
- delete &m_eglDisplay;
- }
- delete app;
- }
- void EGLSurfaceView::setRenderer(Renderer *renderer)
- {
- LOGI("setRenderer");
- m_renderer = renderer;
- }
- bool EGLSurfaceView::surfaceCreated()
- {
- LOGI("createSurface");
- createSurface();
- makeCurrent();
- if (m_renderer && m_firstCreateSurface)
- {
- m_renderer->onSurfaceCreated();
- m_firstCreateSurface = false;
- }
- return true;
- }
- void EGLSurfaceView::surfaceChanged(int width, int height)
- {
- LOGI("surfaceChanged, width: %d, height: %d", width, height);
- app->resize(width, height);
- if (m_renderer)
- {
- m_renderer->onSurfaceChanged(width, height);
- }
- }
- void EGLSurfaceView::drawFrame()
- {
- if (!m_eglSurface || m_eglSurface == EGL_NO_SURFACE || !m_renderer)
- {
- return;
- }
- m_renderer->onDrawFrame();
- EGL_CALL(eglSwapBuffers(m_eglDisplay, m_eglSurface));
- //GLInspector::checkEGLConfig("eglSwapBuffers");
- }
- void EGLSurfaceView::surfaceDestroy()
- {
- LOGI("surfaceDestroy");
- if (m_eglDisplay && m_eglDisplay != EGL_NO_DISPLAY)
- {
- // 与显示设备解绑
- EGL_CALL(eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
- // 销毁 EGLSurface
- if (m_eglSurface && m_eglSurface != EGL_NO_SURFACE)
- {
- EGL_CALL(eglDestroySurface(m_eglDisplay, m_eglSurface));
- //GLInspector::checkEGLConfig("eglDestroySurface");
- m_eglSurface = nullptr;
- }
- }
- app->releaseWindow();
- }
- // 1.创建EGLDisplay
- void EGLSurfaceView::createDisplay()
- {
- EGL_CALL(m_eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY));
- EGL_CALL(eglInitialize(m_eglDisplay, nullptr, nullptr));
- //GLInspector::checkEGLConfig("eglInitialize");
- }
- // 2.创建EGLConfig
- void EGLSurfaceView::createConfig()
- {
- if (m_eglDisplay && m_eglDisplay != EGL_NO_DISPLAY)
- {
- const EGLint configAttrs[] = {
- EGL_RED_SIZE, 8,
- EGL_GREEN_SIZE, 8,
- EGL_BLUE_SIZE, 8,
- EGL_ALPHA_SIZE, 8,
- EGL_DEPTH_SIZE, 8,
- EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT,
- EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
- EGL_NONE
- };
- EGLint numConfigs;
- EGL_CALL(eglChooseConfig(m_eglDisplay, configAttrs, &m_eglConfig, 1, &numConfigs));
- //GLInspector::checkEGLConfig("eglChooseConfig");
- }
- }
- // 3.创建EGLContext
- void EGLSurfaceView::createContext()
- {
- if (m_eglConfig)
- {
- const EGLint contextAttrs[] = {
- EGL_CONTEXT_CLIENT_VERSION, 3,
- EGL_NONE
- };
- EGL_CALL(m_eglContext = eglCreateContext(m_eglDisplay, m_eglConfig, EGL_NO_CONTEXT, contextAttrs));
- //GLInspector::checkEGLConfig("eglCreateContext");
- }
- }
- // 4.创建EGLSurface
- void EGLSurfaceView::createSurface()
- {
- if (m_eglContext && m_eglContext != EGL_NO_CONTEXT)
- {
- EGL_CALL(m_eglSurface = eglCreateWindowSurface(m_eglDisplay, m_eglConfig, app->getWindow(), nullptr));
- //GLInspector::checkEGLConfig("eglCreateWindowSurface");
- }
- }
- // 5.绑定EGLSurface和EGLContext到显示设备(EGLDisplay)
- void EGLSurfaceView::makeCurrent()
- {
- if (m_eglSurface && m_eglSurface != EGL_NO_SURFACE)
- {
- EGL_CALL(eglMakeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext));
- //GLInspector::checkEGLConfig("eglMakeCurrent");
- }
- }
- } // 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- #pragma once
- #include <map>
- #include "core_lib.h"
- using namespace std;
- namespace glcore
- {
- /**
- * 着色器程序
- * @author little fat sheep
- */
- class ShaderProgram
- {
- public:
- static constexpr char* ATTRIBUTE_POSITION = "a_position"; // 着色器中位置属性名
- static constexpr char* ATTRIBUTE_NORMAL = "a_normal"; // 着色器中位法线性名
- static constexpr char* ATTRIBUTE_COLOR = "a_color"; // 着色器中颜色属性名
- static constexpr char* ATTRIBUTE_TEXCOORD = "a_texCoord"; // 着色器中纹理坐标属性名
- static constexpr char* ATTRIBUTE_TANGENT = "a_tangent"; // 着色器中切线属性名
- static constexpr char* ATTRIBUTE_BINORMAL = "a_binormal"; // 着色器中副切线属性名
- static constexpr char* UNIFORM_TEXTURE = "u_texture"; // 着色器中纹理名
- static constexpr char* UNIFORM_VP = "u_projectionViewMatrix"; // 着色器中VP名
- private:
- GLuint m_program;
- map<const char*, int> m_attributes;
- map<const char*, int> m_uniforms;
- public:
- ShaderProgram(const char* vertexCode, const char* fragmentCode);
- ~ShaderProgram();
- void bind();
- GLuint getHandle() { return m_program; }
- // 操作attribute属性
- void enableVertexAttribArray(const char* name);
- void enableVertexAttribArray(int location);
- void setVertexAttribPointer(const char* name, int size, int type, bool normalize, int stride, int offset);
- void setVertexAttribPointer(int location, int size, int type, bool normalize, int stride, int offset);
- void disableVertexAttribArray(const char* name);
- void disableVertexAttribArray(int location);
- // 操作uniform属性
- void setUniformi(const char* name, int value);
- void setUniformi(int location, int value);
- void setUniformi(const char* name, int value1, int value2);
- void setUniformi(int location, int value1, int value2);
- void setUniformi(const char* name, int value1, int value2, int value3);
- void setUniformi(int location, int value1, int value2, int value3);
- void setUniformi(const char* name, int value1, int value2, int value3, int value4);
- void setUniformi(int location, int value1, int value2, int value3, int value4);
- void setUniformf(const char* name, float value);
- void setUniformf(int location, float value);
- void setUniformf(const char* name, float value1, float value2);
- void setUniformf(int location, float value1, float value2);
- void setUniformf(const char* name, float value1, float value2, int value3);
- void setUniformf(int location, float value1, float value2, int value3);
- void setUniformf(const char* name, float value1, float value2, int value3, int value4);
- void setUniformf(int location, float value1, float value2, int value3, int value4);
- void setUniform1fv(const char* name, int length, const float values[]);
- void setUniform1fv(int location, int count, float const values[]);
- void setUniform2fv(const char* name, int count, const float values[]);
- void setUniform2fv(int location, int count, const float values[]);
- void setUniform3fv(const char* name, int count, const float values[]);
- void setUniform3fv(int location, int count, const float values[]);
- void setUniform4fv(const char* name, int count, const float values[]);
- void setUniform4fv(int location, int count, const float values[]);
- void setUniformMatrix2fv(const char* name, int count, bool transpose, const float *value);
- void setUniformMatrix2fv(int location, int count, bool transpose, const float *value);
- void setUniformMatrix3fv(const char* name, int count, bool transpose, const float *value);
- void setUniformMatrix3fv(int location, int count, bool transpose, const float *value);
- void setUniformMatrix4fv(const char* name, int count, bool transpose, const float *value);
- void setUniformMatrix4fv(int location, int count, bool transpose, const float *value);
- int fetchAttributeLocation(const char* name);
- int fetchUniformLocation(const char* name);
- private:
- void compileShaders(const char* vertexCode, const char* fragmentCode);
- GLuint loadShader(GLenum type, const char* source);
- GLuint linkProgram(GLuint vertexShader, GLuint fragmentShader);
- };
- } // namespace glcore
复制代码 shader_program.cpp- #include
- #include "glcore/gl_inspector.h"
- #include "glcore/shader_program.h"
- #define LOG_TAG "Native: ShaderProgram"
- #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
- namespace glcore
- {
- ShaderProgram::ShaderProgram(const char* vertexCode, const char* fragmentCode)
- {
- compileShaders(vertexCode, fragmentCode);
- }
- ShaderProgram::~ShaderProgram()
- {
- if (m_program)
- {
- GL_CALL(glUseProgram(0));
- GL_CALL(glDeleteProgram(m_program));
- m_program = 0;
- }
- m_attributes.clear();
- m_uniforms.clear();
- }
- void ShaderProgram::bind()
- {
- GL_CALL(glUseProgram(m_program));
- }
- void ShaderProgram::enableVertexAttribArray(const char* name)
- {
- int location = fetchAttributeLocation(name);
- enableVertexAttribArray(location);
- }
- void ShaderProgram::enableVertexAttribArray(int location)
- {
- GL_CALL(glEnableVertexAttribArray(location));
- }
- void ShaderProgram::setVertexAttribPointer(const char *name, int size, int type, bool normalize, int stride, int offset)
- {
- int location = fetchAttributeLocation(name);
- setVertexAttribPointer(location, size, type, normalize, stride, offset);
- }
- void ShaderProgram::setVertexAttribPointer(int location, int size, int type, bool normalize, int stride, int offset)
- {
- GL_CALL(glVertexAttribPointer(location, size, type, normalize, stride, (void*) offset));
- }
- void ShaderProgram::disableVertexAttribArray(const char* name)
- {
- int location = fetchAttributeLocation(name);
- disableVertexAttribArray(location);
- }
- void ShaderProgram::disableVertexAttribArray(int location)
- {
- GL_CALL(glDisableVertexAttribArray(location));
- }
- void ShaderProgram::setUniformi(const char* name, int value)
- {
- int location = fetchUniformLocation(name);
- GL_CALL(glUniform1i(location, value));
- }
- void ShaderProgram::setUniformi(int location, int value)
- {
- GL_CALL(glUniform1i(location, value));
- }
- void ShaderProgram::setUniformi(const char* name, int value1, int value2)
- {
- int location = fetchUniformLocation(name);
- GL_CALL(glUniform2i(location, value1, value2));
- }
- void ShaderProgram::setUniformi(int location, int value1, int value2)
- {
- GL_CALL(glUniform2i(location, value1, value2));
- }
- void ShaderProgram::setUniformi(const char* name, int value1, int value2, int value3)
- {
- int location = fetchUniformLocation(name);
- GL_CALL(glUniform3i(location, value1, value2, value3));
- }
- void ShaderProgram::setUniformi(int location, int value1, int value2, int value3)
- {
- GL_CALL(glUniform3i(location, value1, value2, value3));
- }
- void ShaderProgram::setUniformi(const char* name, int value1, int value2, int value3, int value4)
- {
- int location = fetchUniformLocation(name);
- GL_CALL(glUniform4i(location, value1, value2, value3, value4));
- }
- void ShaderProgram::setUniformi(int location, int value1, int value2, int value3, int value4)
- {
- GL_CALL(glUniform4i(location, value1, value2, value3, value4));
- }
- void ShaderProgram::setUniformf(const char* name, float value)
- {
- int location = fetchUniformLocation(name);
- GL_CALL(glUniform1f(location, value));
- }
- void ShaderProgram::setUniformf(int location, float value)
- {
- GL_CALL(glUniform1f(location, value));
- }
- void ShaderProgram::setUniformf(const char* name, float value1, float value2)
- {
- int location = fetchUniformLocation(name);
- GL_CALL(glUniform2f(location, value1, value2));
- }
- void ShaderProgram::setUniformf(int location, float value1, float value2)
- {
- GL_CALL(glUniform2f(location, value1, value2));
- }
- void ShaderProgram::setUniformf(const char* name, float value1, float value2, int value3)
- {
- int location = fetchUniformLocation(name);
- GL_CALL(glUniform3f(location, value1, value2, value3));
- }
- void ShaderProgram::setUniformf(int location, float value1, float value2, int value3)
- {
- GL_CALL(glUniform3f(location, value1, value2, value3));
- }
- void ShaderProgram::setUniformf(const char* name, float value1, float value2, int value3, int value4)
- {
- int location = fetchUniformLocation(name);
- GL_CALL(glUniform4f(location, value1, value2, value3, value4));
- }
- void ShaderProgram::setUniformf(int location, float value1, float value2, int value3, int value4)
- {
- GL_CALL(glUniform4f(location, value1, value2, value3, value4));
- }
- void ShaderProgram::setUniform1fv(const char* name, int count, const float values[])
- {
- int location = fetchUniformLocation(name);
- GL_CALL(glUniform1fv(location, count, values));
- }
- void ShaderProgram::setUniform1fv(int location, int count, const float values[])
- {
- GL_CALL(glUniform1fv(location, count, values));
- }
- void ShaderProgram::setUniform2fv(const char* name, int count, const float values[])
- {
- int location = fetchUniformLocation(name);
- GL_CALL(glUniform2fv(location, count / 2, values));
- }
- void ShaderProgram::setUniform2fv(int location, int count, const float values[])
- {
- GL_CALL(glUniform2fv(location, count / 2, values));
- }
- void ShaderProgram::setUniform3fv(const char* name, int count, const float values[])
- {
- int location = fetchUniformLocation(name);
- GL_CALL(glUniform3fv(location, count / 3, values));
- }
- void ShaderProgram::setUniform3fv(int location, int count, const float values[])
- {
- GL_CALL(glUniform3fv(location, count / 3, values));
- }
- void ShaderProgram::setUniform4fv(const char* name, int count, const float values[])
- {
- int location = fetchUniformLocation(name);
- GL_CALL(glUniform4fv(location, count / 4, values));
- }
- void ShaderProgram::setUniform4fv(int location, int count, const float values[])
- {
- GL_CALL(glUniform4fv(location, count / 4, values));
- }
- void ShaderProgram::setUniformMatrix2fv(const char* name, int count, bool transpose, const float *value)
- {
- int location = fetchUniformLocation(name);
- GL_CALL(glUniformMatrix2fv(location, count, transpose, value));
- }
- void ShaderProgram::setUniformMatrix2fv(int location, int count, bool transpose, const float *value)
- {
- GL_CALL(glUniformMatrix2fv(location, count, transpose, value));
- }
- void ShaderProgram::setUniformMatrix3fv(const char* name, int count, bool transpose, const float *value)
- {
- int location = fetchUniformLocation(name);
- GL_CALL(glUniformMatrix3fv(location, count, transpose, value));
- }
- void ShaderProgram::setUniformMatrix3fv(int location, int count, bool transpose, const float *value)
- {
- GL_CALL(glUniformMatrix3fv(location, count, transpose, value));
- }
- void ShaderProgram::setUniformMatrix4fv(const char* name, int count, bool transpose, const float *value)
- {
- int location = fetchUniformLocation(name);
- GL_CALL(glUniformMatrix4fv(location, count, transpose, value));
- }
- void ShaderProgram::setUniformMatrix4fv(int location, int count, bool transpose, const float *value)
- {
- GL_CALL(glUniformMatrix4fv(location, count, transpose, value));
- }
- int ShaderProgram::fetchAttributeLocation(const char* name)
- {
- int location;
- auto it = m_attributes.find(name);
- if (it == m_attributes.end())
- {
- GL_CALL(location = glGetAttribLocation(m_program, name));
- if (location == -1) {
- LOGI("no attribute: %s", name);
- //GLInspector::printProgramInfoLog(m_program, "fetchAttributeLocation");
- return -1;
- }
- m_attributes[name] = location;
- }
- else
- {
- location = it->second;
- }
- return location;
- }
- int ShaderProgram::fetchUniformLocation(const char* name)
- {
- int location;
- auto it = m_uniforms.find(name);
- if (it == m_uniforms.end())
- {
- GL_CALL(location = glGetUniformLocation(m_program, name));
- if (location == -1) {
- LOGI("no uniform: %s", name);
- //GLInspector::printProgramInfoLog(m_program, "fetchUniformLocation");
- return -1;
- }
- m_uniforms[name] = location;
- }
- else
- {
- location = it->second;
- }
- return location;
- }
- void ShaderProgram::compileShaders(const char* vertexCode, const char* fragmentCode)
- {
- GLuint vertexShader = loadShader(GL_VERTEX_SHADER, vertexCode);
- GLuint fragmentShader = loadShader(GL_FRAGMENT_SHADER, fragmentCode);
- m_program = linkProgram(vertexShader, fragmentShader);
- }
- GLuint ShaderProgram::loadShader(GLenum type, const char* source)
- {
- GL_CALL(GLuint shader = glCreateShader(type));
- GL_CALL(glShaderSource(shader, 1, &source, nullptr));
- GL_CALL(glCompileShader(shader));
- GLint success;
- glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
- if (!success) {
- GLInspector::printShaderInfoLog(shader, "loadShader");
- return 0;
- }
- return shader;
- }
- GLuint ShaderProgram::linkProgram(GLuint vertexShader, GLuint fragmentShader)
- {
- GL_CALL(GLuint program = glCreateProgram());
- GL_CALL(glAttachShader(program, vertexShader));
- GL_CALL(glAttachShader(program, fragmentShader));
- GL_CALL(glLinkProgram(program));
- GLint success;
- glGetProgramiv(program, GL_LINK_STATUS, &success);
- if (!success) {
- GLInspector::printProgramInfoLog(m_program, "linkProgram");
- }
- GL_CALL(glDeleteShader(vertexShader));
- GL_CALL(glDeleteShader(fragmentShader));
- return program;
- }
- } // namespace glcore
复制代码 2.8 VBO
VBO 是 Vertex Buffer Object 的简称,即顶点缓冲对象,作用是缓存顶点数据到显存中,避免频繁调用 glVertexAttribPointer 传输顶点数据,减少 CPU 到 GPU 的数据传输,提高渲染效率。
顶点属性主要有位置、颜色、纹理坐标、法线、切线、副切线等,每个属性又有属性标识、维数、是否已标准化、数据类型、偏移、别名、纹理单元等。
由于 VBO 中有多个属性数据,每个属性有多个字段,笔者除了封装 VertexBufferObject 类,还封装了 VertexAttributes 和 VertexAttribute 两个类。VertexAttribute 是属性描述类,VertexAttributes 是属性描述集合。
vertex_buffer_object.h- #pragma once
- #include <initializer_list>
- #include <vector>
- #include "core_lib.h"
- #include "shader_program.h"
- #include "vertex_attributes.h"
- #include "vertex_attribute.h"
- #include "vertex_attributes.h"
- using namespace std;
- namespace glcore
- {
- /**
- * 顶点属性缓冲对象 (简称VBO)
- * @author little fat sheep
- */
- class VertexBufferObject
- {
- protected:
- bool m_isBound = false; // 是否已绑定到VBO (或VAO)
- bool m_isDirty = false; // 是否有脏数据 (缓存的数据需要更新)
- private:
- GLuint m_vboHandle; // VBO句柄
- VertexAttributes* m_attributes; // 顶点属性
- GLuint m_usage; // GL_STATIC_DRAW 或 GL_DYNAMIC_DRAW
- const float* m_vertices; // 顶点属性数据
- int m_vertexNum = 0; // 顶点个数
- int m_bytes = 0; // 顶点属性字节数
- public:
- VertexBufferObject(bool isStatic, initializer_list<VertexAttribute*> attributes);
- VertexBufferObject(bool isStatic, VertexAttributes* attributes);
- virtual ~VertexBufferObject();
- void setVertices(float* vertices, int bytes);
- void bind(ShaderProgram* shader);
- virtual void bind(ShaderProgram* shader, int* locations);
- void unbind(ShaderProgram* shader);
- virtual void unbind(ShaderProgram* shader, int* locations);
- int getNumVertices() { return m_vertexNum; }
- private:
- void applyBufferData(); // 缓存数据
- };
- } // namespace glcore
复制代码 vertex_buffer_object.cpp- #include
- #include "glcore/gl_inspector.h"
- #include "glcore/vertex_buffer_object.h"
- #define LOG_TAG "Native: VertexBufferObject"
- #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
- namespace glcore
- {
- VertexBufferObject::VertexBufferObject(bool isStatic, initializer_list<VertexAttribute*> attributes):
- VertexBufferObject(isStatic, new VertexAttributes(attributes))
- {
- }
- VertexBufferObject::VertexBufferObject(bool isStatic, VertexAttributes* attributes):
- m_attributes(attributes)
- {
- m_usage = isStatic ? GL_STATIC_DRAW : GL_DYNAMIC_DRAW;
- GL_CALL(glGenBuffers(1, &m_vboHandle));
- LOGI("init: %d", m_vboHandle);
- }
- VertexBufferObject::~VertexBufferObject()
- {
- LOGI("destroy");
- GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, 0));
- GL_CALL(glDeleteBuffers(1, &m_vboHandle));
- m_vboHandle = 0;
- delete m_attributes;
- delete[] m_vertices;
- }
- void VertexBufferObject::setVertices(float* vertices, int bytes)
- {
- m_vertices = vertices;
- m_vertexNum = bytes / m_attributes->vertexSize;
- m_bytes = bytes;
- m_isDirty = true;
- if (m_isBound)
- {
- applyBufferData();
- }
- }
- void VertexBufferObject::bind(ShaderProgram* shader)
- {
- bind(shader, nullptr);
- }
- void VertexBufferObject::bind(ShaderProgram* shader, int* locations)
- {
- GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, m_vboHandle));
- if (m_isDirty)
- {
- applyBufferData();
- }
- if (locations == nullptr)
- {
- for (int i = 0; i < m_attributes->size(); i++)
- {
- VertexAttribute* attribute = m_attributes->get(i);
- shader->enableVertexAttribArray(attribute->alias);
- shader->setVertexAttribPointer(attribute->alias, attribute->numComponents,
- attribute->type, attribute->normalized, m_attributes->vertexSize,
- attribute->offset);
- }
- }
- else
- {
- for (int i = 0; i < m_attributes->size(); i++)
- {
- VertexAttribute* attribute = m_attributes->get(i);
- shader->enableVertexAttribArray(locations[i]);
- shader->setVertexAttribPointer(locations[i], attribute->numComponents,
- attribute->type, attribute->normalized, m_attributes->vertexSize,
- attribute->offset);
- }
- }
- m_isBound = true;
- }
- void VertexBufferObject::unbind(ShaderProgram* shader)
- {
- unbind(shader, nullptr);
- }
- void VertexBufferObject::unbind(ShaderProgram* shader, int* locations)
- {
- if (locations == nullptr)
- {
- for (int i = 0; i < m_attributes->size(); i++)
- {
- shader->disableVertexAttribArray(m_attributes->get(i)->alias);
- }
- }
- else
- {
- for (int i = 0; i < m_attributes->size(); i++)
- {
- shader->disableVertexAttribArray(locations[i]);
- }
- }
- m_isBound = false;
- }
- void VertexBufferObject::applyBufferData()
- {
- GL_CALL(glBufferData(GL_ARRAY_BUFFER, m_bytes, m_vertices, m_usage));
- //GLInspector::checkGLError("vbo: applyBufferData");
- m_isDirty = false;
- }
- } // namespace glcore
复制代码 vertex_attributes.h- #pragma once
- #include <initializer_list>
- #include <vector>
- #include "vertex_attribute.h"
- using namespace std;
- namespace glcore
- {
- /**
- * 顶点属性集(位置、颜色、纹理坐标、法线、切线、副切线等中的一部分)
- * 每个顶点属性可以看作一个通道, 这个通道可能是多维的, 每个维度可能是多字节的
- * @author little fat sheep
- */
- class VertexAttributes
- {
- public:
- int vertexSize; // 所有顶点属性的字节数
- private:
- vector<VertexAttribute*> m_attributes; // 顶点属性列表
- public:
- VertexAttributes(initializer_list<VertexAttribute*> attributes);
- ~VertexAttributes();
- VertexAttribute* get(int index); // 根据索引获取属性
- int size(); // 获取属性个数
- private:
- int calculateOffsets(); // 计算偏移
- };
- /**
- * 顶点属性标识
- * @author little fat sheep
- */
- class Usage {
- public:
- static const int Position = 1;
- static const int ColorUnpacked = 2;
- static const int ColorPacked = 4;
- static const int Normal = 8;
- static const int TextureCoordinates = 16;
- static const int Tangent = 32;
- static const int BiNormal = 64;
- };
- } // namespace glcore
复制代码 vertex_attributes.cpp- #include "glcore/vertex_attributes.h"
- namespace glcore
- {
- VertexAttributes::VertexAttributes(initializer_list<VertexAttribute*> attributes):
- m_attributes(attributes)
- {
- vertexSize = calculateOffsets();
- }
- VertexAttributes::~VertexAttributes()
- {
- m_attributes.clear();
- }
- VertexAttribute* VertexAttributes::get(int index)
- {
- if (index >= 0 && index < m_attributes.size())
- {
- return m_attributes[index];
- }
- return nullptr;
- }
- int VertexAttributes::size()
- {
- return m_attributes.size();
- }
- int VertexAttributes::calculateOffsets() {
- int count = 0;
- for (VertexAttribute* attribute : m_attributes) {
- attribute->offset = count;
- count += attribute->getSizeInBytes();
- }
- return count;
- }
- } // namespace glcore
复制代码 vertex_attribute.h- #pragma once
- namespace glcore
- {
- /**
- * 单个顶点属性(位置、颜色、纹理坐标、法线、切线、副切线等中的一个)
- * 每个顶点属性可以看作一个通道, 这个通道可能是多维的, 每个维度可能是多字节的
- * @author little fat sheep
- */
- class VertexAttribute
- {
- public:
- int usage; // 顶点属性标识
- int numComponents; // 顶点属性维数 (如顶点坐标属性是3维的, 纹理坐标是2维的)
- bool normalized; // 顶点属性是否已经标准化 (有符号: -1~1, 无符号: 0~1)
- int type; // 顶点属性的变量类型 (GL_FLOAT、GL_UNSIGNED_BYTE等)
- int offset; // 顶点属性在字节上的偏移
- const char* alias; // 顶点属性别名 (着色器中变量名)
- int unit; // 纹理单元 (可能有多个纹理, 可选)
- public:
- VertexAttribute(int usage, int numComponents, const char* alias);
- VertexAttribute(int usage, int numComponents, const char* alias, int unit);
- VertexAttribute(int usage, int numComponents, int type, bool normalized, const char* alias);
- VertexAttribute(int usage, int numComponents, int type, bool normalized, const char* alias, int unit);
- ~VertexAttribute();
- static VertexAttribute* Position(); // 位置参数信息
- static VertexAttribute* TexCoords(int unit); // 纹理坐标参数信息
- static VertexAttribute* Normal(); // 法线参数信息
- static VertexAttribute* ColorPacked(); // 颜色参数信息
- static VertexAttribute* ColorUnpacked(); // 颜色参数信息
- static VertexAttribute* Tangent(); // 切线参数信息
- static VertexAttribute* Binormal(); // 副切线参数信息
- int getSizeInBytes(); // 属性对应的字节数
- private:
- void create(int usage, int numComponents, int type, bool normalized, const char* alias, int unit);
- };
- } // namespace glcore
复制代码 vertex_attribute.cpp- #include
- #include <string>
- #include "glcore/shader_program.h"
- #include "glcore/vertex_attribute.h"
- #include "glcore/vertex_attributes.h"
- #define LOG_TAG "Native: VertexAttribute"
- #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
- using namespace std;
- namespace glcore
- {
- VertexAttribute::VertexAttribute(int usage, int numComponents, const char* alias):
- VertexAttribute(usage, numComponents, alias, 0)
- {
- }
- VertexAttribute::VertexAttribute(int usage, int numComponents, const char* alias, int unit)
- {
- int type = usage == Usage::ColorPacked ? GL_UNSIGNED_BYTE : GL_FLOAT;
- bool normalized = usage == Usage::ColorPacked;
- create(usage, numComponents, type, normalized, alias, unit);
- }
- VertexAttribute::VertexAttribute(int usage, int numComponents, int type, bool normalized, const char* alias)
- {
- create(usage, numComponents, type, normalized, alias, 0);
- }
- VertexAttribute::VertexAttribute(int usage, int numComponents, int type, bool normalized, const char* alias, int unit)
- {
- create(usage, numComponents, type, normalized, alias, unit);
- }
- VertexAttribute::~VertexAttribute()
- {
- free((void*)alias);
- }
- VertexAttribute* VertexAttribute::Position() {
- return new VertexAttribute(Usage::Position, 3, ShaderProgram::ATTRIBUTE_POSITION);
- }
- VertexAttribute* VertexAttribute::TexCoords(int unit) {
- string str = string(ShaderProgram::ATTRIBUTE_TEXCOORD) + to_string(unit);
- // 复制字符串, 避免str被回收导致悬垂指针问题, 通过free((void*)alias)释放内存
- const char* combined = strdup(str.c_str());
- return new VertexAttribute(Usage::TextureCoordinates, 2, combined, unit);
- }
- VertexAttribute* VertexAttribute::Normal() {
- return new VertexAttribute(Usage::Normal, 3, ShaderProgram::ATTRIBUTE_NORMAL);
- }
- VertexAttribute* VertexAttribute::ColorPacked() {
- return new VertexAttribute(Usage::ColorPacked, 4, GL_UNSIGNED_BYTE, true, ShaderProgram::ATTRIBUTE_COLOR);
- }
- VertexAttribute* VertexAttribute::ColorUnpacked() {
- return new VertexAttribute(Usage::ColorUnpacked, 4, GL_FLOAT, false, ShaderProgram::ATTRIBUTE_COLOR);
- }
- VertexAttribute* VertexAttribute::Tangent() {
- return new VertexAttribute(Usage::Tangent, 3, ShaderProgram::ATTRIBUTE_TANGENT);
- }
- VertexAttribute* VertexAttribute::Binormal() {
- return new VertexAttribute(Usage::BiNormal, 3, ShaderProgram::ATTRIBUTE_BINORMAL);
- }
- int VertexAttribute::getSizeInBytes()
- {
- switch (type) {
- case GL_FLOAT:
- case GL_FIXED:
- return 4 * numComponents;
- case GL_UNSIGNED_BYTE:
- case GL_BYTE:
- return numComponents;
- case GL_UNSIGNED_SHORT:
- case GL_SHORT:
- return 2 * numComponents;
- }
- return 0;
- }
- void VertexAttribute::create(int usage, int numComponents, int type, bool normalized, const char* alias, int unit)
- {
- this->usage = usage;
- this->numComponents = numComponents;
- this->type = type;
- this->normalized = normalized;
- this->alias = alias;
- this->unit = unit;
- LOGI("create, alias: %s", alias);
- }
- } // namespace glcore
复制代码 2.9 VAO
VAO 是 Vertex Array Object 的简称,即顶点数组对象,作用是缓存顶点属性的指针和描述(或格式)信息,简化顶点属性设置的流程,避免频繁调用 glVertexAttribPointer 设置属性描述(或格式)信息,减少 CPU 与 GPU 的交互,提高渲染效率。
vertex_buffer_object_with_vao.h- #pragma once
- #include <initializer_list>
- #include "core_lib.h"
- #include "vertex_buffer_object.h"
- namespace glcore
- {
- /**
- * 携带VAO的顶点属性缓冲对象
- * @author little fat sheep
- */
- class VertexBufferObjectWithVAO : public VertexBufferObject
- {
- private:
- GLuint m_vaoHandle; // VAO句柄
- public:
- VertexBufferObjectWithVAO(bool isStatic, initializer_list<VertexAttribute*> attributes);
- VertexBufferObjectWithVAO(bool isStatic, VertexAttributes* attributes);
- ~VertexBufferObjectWithVAO() override;
- void bind(ShaderProgram* shader, int* locations) override;
- void unbind(ShaderProgram* shader, int* locations) override;
- };
- } // namespace glcore
复制代码 vertex_buffer_object_with_vao.cpp- #include
- #include "glcore/gl_inspector.h"
- #include "glcore/vertex_buffer_object_with_vao.h"
- #define LOG_TAG "Native: VertexBufferObjectWithVAO"
- #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
- namespace glcore
- {
- VertexBufferObjectWithVAO::VertexBufferObjectWithVAO(bool isStatic,
- initializer_list<VertexAttribute*> attributes):
- VertexBufferObjectWithVAO(isStatic, new VertexAttributes(attributes))
- {
- }
- VertexBufferObjectWithVAO::VertexBufferObjectWithVAO(bool isStatic, VertexAttributes* attributes):
- VertexBufferObject(isStatic, attributes)
- {
- GL_CALL(glGenVertexArrays(1, &m_vaoHandle));
- LOGI("init: %d", m_vaoHandle);
- }
- VertexBufferObjectWithVAO::~VertexBufferObjectWithVAO()
- {
- LOGI("destroy");
- GL_CALL(glDeleteVertexArrays(1, &m_vaoHandle));
- }
- void VertexBufferObjectWithVAO::bind(ShaderProgram* shader, int* locations)
- {
- GL_CALL(glBindVertexArray(m_vaoHandle));
- if (m_isDirty)
- {
- VertexBufferObject::bind(shader, locations);
- }
- m_isBound = true;
- }
- void VertexBufferObjectWithVAO::unbind(ShaderProgram* shader, int* locations)
- {
- GL_CALL(glBindVertexArray(0));
- m_isBound = false;
- }
- } // 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- #pragma once
- #include "core_lib.h"
- namespace glcore
- {
- /**
- * 顶点索引缓冲对象 (简称IBO)
- * @author little fat sheep
- */
- class IndexBufferObject
- {
- private:
- GLuint m_iboHandle; // IBO句柄
- GLuint m_usage; // GL_STATIC_DRAW 或 GL_DYNAMIC_DRAW
- GLenum m_type = GL_UNSIGNED_SHORT; // 索引数据类型 (GL_UNSIGNED_SHORT 或 GL_UNSIGNED_INT)
- const void* m_indices; // 顶点索引数据(short*或int*类型)
- int m_indexNum = 0; // 索引个数
- int m_bytes = 0; // 顶点索引字节数
- bool m_isDirty = false; // 是否有脏数据 (缓存的数据需要更新)
- bool m_isBound = false; // 是否已绑定到IBO
- public:
- IndexBufferObject(bool isStatic);
- IndexBufferObject(bool isStatic, GLenum type);
- ~IndexBufferObject();
- void setIndices (void* indices, int bytes);
- void setIndices (void* indices, int bytes, GLenum type);
- void bind();
- void unbind();
- int getNumIndices() { return m_indexNum; }
- GLenum getType() { return m_type; }
- private:
- void applyBufferData(); // 缓存数据
- int getTypeSize(); // 获取type对应的字节数
- };
- } // namespace glcore
复制代码 index_buffer_object.cpp- #include
- #include "glcore/gl_inspector.h"
- #include "glcore/index_buffer_object.h"
- #define LOG_TAG "Native: IndexBufferObject"
- #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
- namespace glcore
- {
- IndexBufferObject::IndexBufferObject(bool isStatic):
- IndexBufferObject(isStatic, GL_UNSIGNED_SHORT)
- {
- }
- IndexBufferObject::IndexBufferObject(bool isStatic, GLenum type)
- {
- m_usage = isStatic ? GL_STATIC_DRAW : GL_DYNAMIC_DRAW;
- m_type = type;
- GL_CALL(glGenBuffers(1, &m_iboHandle));
- LOGI("init: %d", m_iboHandle);
- }
- IndexBufferObject::~IndexBufferObject()
- {
- LOGI("destroy");
- GL_CALL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
- GL_CALL(glDeleteBuffers(1, &m_iboHandle));
- m_iboHandle = 0;
- delete[] m_indices;
- }
- void IndexBufferObject::setIndices(void* indices, int bytes)
- {
- setIndices(indices, bytes, m_type);
- }
- void IndexBufferObject::setIndices(void* indices, int bytes, GLenum type)
- {
- m_indices = indices;
- m_type = type;
- m_indexNum = bytes > 0 ? bytes / getTypeSize() : 0;
- m_bytes = bytes;
- m_isDirty = true;
- if (m_isBound)
- {
- applyBufferData();
- }
- }
- void IndexBufferObject::bind()
- {
- GL_CALL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_iboHandle));
- if (m_isDirty)
- {
- applyBufferData();
- }
- m_isBound = true;
- }
- void IndexBufferObject::unbind()
- {
- GL_CALL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
- m_isBound = false;
- }
- void IndexBufferObject::applyBufferData()
- {
- GL_CALL(glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_bytes, m_indices, m_usage));
- //GLInspector::checkGLError("ibo: applyBufferData");
- m_isDirty = false;
- }
- int IndexBufferObject::getTypeSize() {
- switch (m_type) {
- case GL_UNSIGNED_SHORT:
- return 2;
- case GL_UNSIGNED_INT:
- return 4;
- }
- return 2;
- }
- } // namespace glcore
复制代码 2.11 Mesh
Mesh 是网格类,用于管理顶点数据、索引、描述(或格式)等信息,由于 VBO 管理了顶点数据、IBO 管理了顶点索引、VAO 管理了顶点描述(或格式),因此 Mesh 只需管理 VBO、IBO、VAO。另外 IBO 和 VAO 是可选的,Mesh 中需要根据用户的行为调整渲染指令。
为方便用户快速创建平面网格,笔者提供了 MeshUtils 类,用户也可以根据该类提供的模板创建自己的网格。
mesh.h- #pragma once
- #include <initializer_list>
- #include "core_lib.h"
- #include "index_buffer_object.h"
- #include "shader_program.h"
- #include "vertex_buffer_object.h"
- #include "vertex_attribute.h"
- #include "vertex_attributes.h"
- using namespace std;
- namespace glcore
- {
- /**
- * 网格
- * @author little fat sheep
- */
- class Mesh
- {
- private:
- VertexBufferObject* m_vbo; // 顶点属性缓冲对象
- IndexBufferObject* m_ibo; // 顶点索引缓冲对象
- GLenum m_mode = GL_TRIANGLES; // 渲染模式 (GL_TRIANGLES、GL_TRIANGLE_STRIP、GL_TRIANGLE_FAN等)
- public:
- Mesh(bool isStatic, initializer_list<VertexAttribute*> attributes);
- Mesh(bool isStatic, VertexAttributes* attributes);
- Mesh(bool useVao, bool isStatic, initializer_list<VertexAttribute*> attributes);
- Mesh(bool useVao, bool isStatic, VertexAttributes* attributes);
- ~Mesh();
- void setVertices(float* vertices, int bytes); // 设置顶点属性
- void setIndices(void* indices, int bytes); // 设置顶点索引
- void setIndices(void* indices, int bytes, GLenum type); // 设置顶点索引
- void setMode(GLenum mode); // 设置渲染模式
- void render(ShaderProgram* shader); // 渲染
- };
- } // namespace glcore
复制代码 mesh.cpp- #include "glcore/gl_inspector.h"
- #include "glcore/mesh.h"
- #include "glcore/vertex_buffer_object_with_vao.h"
- namespace glcore
- {
- Mesh::Mesh(bool isStatic, initializer_list<VertexAttribute*> attributes):
- Mesh(true, isStatic, new VertexAttributes(attributes))
- {
- }
- Mesh::Mesh(bool isStatic, VertexAttributes* attributes):
- Mesh(true, isStatic, attributes)
- {
- }
- Mesh::Mesh(bool useVao, bool isStatic, initializer_list<VertexAttribute*> attributes):
- Mesh(useVao, isStatic, new VertexAttributes(attributes))
- {
- }
- Mesh::Mesh(bool useVao, bool isStatic, VertexAttributes* attributes)
- {
- m_vbo = useVao ? new VertexBufferObjectWithVAO(isStatic, attributes) :
- new VertexBufferObject(isStatic, attributes);
- m_ibo = new IndexBufferObject(isStatic);
- }
- Mesh::~Mesh()
- {
- delete m_vbo;
- delete m_ibo;
- }
- void Mesh::setVertices(float* vertices, int bytes)
- {
- m_vbo->setVertices(vertices, bytes);
- }
- void Mesh::setIndices(void* indices, int bytes)
- {
- m_ibo->setIndices(indices, bytes);
- }
- void Mesh::setIndices(void* indices, int bytes, GLenum type)
- {
- m_ibo->setIndices(indices, bytes, type);
- }
- void Mesh::setMode(GLenum mode)
- {
- m_mode = mode;
- }
- void Mesh::render(ShaderProgram* shader)
- {
- m_vbo->bind(shader);
- if (m_ibo->getNumIndices() > 0) {
- m_ibo->bind();
- GL_CALL(glDrawElements(m_mode, m_ibo->getNumIndices(), m_ibo->getType(), nullptr));
- m_ibo->unbind();
- } else {
- GL_CALL(glDrawArrays(m_mode, 0, m_vbo->getNumVertices()));
- }
- m_vbo->unbind(shader);
- }
- } // namespace glcore
复制代码 mesh_utils.h- #pragma once
- #include "mesh.h"
- namespace glcore
- {
- /**
- * 网格工具类
- * @author little fat sheep
- */
- class MeshUtils
- {
- public:
- static Mesh* createRect(bool reverse);
- private:
- static float* getRectVertices(bool reverse);
- };
- } // namespace glcore
复制代码 mesh_utils.cpp- #include "glcore/mesh_utils.h"
- namespace glcore
- {
- Mesh* MeshUtils::createRect(bool reverse)
- {
- Mesh* mesh = new Mesh(true, {
- VertexAttribute::Position(),
- VertexAttribute::TexCoords(0)
- });
- float* vertices = getRectVertices(reverse);
- mesh->setVertices(vertices, 4 * 5 * sizeof(float));
- void* indices = new short[] { 0, 1, 2, 2, 3, 0 };
- mesh->setIndices(indices, 6 * sizeof(short));
- return mesh;
- }
- float* MeshUtils::getRectVertices(bool reverse)
- {
- if (reverse) {
- return new float[] { // 中间渲染(FBO)使用
- -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, // 左下
- 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, // 右下
- 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, // 右上
- -1.0f, 1.0f, 0.0f, 0.0f, 1.0f // 左上
- };
- }
- return new float[] { // 终端渲染使用
- -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, // 左下
- 1.0f, -1.0f, 0.0f, 1.0f, 1.0f, // 右下
- 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, // 右上
- -1.0f, 1.0f, 0.0f, 0.0f, 0.0f // 左上
- };
- }
- } // namespace glcore
复制代码 2.12 GLTexture
封装 GLTexture 类是了方便用户进行纹理贴图。为了方便管理多渲染目标图层,定义了 TextureAction 接口,并提供 bind 函数,GLTexture、FBO 都继承了 TextureAction,用户自定义的渲染器或特效类也可以继承 TextureAction,将它们统一视为纹理活动(可绑定),这在特效叠加(或后处理)中非常有用,易于扩展。
texture_action.h- #pragma once
- #include "core_lib.h"
- #include "shader_program.h"
- namespace glcore
- {
- /**
- * 纹理活动 (纹理绑定、FBO绑定)
- * @author little fat sheep
- */
- class TextureAction
- {
- public:
- virtual ~TextureAction() = default;
- virtual void setTexParameter(GLint filter, GLint wrap) {}
- virtual void setBindParameter(char* alias, GLenum unit) {}
- virtual void bind(ShaderProgram* shader) = 0;
- };
- } // namespace glcore
复制代码 gl_texture.h- #pragma once
- #include "core_lib.h"
- #include "shader_program.h"
- #include "texture_action.h"
- namespace glcore
- {
- /**
- * 纹理贴图
- * @author little fat sheep
- */
- class GLTexture: public TextureAction
- {
- private:
- GLuint m_textureHandle = 0; // 纹理句柄
- int m_width = 0; // 纹理宽度
- int m_height = 0; // 纹理高度
- GLint m_filter = GL_LINEAR; // 滤波方式
- GLint m_wrap = GL_CLAMP_TO_EDGE; // 环绕方式
- const char* m_alias = ShaderProgram::UNIFORM_TEXTURE; // 纹理别名(着色器中变量名)
- GLenum m_unit = 0; // 纹理单元 (可能有多个纹理)
- bool m_isDirty = false; // 是否有脏数据 (纹理参数需要更新)
- public:
- GLTexture(int width, int height);
- GLTexture(void *buffer, int width, int height);
- ~GLTexture() override;
- void setTexture(const void *buffer);
- void setTexParameter(GLint filter, GLint wrap) override;
- void setBindParameter(char* alias, GLenum unit) override;
- void bind(ShaderProgram* shader) override;
- int getWidth() { return m_width; }
- int getHeight() { return m_height; }
- private:
- void applyTexParameter();
- };
- } // namespace glcore
复制代码 gl_texture.cpp- #include "glcore/gl_inspector.h"
- #include "glcore/gl_texture.h"
- namespace glcore
- {
- GLTexture::GLTexture(int width, int height):
- m_width(width),
- m_height(height)
- {
- }
- GLTexture::GLTexture(void *buffer, int width, int height): GLTexture(width, height)
- {
- setTexture(buffer);
- }
- GLTexture::~GLTexture()
- {
- GL_CALL(glBindTexture(GL_TEXTURE_2D, 0));
- if (m_textureHandle != 0) {
- GL_CALL(glDeleteTextures(1, &m_textureHandle));
- m_textureHandle = 0;
- }
- }
- /**
- * buffer 可以通过以下两种方式得到
- * 1) bitmap.copyPixelsToBuffer(bytebuffer);
- * void* buffer = env->GetDirectBufferAddress(bytebuffer);
- * 2) AndroidBitmap_lockPixels(env, bitmap, &buffer)
- */
- void GLTexture::setTexture(const void *buffer)
- {
- GL_CALL(glGenTextures(1, &m_textureHandle));
- GL_CALL(glBindTexture(GL_TEXTURE_2D, m_textureHandle));
- applyTexParameter();
- GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_width, m_height, 0,
- GL_RGBA, GL_UNSIGNED_BYTE, buffer));
- GL_CALL(glGenerateMipmap(GL_TEXTURE_2D));
- GL_CALL(glBindTexture(GL_TEXTURE_2D, 0));
- //GLInspector::checkGLError("setTexture");
- }
- void GLTexture::setTexParameter(GLint filter, GLint wrap)
- {
- m_filter = filter;
- m_wrap = wrap;
- m_isDirty = true;
- }
- void GLTexture::setBindParameter(char *alias, GLenum unit)
- {
- m_alias = alias;
- m_unit = unit;
- }
- void GLTexture::bind(ShaderProgram *shader)
- {
- shader->setUniformi(m_alias, m_unit);
- GL_CALL(glActiveTexture(GL_TEXTURE0 + m_unit));
- GL_CALL(glBindTexture(GL_TEXTURE_2D, m_textureHandle));
- if (m_isDirty)
- {
- applyTexParameter();
- }
- }
- void GLTexture::applyTexParameter()
- {
- GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, m_filter));
- GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, m_filter));
- GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, m_wrap));
- GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, m_wrap));
- m_isDirty = false;
- }
- } // namespace glcore
复制代码 2.13 FBO
FBO 是 Frame Buffer Object 的简称,即帧缓冲对象,主要用于离屏渲染、特效叠加,
frame_buffer_object.h- #pragma once
- #include "core_lib.h"
- #include "format.h"
- #include "shader_program.h"
- #include "texture_action.h"
- namespace glcore
- {
- /**
- * 帧缓冲对象 (简称FBO, 用于离屏渲染)
- * @author little fat sheep
- */
- class FrameBufferObject: public TextureAction
- {
- private:
- Format* m_format; // 颜色格式
- int m_width; // 缓冲区宽度
- int m_height; // 缓冲区高度
- bool m_hasDepth; // 是否有深度缓冲区
- bool m_hasStencil; // 是否有模板缓冲区
- GLuint m_frameBufferHandle; // 帧缓冲区句柄
- GLuint m_depthBufferHandle; // 深度缓冲区句柄
- GLuint m_stencilBufferHandle; // 模板缓冲区句柄
- GLuint m_colorTextureHandle; // 颜色缓冲区句柄
- GLint m_preFramebufferHandle; // 前一个帧缓冲区句柄
- int m_preFramebufferViewPort[4]; // 前一个帧缓冲区视口
- GLint m_filter = GL_LINEAR; // 滤波方式
- GLint m_wrap = GL_CLAMP_TO_EDGE; // 环绕方式
- const char* m_alias = ShaderProgram::UNIFORM_TEXTURE; // 纹理别名(着色器中变量名)
- GLenum m_unit = 0; // 纹理单元 (可能有多个纹理)
- bool m_isDirty = true; // 是否有脏数据 (纹理参数需要更新)
- public:
- FrameBufferObject(Format* format, int width, int height, bool hasDepth, bool hasStencil);
- ~FrameBufferObject() override;
- void setTexParameter(GLint filter, GLint wrap) override;
- void setBindParameter(char* alias, GLenum unit) override;
- void begin();
- void end();
- void bind(ShaderProgram* shader) override;
- private:
- void applyTexParameter();
- };
- } // namespace glcore
复制代码 frame_buffer_object.cpp- #include "glcore/frame_buffer_object.h"
- #include "glcore/gl_inspector.h"
- namespace glcore
- {
- FrameBufferObject::FrameBufferObject(Format* format, int width, int height, bool hasDepth, bool hasStencil)
- {
- m_format = format;
- m_width = width;
- m_height = height;
- m_hasDepth = hasDepth;
- m_hasStencil = hasStencil;
- GL_CALL(glGenFramebuffers(1, &m_frameBufferHandle));
- begin();
- if (m_hasDepth)
- {
- GL_CALL(glGenRenderbuffers(1, &m_depthBufferHandle));
- GL_CALL(glBindRenderbuffer(GL_RENDERBUFFER, m_depthBufferHandle));
- GL_CALL(glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height));
- GL_CALL(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
- GL_RENDERBUFFER, m_depthBufferHandle));
- GL_CALL(glBindRenderbuffer(GL_RENDERBUFFER, 0));
- }
- if (m_hasStencil)
- {
- GL_CALL(glGenRenderbuffers(1, &m_stencilBufferHandle));
- GL_CALL(glBindRenderbuffer(GL_RENDERBUFFER, m_stencilBufferHandle));
- GL_CALL(glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, width, height));
- GL_CALL(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
- GL_RENDERBUFFER, m_stencilBufferHandle));
- GL_CALL(glBindRenderbuffer(GL_RENDERBUFFER, 0));
- }
- GL_CALL(glGenTextures(1, &m_colorTextureHandle));
- GL_CALL(glBindTexture(GL_TEXTURE_2D, m_colorTextureHandle));
- GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, m_format->getFormat(), m_width,
- m_height, 0, m_format->getFormat(), m_format->getType(), nullptr));
- applyTexParameter();
- GL_CALL(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
- GL_TEXTURE_2D, m_colorTextureHandle, 0));
- end();
- }
- FrameBufferObject::~FrameBufferObject()
- {
- GL_CALL(glBindTexture(GL_TEXTURE_2D, 0));
- GL_CALL(glDeleteTextures(1, &m_colorTextureHandle));
- if (m_hasDepth) {
- GL_CALL(glDeleteRenderbuffers(1, &m_depthBufferHandle));
- }
- if (m_hasStencil) {
- GL_CALL(glDeleteRenderbuffers(1, &m_stencilBufferHandle));
- }
- GL_CALL(glDeleteFramebuffers(1, &m_frameBufferHandle));
- }
- void FrameBufferObject::setTexParameter(GLint filter, GLint wrap)
- {
- m_filter = filter;
- m_wrap = wrap;
- m_isDirty = true;
- }
- void FrameBufferObject::setBindParameter(char* alias, GLenum unit)
- {
- m_alias = alias;
- m_unit = unit;
- }
- void FrameBufferObject::begin()
- {
- GL_CALL(glGetIntegerv(GL_FRAMEBUFFER_BINDING, &m_preFramebufferHandle));
- GL_CALL(glGetIntegerv(GL_VIEWPORT, m_preFramebufferViewPort));
- GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, m_frameBufferHandle));
- GL_CALL(glViewport(0, 0, m_width, m_height));
- }
- void FrameBufferObject::end()
- {
- GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, m_preFramebufferHandle));
- GL_CALL(glViewport(m_preFramebufferViewPort[0], m_preFramebufferViewPort[1],
- m_preFramebufferViewPort[2], m_preFramebufferViewPort[3]));
- }
- void FrameBufferObject::bind(ShaderProgram* shader)
- {
- shader->setUniformi(m_alias, m_unit);
- GL_CALL(glActiveTexture(GL_TEXTURE0 + m_unit));
- GL_CALL(glBindTexture(GL_TEXTURE_2D, m_colorTextureHandle));
- if (m_isDirty)
- {
- applyTexParameter();
- }
- }
- void FrameBufferObject::applyTexParameter()
- {
- GL_CALL(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, m_filter));
- GL_CALL(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, m_filter));
- GL_CALL(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, m_wrap));
- GL_CALL(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, m_wrap));
- m_isDirty = false;
- }
- } // namespace glcore
复制代码 format.h- #pragma once
- #include "core_lib.h"
- namespace glcore
- {
- /**
- * 纹理格式
- * @author little fat sheep
- */
- class Format
- {
- private:
- GLint format;
- GLenum type;
- public:
- Format(GLint format, GLenum type);
- GLint getFormat() { return format; }
- GLenum getType() { return type; }
- static Format* Alpha();
- static Format* LuminanceAlpha();
- static Format* RGB565();
- static Format* RGBA4444();
- static Format* RGB888();
- static Format* RGBA8888();
- };
- } // namespace glcore
复制代码 format.cpp- #include "glcore/format.h"
- namespace glcore
- {
- Format::Format(GLint format, GLenum type):
- format(format),
- type(type)
- {
- }
- Format *Format::Alpha()
- {
- return new Format(GL_ALPHA, GL_UNSIGNED_BYTE);
- }
- Format *Format::LuminanceAlpha()
- {
- return new Format(GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE);
- }
- Format *Format::RGB565()
- {
- return new Format(GL_RGB, GL_UNSIGNED_SHORT_5_6_5);
- }
- Format *Format::RGBA4444()
- {
- return new Format(GL_RGB, GL_UNSIGNED_SHORT_4_4_4_4);
- }
- Format *Format::RGB888()
- {
- return new Format(GL_RGB, GL_UNSIGNED_BYTE);
- }
- Format *Format::RGBA8888()
- {
- return new Format(GL_RGBA, GL_UNSIGNED_BYTE);
- }
- } // 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- package com.zhyan8.egldemo;
- import android.content.Context;
- import android.graphics.SurfaceTexture;
- import android.util.Log;
- import android.view.Choreographer;
- import android.view.Surface;
- import android.view.TextureView;
- import androidx.annotation.NonNull;
- /**
- * @author little fat sheep
- * 承载EGL环境的View, 类比GLSurfaceView
- */
- public class EGLSurfaceView extends TextureView implements TextureView.SurfaceTextureListener {
- private static final String TAG = "EGLSurfaceView";
- private long mNativeHandle;
- protected Surface mSurface;
- private Choreographer mChoreographer = Choreographer.getInstance();
- static {
- System.loadLibrary("egl-native");
- }
- public EGLSurfaceView(Context context) {
- super(context);
- setSurfaceTextureListener(this);
- mNativeHandle = nativeCreate();
- }
- public void setRenderer(long handle) {
- Log.i(TAG, "setRenderer");
- nativeSetRenderer(mNativeHandle, handle);
- }
- public void startRender() {
- Log.i(TAG, "startRender");
- mChoreographer.removeFrameCallback(mFrameCallback);
- mChoreographer.postFrameCallback(mFrameCallback);
- }
- public void stopRender() {
- Log.i(TAG, "stopRender");
- mChoreographer.removeFrameCallback(mFrameCallback);
- }
- public void requestRender() {
- mFrameCallback.doFrame(System.nanoTime());
- }
- @Override
- public void onSurfaceTextureAvailable(@NonNull SurfaceTexture surface, int width, int height) {
- Log.i(TAG, "onSurfaceTextureAvailable");
- mSurface = new Surface(surface);
- nativeSurfaceCreated(mNativeHandle, mSurface);
- nativeSurfaceChanged(mNativeHandle, width, height);
- }
- @Override
- public void onSurfaceTextureSizeChanged(@NonNull SurfaceTexture surface, int width, int height) {
- Log.i(TAG, "onSurfaceTextureSizeChanged, width=" + width + ", height=" + height);
- nativeSurfaceChanged(mNativeHandle, width, height);
- }
- @Override
- public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surface) {
- Log.i(TAG, "onSurfaceTextureDestroyed");
- nativeSurfaceDestroyed(mNativeHandle);
- return false;
- }
- @Override
- public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface) {
- }
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- Log.i(TAG, "onDetachedFromWindow");
- stopRender();
- setSurfaceTextureListener(null);
- mSurface.release();
- nativeDestroy(mNativeHandle);
- mNativeHandle = 0;
- }
- private Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
- @Override
- public void doFrame(long frameTimeNanos) {
- mChoreographer.postFrameCallback(this);
- nativeDrawFrame(mNativeHandle);
- }
- };
- private native long nativeCreate();
- private native void nativeSetRenderer(long viewHandle, long rendererHandle);
- private native void nativeSurfaceCreated(long handle, Object surface);
- private native void nativeSurfaceChanged(long handle, int width, int height);
- private native void nativeDrawFrame(long handle);
- private native void nativeSurfaceDestroyed(long handle);
- private native void nativeDestroy(long handle);
- }
复制代码 jin_egl_surface_view.cpp- #include
- #include
- #include
- #include <jni.h>
- #include "glcore/core.h"
- #define LOG_TAG "JNIBrige_EGLSurfaceView"
- #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
- using namespace glcore;
- static jlong nativeCreate(JNIEnv *env, jobject thiz)
- {
- LOGI("nativeCreate");
- EGLSurfaceView* view = new EGLSurfaceView();
- return reinterpret_cast<jlong>(view);
- }
- static void nativeSetRenderer(JNIEnv *env, jobject thiz, jlong viewHandle, jlong rendererHandle)
- {
- LOGI("nativeSetRenderer");
- EGLSurfaceView* view = reinterpret_cast<EGLSurfaceView*>(viewHandle);
- EGLSurfaceView::Renderer* renderer = reinterpret_cast<EGLSurfaceView::Renderer*>(rendererHandle);
- view->setRenderer(renderer);
- }
- static void nativeSurfaceCreated(JNIEnv* env, jobject thiz, jlong handle, jobject surface)
- {
- LOGI("nativeSurfaceCreated");
- EGLSurfaceView* view = reinterpret_cast<EGLSurfaceView*>(handle);
- ANativeWindow* window = ANativeWindow_fromSurface(env, surface);
- app->setWindow(window);
- view->surfaceCreated();
- }
- static void nativeSurfaceChanged(JNIEnv* env, jobject thiz, jlong handle, jint width, jint height)
- {
- LOGI("nativeSurfaceChanged");
- EGLSurfaceView* view = reinterpret_cast<EGLSurfaceView*>(handle);
- view->surfaceChanged(width, height);
- }
- static void nativeDrawFrame(JNIEnv* env, jobject thiz, jlong handle)
- {
- EGLSurfaceView* view = reinterpret_cast<EGLSurfaceView*>(handle);
- view->drawFrame();
- }
- static void nativeSurfaceDestroyed(JNIEnv* env, jobject thiz, jlong handle)
- {
- LOGI("nativeSurfaceDestroyed");
- EGLSurfaceView* view = reinterpret_cast<EGLSurfaceView*>(handle);
- view->surfaceDestroy();
- }
- static void nativeDestroy(JNIEnv* env, jobject thiz, jlong handle)
- {
- LOGI("nativeDestroy");
- EGLSurfaceView* view = reinterpret_cast<EGLSurfaceView*>(handle);
- delete view;
- }
- static JNINativeMethod methods[] = {
- { "nativeCreate", "()J", (void*) nativeCreate },
- { "nativeSetRenderer", "(JJ)V", (void*) nativeSetRenderer },
- { "nativeSurfaceCreated", "(JLjava/lang/Object;)V", (void*) nativeSurfaceCreated },
- { "nativeSurfaceChanged", "(JII)V", (void*) nativeSurfaceChanged },
- { "nativeDrawFrame", "(J)V", (void*) nativeDrawFrame },
- { "nativeSurfaceDestroyed", "(J)V", (void*) nativeSurfaceDestroyed },
- { "nativeDestroy", "(J)V", (void*) nativeDestroy },
- };
- static int registerNativeMethods(JNIEnv* env) {
- int result = -1;
- jclass clazz = env->FindClass("com/zhyan8/egldemo/EGLSurfaceView");
- if (clazz != NULL) {
- jint len = sizeof(methods) / sizeof(methods[0]);
- if (env->RegisterNatives(clazz, methods, len) == JNI_OK) {
- result = 0;
- }
- }
- return result;
- }
- jint JNI_OnLoad(JavaVM* vm, void* reserved) {
- JNIEnv* env = NULL;
- jint result = -1;
- if (vm->GetEnv((void**) &env, JNI_VERSION_1_6) == JNI_OK) {
- if (NULL != env && registerNativeMethods(env) == 0) {
- result = JNI_VERSION_1_6;
- }
- }
- return result;
- }
复制代码 3.2 StringUtils
StringUtils 用于加载顶点和片元着色器资源为字符串。
StringUtils.java- package com.zhyan8.egldemo;
- import android.content.Context;
- import android.util.Log;
- import java.io.BufferedReader;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.InputStreamReader;
- /**
- * 字符串工具类
- * @author little fat sheep
- */
- public class StringUtils {
- private static final String TAG = "BitmapUtils";
- /**
- * 根据资源路径读取字符串
- * @param assetPath 资源路径, 如: "jelly_vert.glsl"
- */
- public static String loadStringFromAsset(Context context, String assetPath) {
- String str = "";
- try (InputStream inputStream = context.getAssets().open(assetPath)) {
- str = loadString(inputStream);
- } catch (IOException e) {
- Log.w(TAG, "loadString error, message=" + e.getMessage());
- }
- return str;
- }
- /**
- * 根据资源id读取字符串
- * @param rawId 资源id, 如: "R.raw.vertex_shader"
- */
- public static String loadStringFromRaw(Context context, String rawId) {
- if (rawId.startsWith("R.raw.")) {
- rawId = rawId.substring(6); // Remove "R.raw."
- }
- int id = context.getResources().getIdentifier(rawId, "raw", context.getPackageName());
- if (id == 0) {
- Log.e(TAG, "loadBitmapFromRaw, resource is not found, rawId=" + rawId);
- return null;
- }
- return loadStringFromRaw(context, id);
- }
- /**
- * 根据资源id读取字符串
- * @param rawId 资源id, 如: R.raw.vertex_shader
- */
- public static String loadStringFromRaw(Context context, int rawId) {
- String str = "";
- try (InputStream inputStream = context.getResources().openRawResource(rawId)) {
- str = loadString(inputStream);
- } catch (IOException e) {
- Log.w(TAG, "loadString error, message=" + e.getMessage());
- }
- return str;
- }
- private static String loadString(InputStream inputStream) {
- StringBuilder sb = new StringBuilder();
- try (BufferedReader br = new BufferedReader(new InputStreamReader(inputStream))) {
- String line;
- while ((line = br.readLine()) != null) {
- sb.append(line).append("\n");
- }
- } catch (IOException e) {
- Log.w(TAG, "loadString error, message=" + e.getMessage());
- }
- return sb.toString();
- }
- }
复制代码 string_utils.h- #pragma once
- /**
- * String工具类
- * @author little fat sheep
- */
- class StringUtils
- {
- public:
- /**
- * 根据资源路径读取字符串
- * @param asset 资源路径, 如: "vertex_shader.glsl"
- */
- static const char* loadStringFromAsset(const char* asset);
- /**
- * 根据资源id读取字符串
- * @param rawId 资源id, 如: "R.raw.vertex_shader"
- */
- static const char* loadStringFromRaw(const char* rawId);
- };
复制代码 string_utils.cpp- #include <jni.h>
- #include "glcore/core.h"
- #include "jni/jni_refs.h"
- #include "jni/string_utils.h"
- using namespace glcore;
- const char* StringUtils::loadStringFromAsset(const char* asset)
- {
- JNIEnv* env = app->jniEnv;
- jobject context = app->context;
- jstring assetStr = env->NewStringUTF(asset);
- jclass clazz = env->FindClass("com/zhyan8/egldemo/StringUtils");
- jmethodID method = LoadStringFromAssetMethodId(env);
- jstring jstr = (jstring) env->CallStaticObjectMethod(clazz, method, context, assetStr);
- const char* str = env->GetStringUTFChars(jstr, nullptr);
- return str;
- }
- const char* StringUtils::loadStringFromRaw(const char* rawId)
- {
- JNIEnv* env = app->jniEnv;
- jobject context = app->context;
- jstring rawIdStr = env->NewStringUTF(rawId);
- jclass clazz = env->FindClass("com/zhyan8/egldemo/StringUtils");
- jmethodID method = LoadStringFromRawMethodId(env);
- jstring jstr = (jstring) env->CallStaticObjectMethod(clazz, method, context, rawIdStr);
- const char* str = env->GetStringUTFChars(jstr, nullptr);
- return str;
- }
复制代码 3.3 BitmapUtils
BitmapUtils 用于加载图片资源为位图。
BitmapUtils.java- package com.zhyan8.egldemo;
- import android.content.Context;
- import android.graphics.Bitmap;
- import android.graphics.BitmapFactory;
- import android.util.Log;
- import java.io.IOException;
- import java.io.InputStream;
- /**
- * Bitmap工具类
- * @author little fat sheep
- */
- public class BitmapUtils {
- private static final String TAG = "BitmapUtils";
- /**
- * 根据资源路径读取bitmap
- * @param assetPath 资源路径, 如: "textures/xxx.jpg"
- */
- public static Bitmap loadBitmapFromAsset(Context context, String assetPath) {
- Bitmap bitmap = null;
- try (InputStream inputStream = context.getAssets().open(assetPath)) {
- BitmapFactory.Options options = getOptions();
- bitmap = BitmapFactory.decodeStream(inputStream, null, options);
- } catch (IOException e) {
- Log.e(TAG, "loadBitmapFromAsset error, message=" + e.getMessage());
- }
- return bitmap;
- }
- /**
- * 根据资源id读取bitmap
- * @param rawId 资源id, 如: "R.raw.xxx"
- */
- public static Bitmap loadBitmapFromRaw(Context context, String rawId) {
- if (rawId.startsWith("R.raw.")) {
- rawId = rawId.substring(6); // Remove "R.raw."
- }
- int id = context.getResources().getIdentifier(rawId, "raw", context.getPackageName());
- if (id == 0) {
- Log.e(TAG, "loadBitmapFromRaw, resource is not found, rawId=" + rawId);
- return null;
- }
- return loadBitmapFromRaw(context, id);
- }
- /**
- * 根据资源id读取bitmap
- * @param rawId 资源id, 如: R.raw.xxx
- */
- public static Bitmap loadBitmapFromRaw(Context context, int rawId) {
- Bitmap bitmap = null;
- try (InputStream inputStream = context.getResources().openRawResource(rawId)) {
- BitmapFactory.Options options = getOptions();
- bitmap = BitmapFactory.decodeStream(inputStream, null, options);
- } catch (IOException e) {
- Log.e(TAG, "loadBitmapFromRaw error, message=" + e.getMessage());
- }
- return bitmap;
- }
- private static BitmapFactory.Options getOptions() {
- BitmapFactory.Options options = new BitmapFactory.Options();
- options.inScaled = false;
- return options;
- }
- }
复制代码 bitmap_utils.h- #pragma once
- #include <jni.h>
- struct BitmapData
- {
- void* buffer;
- int width;
- int height;
- };
- /**
- * Bitmap工具类
- * @author little fat sheep
- */
- class BitmapUtils
- {
- public:
- /**
- * 根据资源路径读取bitmap
- * @param asset 资源路径, 如: "textures/xxx.jpg"
- */
- static BitmapData* loadBitmapDataFromAsset(const char* asset);
- /**
- * 根据资源id读取bitmap
- * @param rawId 资源id, 如: "R.raw.xxx"
- */
- static BitmapData* loadBitmapDataFromRaw(const char* rawId);
- private:
- static jobject loadBitmapFromAsset(JNIEnv* env, jobject context, const char* asset);
- static jobject loadBitmapFromRaw(JNIEnv* env, jobject context, const char* rawId);
- static BitmapData* getBitmapData(JNIEnv* env, jobject bitmap);
- };
复制代码 bitmap_utils.cpp- #include
- #include
- #include <string>
- #include "glcore/core.h"
- #include "jni/bitmap_utils.h"
- #include "jni/jni_refs.h"
- #define LOG_TAG "Native: BitmapUtils"
- #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
- using namespace glcore;
- BitmapData* BitmapUtils::loadBitmapDataFromAsset(const char* asset) {
- JNIEnv* env = app->jniEnv;
- jobject context = app->context;
- jobject bitmap = loadBitmapFromAsset(env, context, asset);
- if (!bitmap) {
- LOGI("loadBitmapDataFromAsset, bitmap is null: %s", asset);
- return nullptr;
- }
- return getBitmapData(env, bitmap);
- }
- BitmapData* BitmapUtils::loadBitmapDataFromRaw(const char* rawId)
- {
- JNIEnv* env = app->jniEnv;
- jobject context = app->context;
- jobject bitmap = loadBitmapFromRaw(env, context, rawId);
- if (!bitmap) {
- LOGI("loadBitmapDataFromRaw, bitmap is null: %s", rawId);
- return nullptr;
- }
- return getBitmapData(env, bitmap);
- }
- jobject BitmapUtils::loadBitmapFromAsset(JNIEnv* env, jobject context, const char* asset)
- {
- jstring assetStr = env->NewStringUTF(asset);
- jclass clazz = env->FindClass("com/zhyan8/egldemo/BitmapUtils");
- jmethodID method = LoadBitmapFromAssetMethodId(env);
- jobject bitmap = env->CallStaticObjectMethod(clazz, method, context, assetStr);
- return bitmap;
- }
- jobject BitmapUtils::loadBitmapFromRaw(JNIEnv* env, jobject context, const char* rawId)
- {
- jstring rawIdStr = env->NewStringUTF(rawId);
- jclass clazz = env->FindClass("com/zhyan8/egldemo/BitmapUtils");
- jmethodID method = LoadBitmapFromRawMethodId(env);
- jobject bitmap = env->CallStaticObjectMethod(clazz, method, context, rawIdStr);
- return bitmap;
- }
- BitmapData* BitmapUtils::getBitmapData(JNIEnv* env, jobject bitmap)
- {
- AndroidBitmapInfo info;
- if (AndroidBitmap_getInfo(env, bitmap, &info))
- {
- LOGI("getBitmapData, failed to get bitmap info");
- return nullptr;
- }
- void* buffer;
- if (AndroidBitmap_lockPixels(env, bitmap, &buffer)) {
- LOGI("getBitmapData, failed to lock bitmap pixels");
- return nullptr;
- }
- BitmapData* data = new BitmapData();
- data->buffer = buffer;
- data->width = info.width;
- data->height = info.height;
- return data;
- }
复制代码 3.4 jin_ref
jni_ref 提供了 StringUtils 和 BitmaUtils 的类路径、函数名、函数签名等信息。
jni_ref.h- #pragma once
- #include <jni.h>
- jmethodID LoadBitmapFromAssetMethodId(JNIEnv* env);
- jmethodID LoadBitmapFromRawMethodId(JNIEnv* env);
- jmethodID LoadStringFromAssetMethodId(JNIEnv* env);
- jmethodID LoadStringFromRawMethodId(JNIEnv* env);
- jmethodID GetMethodId(JNIEnv* env, const char* method[]);
- jmethodID GetStaticMethodId(JNIEnv* env, const char* method[]);
复制代码 jni_ref.c- #include "jni/jni_refs.h"
- const char* loadBitmapFromAssetTab[] = {
- "com/zhyan8/egldemo/BitmapUtils",
- "loadBitmapFromAsset",
- "(Landroid/content/Context;Ljava/lang/String;)Landroid/graphics/Bitmap;"
- };
- const char* loadBitmapFromRawTab[] = {
- "com/zhyan8/egldemo/BitmapUtils",
- "loadBitmapFromRaw",
- "(Landroid/content/Context;Ljava/lang/String;)Landroid/graphics/Bitmap;"
- };
- const char* loadStringFromAssetTab[] = {
- "com/zhyan8/egldemo/StringUtils",
- "loadStringFromAsset",
- "(Landroid/content/Context;Ljava/lang/String;)Ljava/lang/String;"
- };
- const char* loadStringFromRawTab[] = {
- "com/zhyan8/egldemo/StringUtils",
- "loadStringFromRaw",
- "(Landroid/content/Context;Ljava/lang/String;)Ljava/lang/String;"
- };
- jmethodID LoadBitmapFromAssetMethodId(JNIEnv* env)
- {
- return GetStaticMethodId(env, loadBitmapFromAssetTab);
- }
- jmethodID LoadBitmapFromRawMethodId(JNIEnv* env)
- {
- return GetStaticMethodId(env, loadBitmapFromRawTab);
- }
- jmethodID LoadStringFromAssetMethodId(JNIEnv* env)
- {
- return GetStaticMethodId(env, loadStringFromAssetTab);
- }
- jmethodID LoadStringFromRawMethodId(JNIEnv* env)
- {
- return GetStaticMethodId(env, loadStringFromRawTab);
- }
- jmethodID GetMethodId(JNIEnv* env, const char* method[])
- {
- jclass clazz = env->FindClass(method[0]);
- return env->GetMethodID(clazz, method[1], method[2]);
- }
- jmethodID GetStaticMethodId(JNIEnv* env, const char* method[])
- {
- jclass clazz = env->FindClass(method[0]);
- return env->GetStaticMethodID(clazz, method[1], method[2]);
- }
复制代码 4 应用
本节将基于 glcore 框架写一个色散特效叠加果冻特效的 Demo,体验一下 glcore 的便捷之处。
4.1 MyRenderer
my_renderer.h- #pragma once
- #include "glcore/core.h"
- #include "dispersion_effect.h"
- #include "jelly_effect.h"
- using namespace glcore;
- /**
- * 自定义渲染器
- * @author little fat sheep
- */
- class MyRenderer : public EGLSurfaceView::Renderer
- {
- private:
- DispersionEffect* m_dispersionEffect;
- JellyEffect* m_jellyEffect;
- long m_startTime = 0;
- float m_runTime = 0.0f;
- public:
- MyRenderer();
- ~MyRenderer() override;
- void onSurfaceCreated() override;
- void onSurfaceChanged(int width, int height) override;
- void onDrawFrame() override;
- private:
- long getTimestamp();
- };
复制代码 my_renderer.cpp- #include
- #include <chrono>
- #include "custom/my_renderer.h"
- #define LOG_TAG "Native: MyRenderer"
- #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
- using namespace glcore;
- using namespace std::chrono;
- MyRenderer::MyRenderer()
- {
- LOGI("init");
- m_dispersionEffect = new DispersionEffect();
- m_jellyEffect = new JellyEffect();
- m_jellyEffect->setTexAction(m_dispersionEffect);
- }
- MyRenderer::~MyRenderer()
- {
- LOGI("destroy");
- delete m_dispersionEffect;
- delete m_jellyEffect;
- }
- void MyRenderer::onSurfaceCreated()
- {
- LOGI("onSurfaceCreated");
- m_dispersionEffect->onCreate();
- m_jellyEffect->onCreate();
- GL_CALL(glClearColor(0.1f, 0.2f, 0.3f, 0.4f));
- m_startTime = getTimestamp();
- }
- void MyRenderer::onSurfaceChanged(int width, int height)
- {
- LOGI("onSurfaceChanged, width: %d, height: %d", width, height);
- GL_CALL(glViewport(0, 0, width, height));
- m_dispersionEffect->onResize(width, height);
- m_jellyEffect->onResize(width, height);
- }
- void MyRenderer::onDrawFrame()
- {
- m_runTime = (getTimestamp() - m_startTime) / 1000.0f;
- GL_CALL(glClear(GL_COLOR_BUFFER_BIT));
- m_dispersionEffect->onDraw(m_runTime);
- m_jellyEffect->onDraw(m_runTime);
- }
- long MyRenderer::getTimestamp()
- {
- auto now = std::chrono::system_clock::now(); // 获取当前时间
- auto duration = now.time_since_epoch(); // 转换为自纪元以来的时间
- return duration_cast<milliseconds>(duration).count();
- }
复制代码 4.2 DispersionEffect
DispersionEffect 是色散特效。
dispersion_effect.h- #pragma once
- #include "glcore/core.h"
- using namespace glcore;
- /**
- * 色散特效
- * @author little fat sheep
- */
- class DispersionEffect: public TextureAction
- {
- private:
- ShaderProgram* m_program;
- Mesh* m_mesh;
- GLTexture* m_glTexture;
- FrameBufferObject* m_fbo;
- public:
- DispersionEffect();
- ~DispersionEffect() override;
- void onCreate();
- void onResize(int width, int height);
- void onDraw(float runtime);
- void bind(ShaderProgram* shader) override;
- private:
- void createProgram();
- void createTexture();
- };
复制代码 dispersion_effect.cpp- #include
- #include "custom/dispersion_effect.h"
- #include "jni/bitmap_utils.h"
- #include "jni/string_utils.h"
- #define LOG_TAG "Native: DispersionEffect"
- #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
- using namespace glcore;
- DispersionEffect::DispersionEffect()
- {
- LOGI("init");
- }
- DispersionEffect::~DispersionEffect()
- {
- LOGI("destroy");
- delete m_program;
- delete m_mesh;
- delete m_glTexture;
- delete m_fbo;
- }
- void DispersionEffect::onCreate()
- {
- LOGI("onCreate");
- createProgram();
- createTexture();
- m_mesh = MeshUtils::createRect(true);
- m_fbo = new FrameBufferObject(Format::RGBA8888(), app->width, app->height, false, false);
- }
- void DispersionEffect::onResize(int width, int height)
- {
- LOGI("onResize, width: %d, height: %d", width, height);
- }
- void DispersionEffect::onDraw(float runtime)
- {
- m_fbo->begin();
- m_program->bind();
- m_program->setUniformf("u_time", runtime);
- m_program->setUniformf("u_aspect", app->aspect);
- m_glTexture->bind(m_program);
- m_mesh->render(m_program);
- m_fbo->end();
- }
- void DispersionEffect::bind(ShaderProgram* shader)
- {
- m_fbo->bind(shader);
- }
- void DispersionEffect::createProgram()
- {
- LOGI("createProgram");
- const char* vertexCode = StringUtils::loadStringFromAsset("dispersion_vert.glsl");
- const char* fragmentCode = StringUtils::loadStringFromAsset("dispersion_frag.glsl");
- m_program = new ShaderProgram(vertexCode, fragmentCode);
- }
- void DispersionEffect::createTexture()
- {
- LOGI("createTexture");
- BitmapData* data = BitmapUtils::loadBitmapDataFromAsset("girl.jpg");
- m_glTexture = new GLTexture(data->buffer, data->width, data->height);
- }
复制代码 dispersion_vert.glsl- attribute vec4 a_position;
- attribute vec2 a_texCoord0;
- varying vec2 v_texCoord;
- void main() {
- gl_Position = a_position;
- v_texCoord = a_texCoord0;
- }
复制代码 dispersion_frag.glsl- precision highp float;
- uniform float u_aspect;
- uniform float u_time;
- uniform sampler2D u_texture;
- varying vec2 v_texCoord;
- vec2 getOffset() { // 偏移函数
- float time = u_time * 1.5;
- vec2 dire = vec2(sin(time), cos(time));
- float strength = sin(u_time * 2.0) * 0.004;
- return dire * strength * vec2(1.0, 1.0 / u_aspect);
- }
- void main() {
- vec2 offset = getOffset();
- vec4 color = texture2D(u_texture, v_texCoord);
- color.r = texture2D(u_texture, v_texCoord + offset).r;
- color.b = texture2D(u_texture, v_texCoord - offset).b;
- gl_FragColor = color;
- }
复制代码 4.3 JellyEffect
JellyEffect 是果冻特效。
jelly_effect.h- #pragma once
- #include "glcore/core.h"
- using namespace glcore;
- /**
- * 果冻特效
- * @author little fat sheep
- */
- class JellyEffect
- {
- private:
- ShaderProgram* m_program;
- Mesh* m_mesh;
- TextureAction* m_texAction;
- public:
- JellyEffect();
- ~JellyEffect();
- void setTexAction(TextureAction* texAction);
- void onCreate();
- void onResize(int width, int height);
- void onDraw(float runtime);
- private:
- void createProgram();
- };
复制代码 jelly_effect.cpp- #include
- #include "custom/jelly_effect.h"
- #include "jni/bitmap_utils.h"
- #include "jni/string_utils.h"
- #define LOG_TAG "Native: JellyEffect"
- #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
- using namespace glcore;
- JellyEffect::JellyEffect()
- {
- LOGI("init");
- }
- JellyEffect::~JellyEffect()
- {
- LOGI("destroy");
- delete m_program;
- delete m_mesh;
- }
- void JellyEffect::setTexAction(TextureAction* texAction)
- {
- m_texAction = texAction;
- }
- void JellyEffect::onCreate()
- {
- LOGI("onCreate");
- createProgram();
- m_mesh = MeshUtils::createRect(false);
- }
- void JellyEffect::onResize(int width, int height)
- {
- LOGI("onResize, width: %d, height: %d", width, height);
- }
- void JellyEffect::onDraw(float runtime)
- {
- m_program->bind();
- m_program->setUniformf("u_time", runtime);
- m_program->setUniformf("u_aspect", app->aspect);
- m_texAction->bind(m_program);
- m_mesh->render(m_program);
- }
- void JellyEffect::createProgram()
- {
- LOGI("createProgram");
- const char* vertexCode = StringUtils::loadStringFromAsset("jelly_vert.glsl");
- const char* fragmentCode = StringUtils::loadStringFromAsset("jelly_frag.glsl");
- m_program = new ShaderProgram(vertexCode, fragmentCode);
- }
复制代码 jelly_vert.glsl- attribute vec4 a_position;
- attribute vec2 a_texCoord0;
- varying vec2 v_texCoord;
- void main() {
- gl_Position = a_position;
- v_texCoord = a_texCoord0;
- }
复制代码 jelly_frag.glsl- precision highp float;
- uniform float u_aspect;
- uniform float u_time;
- uniform sampler2D u_texture;
- varying vec2 v_texCoord;
- vec2 fun(vec2 center, vec2 uv) { // 畸变函数
- vec2 dire = normalize(uv - center);
- float dist = distance(uv, center);
- vec2 uv1 = uv + sin(dist * 2.2 + u_time * 3.5) * 0.025;
- return uv1;
- }
- void main() {
- vec2 uv = vec2(v_texCoord.x, v_texCoord.y / u_aspect);
- vec2 center = vec2(0.5, 0.5 / u_aspect);
- vec2 uv1 = fun(center, uv);
- uv1.y *= u_aspect;
- gl_FragColor = texture2D(u_texture, uv1);
- }
复制代码 4.4 运行效果
运行效果如下,可以看到叠加了色散和果冻特效。
声明:本文转自【OpenGL ES】在Android上手撕一个mini版的渲染框架。
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |