摄像头画面渲染之OpenGL ES渲染NV21格式
2023-09-12 11:14:09
使用 OpenGL ES 渲染 NV21 格式图像
在计算机视觉和游戏开发领域,将摄像头捕获的图像渲染到屏幕上是司空见惯的操作。传统方法是采用 SurfaceTexture 提供的纹理,但这种做法存在一定限制。比如,SurfaceTexture 仅适用于渲染 YUV420 格式的图像,而 NV21 格式的图像则无法直接渲染。
OpenGL ES 是一个跨平台图形库,它支持多种图像格式,包括 NV21 格式。这让我们得以使用 OpenGL ES 直接渲染 NV21 格式的图像,从而获得更强的灵活性。
OpenGL ES 渲染 NV21 格式图像的步骤
-
转换图像格式:
首先,我们需要将 NV21 格式的图像转换为 OpenGL ES 支持的格式,例如 RGBA、BGRA、RGB 或 BGR。我们可以使用glTexImage2D
函数完成此转换。 -
创建纹理对象:
接下来,我们需要创建一个纹理对象。纹理对象是 OpenGL ES 中用于存储图像数据的对象。我们可以使用glGenTextures
函数创建一个纹理对象。 -
加载图像数据:
然后,我们需要将图像数据加载到纹理对象中。我们可以使用glTexImage2D
函数完成此操作。 -
创建着色器程序:
最后,我们需要创建一个着色器程序。着色器程序是 OpenGL ES 中用于处理图像数据并将其渲染到屏幕上的程序。我们可以使用glCreateProgram
函数创建一个着色器程序。
示例代码
以下是一个使用 OpenGL ES 渲染 NV21 格式图像的示例代码:
#include <GLES2/gl2.h>
#define NV21_WIDTH 640
#define NV21_HEIGHT 480
unsigned char nv21_data[NV21_WIDTH * NV21_HEIGHT * 3 / 2];
void render_nv21_image() {
// 转换图像格式
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, NV21_WIDTH, NV21_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, nv21_data);
// 创建纹理对象
GLuint texture_id;
glGenTextures(1, &texture_id);
// 加载图像数据
glBindTexture(GL_TEXTURE_2D, texture_id);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, NV21_WIDTH, NV21_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, nv21_data);
// 创建着色器程序
GLuint program_id = glCreateProgram();
// 编译顶点着色器
GLuint vertex_shader_id = glCreateShader(GL_VERTEX_SHADER);
const char *vertex_shader_source =
"attribute vec2 a_position;"
"attribute vec2 a_texcoord;"
"varying vec2 v_texcoord;"
"void main() {"
" gl_Position = vec4(a_position, 0.0, 1.0);"
" v_texcoord = a_texcoord;"
"}";
glShaderSource(vertex_shader_id, 1, &vertex_shader_source, NULL);
glCompileShader(vertex_shader_id);
// 编译片段着色器
GLuint fragment_shader_id = glCreateShader(GL_FRAGMENT_SHADER);
const char *fragment_shader_source =
"precision mediump float;"
"varying vec2 v_texcoord;"
"uniform sampler2D u_texture;"
"void main() {"
" gl_FragColor = texture2D(u_texture, v_texcoord);"
"}";
glShaderSource(fragment_shader_id, 1, &fragment_shader_source, NULL);
glCompileShader(fragment_shader_id);
// 将着色器链接到程序
glAttachShader(program_id, vertex_shader_id);
glAttachShader(program_id, fragment_shader_id);
glLinkProgram(program_id);
// 使用程序
glUseProgram(program_id);
// 获取属性位置
GLint position_attribute = glGetAttribLocation(program_id, "a_position");
GLint texcoord_attribute = glGetAttribLocation(program_id, "a_texcoord");
// 获取 uniform 变量位置
GLint texture_uniform = glGetUniformLocation(program_id, "u_texture");
// 启用属性数组
glEnableVertexAttribArray(position_attribute);
glEnableVertexAttribArray(texcoord_attribute);
// 设置属性数组
glVertexAttribPointer(position_attribute, 2, GL_FLOAT, GL_FALSE, 0, vertices);
glVertexAttribPointer(texcoord_attribute, 2, GL_FLOAT, GL_FALSE, 0, texcoords);
// 绑定纹理
glBindTexture(GL_TEXTURE_2D, texture_id);
// 设置 uniform 变量
glUniform1i(texture_uniform, 0);
// 绘制图像
glDrawArrays(GL_TRIANGLES, 0, 6);
// 禁用属性数组
glDisableVertexAttribArray(position_attribute);
glDisableVertexAttribArray(texcoord_attribute);
// 解绑纹理
glBindTexture(GL_TEXTURE_2D, 0);
// 解除程序
glUseProgram(0);
// 删除程序
glDeleteProgram(program_id);
// 删除着色器
glDeleteShader(fragment_shader_id);
glDeleteShader(vertex_shader_id);
// 删除纹理对象
glDeleteTextures(1, &texture_id);
}
常见问题解答
1. 为什么我需要使用 OpenGL ES 而不是 SurfaceTexture 来渲染 NV21 格式的图像?
使用 OpenGL ES 可以提供更高的灵活性,因为它支持多种图像格式,包括 NV21 格式。SurfaceTexture 只支持渲染 YUV420 格式的图像。
2. 我该如何转换 NV21 格式的图像为 OpenGL ES 支持的格式?
你可以使用 glTexImage2D
函数将 NV21 格式的图像转换为 OpenGL ES 支持的格式。
3. 创建纹理对象有什么作用?
纹理对象用于存储图像数据。在渲染图像之前,需要创建一个纹理对象并将图像数据加载到其中。
4. 着色器程序在渲染过程中扮演什么角色?
着色器程序是一段代码,用于处理图像数据并将其渲染到屏幕上。它包括顶点着色器和片段着色器。
5. 我需要使用哪些代码来渲染 NV21 格式的图像?
你可以参考本文中提供的示例代码。它包含了渲染 NV21 格式图像所需的所有步骤。