SkyImageRender
一个基于 Android NDK 和 OpenGL ES 2.0 的高性能图像渲染项目,专注于实现完整的图像渲染管线。
项目概述
SkyImageRender 是一个 Android 原生图像渲染应用,通过 JNI 调用 C++ 代码,使用 OpenGL ES 2.0 实现高效的图像渲染。项目展示了从图像数据加载到屏幕显示的完整渲染管线流程。
技术架构
整体架构
┌─────────────────┐ JNI ┌─────────────────┐
│ Kotlin/Java │ ◄────────► │ C++ │
│ (UI Layer) │ │ (Render Layer) │
└─────────────────┘ └─────────────────┘
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ SurfaceView │ │ OpenGL ES │
│ (Display) │ │ (Graphics) │
└─────────────────┘ └─────────────────┘
核心组件
- MainActivity.kt: Android 主界面,负责 Surface 管理和用户交互
- NativeRenderer.kt: JNI 接口层,连接 Java 和 C++ 代码
- RenderContext (C++): 核心渲染上下文,管理完整的渲染管线
渲染管线详解
1. 初始化阶段 (Initialization Pipeline)
EGL 上下文初始化
void RenderContext::initEGL() {
// 1. 获取 EGL Display
display_ = eglGetDisplay(EGL_DEFAULT_DISPLAY);
eglInitialize(display_, nullptr, nullptr);
// 2. 配置 EGL 属性
const EGLint configAttribs[] = {
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_BLUE_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_RED_SIZE, 8,
EGL_DEPTH_SIZE, 0,
EGL_NONE
};
// 3. 创建 Surface 和 Context
surface_ = eglCreateWindowSurface(display_, config, window_, nullptr);
context_ = eglCreateContext(display_, config, EGL_NO_CONTEXT, contextAttribs);
// 4. 绑定上下文
eglMakeCurrent(display_, surface_, surface_, context_);
}
Shader 程序创建
// 顶点着色器 - 处理顶点变换
const char* VERTEX_SHADER_SOURCE =
"attribute vec4 aPosition;\n"
"attribute vec2 aTexCoord;\n"
"varying vec2 vTexCoord;\n"
"uniform mat4 uTextureMatrix;\n"
"uniform mat4 uPositionMatrix;\n"
"void main() {\n"
" gl_Position = uPositionMatrix * aPosition;\n"
" vTexCoord = (uTextureMatrix * vec4(aTexCoord, 0.0, 1.0)).xy;\n"
"}\n";
// 片段着色器 - 处理像素着色
const char* FRAGMENT_SHADER_SOURCE =
"precision mediump float;\n"
"varying vec2 vTexCoord;\n"
"uniform sampler2D uTexture;\n"
"void main() {\n"
" gl_FragColor = texture2D(uTexture, vTexCoord);\n"
"}\n";
2. 资源管理阶段 (Resource Management)
纹理创建与管理
GLuint RenderContext::createTexture(const uint8_t *data, int width, int height) {
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
// 设置纹理参数
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// 上传纹理数据
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height,
0, GL_RGBA, GL_UNSIGNED_BYTE, data);
return texture;
}
顶点缓冲对象 (VBO) 管理
// 顶点数据(位置 + 纹理坐标)
const GLfloat vertices[] = {
// 位置 // 纹理坐标
-1.0f, 1.0f, 0.0f, 1.0f, // 左上
-1.0f, -1.0f, 0.0f, 0.0f, // 左下
1.0f, -1.0f, 1.0f, 0.0f, // 右下
1.0f, 1.0f, 1.0f, 1.0f // 右上
};
const GLushort indices[] = {0, 1, 2, 0, 2, 3}; // 索引数据
3. 渲染管线 (Rendering Pipeline)
几何变换阶段
// 1. 计算宽高比和缩放比例
float bitmapAspectRatio = bitmapWidth / bitmapHeight;
float screenAspectRatio = screenWidth / screenHeight;
float scaleRatio = bitmapAspectRatio > screenAspectRatio
? screenWidth / bitmapWidth
: screenHeight / bitmapHeight;
// 2. 构建位置变换矩阵
float sx = (bitmapWidth * scaleRatio) / screenWidth;
float sy = (bitmapHeight * scaleRatio) / screenHeight * 1.05f;
float pMatrix[16] = {
sx, 0, 0, 0, // 第一列
0, sy, 0, 0, // 第二列
0, 0, 1, 0, // 第三列
0, 0, 0, 1 // 第四列
};
// 3. 纹理翻转矩阵(处理 Android 坐标系差异)
float flipYMatrix[16] = {
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, -1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 1.0f, 0.0f, 1.0f
};
渲染执行阶段
void RenderContext::renderFrame(...) {
// 1. 设置顶点属性
GLint posLoc = glGetAttribLocation(program_, "aPosition");
GLint texLoc = glGetAttribLocation(program_, "aTexCoord");
glEnableVertexAttribArray(posLoc);
glVertexAttribPointer(posLoc, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GL_FLOAT), (void*)0);
glEnableVertexAttribArray(texLoc);
glVertexAttribPointer(texLoc, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GL_FLOAT),
(void*)(2 * sizeof(GL_FLOAT)));
// 2. 设置 Uniform 变量
glUniformMatrix4fv(positionMatrix, 1, GL_FALSE, pMatrix);
glUniformMatrix4fv(textureMatrix, 1, GL_FALSE, flipYMatrix);
// 3. 清除缓冲区
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// 4. 绑定纹理并绘制
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture_);
glUniform1i(glGetUniformLocation(program_, "uTexture"), 0);
// 5. 执行绘制调用
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, nullptr);
// 6. 交换缓冲区
eglSwapBuffers(display_, surface_);
}
4. 渲染管线流程图
图像数据 (RGBA)
│
▼
┌─────────────┐
│ 纹理上传 │ ◄── glTexImage2D()
│ (GPU Memory)│
└─────────────┘
│
▼
┌─────────────┐
│ 顶点处理 │ ◄── Vertex Shader
│ (变换矩阵) │ • 位置变换
└─────────────┘ • 纹理坐标变换
│
▼
┌─────────────┐
│ 图元装配 │ ◄── 三角形组装
│ (Primitive) │
└─────────────┘
│
▼
┌─────────────┐
│ 光栅化 │ ◄── 像素生成
│ (Rasterize) │
└─────────────┘
│
▼
┌─────────────┐
│ 片段处理 │ ◄── Fragment Shader
│ (像素着色) │ • 纹理采样
└─────────────┘ • 颜色计算
│
▼
┌─────────────┐
│ 帧缓冲输出 │ ◄── eglSwapBuffers()
│ (屏幕显示) │
└─────────────┘
关键特性
1. 高性能渲染
- 零拷贝纹理上传: 直接从 Java ByteArray 到 GPU 内存
- 硬件加速: 完全基于 GPU 的并行处理
- 优化的顶点数据: 紧凑的顶点缓冲布局
2. 自适应显示
- 宽高比保持: 自动计算缩放比例,保持图像不变形
- 屏幕适配: 支持不同分辨率和屏幕尺寸
- 坐标系转换: 处理 Android 和 OpenGL 坐标系差异
3. 内存管理
- 资源生命周期: 完整的 EGL 上下文和 OpenGL 资源管理
- 异常处理: 完善的错误检查和资源清理机制
项目结构
SkyImageRender/
├── app/
│ ├── src/main/
│ │ ├── java/imt/skyimagerender/
│ │ │ ├── MainActivity.kt # 主界面活动
│ │ │ └── renderer/
│ │ │ └── NativeRenderer.kt # JNI 接口
│ │ ├── cpp/
│ │ │ ├── renderer/
│ │ │ │ ├── renderer.h # 渲染器头文件
│ │ │ │ └── renderer.cpp # 渲染器实现
│ │ │ ├── utils/
│ │ │ │ └── logger.h # 日志工具
│ │ │ ├── renderer_jni.cpp # JNI 入口
│ │ │ ├── renderer_jni.h # JNI 头文件
│ │ │ └── CMakeLists.txt # CMake 构建配置
│ │ └── res/
│ │ ├── drawable/ # 图像资源
│ │ └── layout/ # 布局文件
│ └── build.gradle.kts # 应用构建配置
├── build.gradle.kts # 项目构建配置
└── settings.gradle.kts # 项目设置
构建要求
- Android SDK: API Level 30+
- NDK: 支持 armeabi-v7a, arm64-v8a
- CMake: 3.22.1+
- Kotlin: 1.8+
- OpenGL ES: 2.0+
编译与运行
-
克隆项目
git clone https://github.com/zhiwei-wu/SkyImageRender.git cd SkyImageRender -
打开项目
- 使用 Android Studio 打开项目
- 确保安装了 NDK 和 CMake
-
构建项目
./gradlew assembleDebug -
运行应用
- 连接 Android 设备或启动模拟器
- 点击 Run 按钮或使用
./gradlew installDebug
使用说明
- 启动应用: 应用启动后会自动加载并显示第一张图片
- 切换图片: 点击 "Change Picture" 按钮可以在两张预设图片间切换
- 自适应显示: 图片会自动适配屏幕尺寸,保持原始宽高比
技术亮点
1. 完整的 OpenGL ES 渲染管线
- 从 EGL 初始化到最终屏幕输出的完整流程
- 标准的顶点着色器和片段着色器实现
- 高效的纹理管理和顶点缓冲管理
2. 跨平台架构设计
- 清晰的 Java/Kotlin 与 C++ 分层
- 标准的 JNI 接口设计
- 可扩展的渲染器架构
3. 性能优化
- GPU 硬件加速渲染
- 最小化 CPU-GPU 数据传输
- 高效的矩阵变换计算
扩展可能
- 多纹理支持: 扩展为支持多个纹理单元
- 后处理效果: 添加滤镜、色彩调整等后处理管线
- 3D 渲染: 扩展为支持 3D 模型渲染
- 视频渲染: 支持视频帧的实时渲染
许可证
本项目仅供学习和研究使用。
本项目展示了 Android 平台上使用 OpenGL ES 进行高性能图像渲染的完整实现,是学习移动端图形编程的优秀参考。