OpenGL 使用 Shader 实现 RGBA 转 I420(附项目源码)

本文介绍了如何使用 OpenGL 的 Shader 来实现 RGBA 图像到 I420 格式的转换,详细讲解了转换原理,包括视口设置、纹理坐标采样,并提供了关键代码实现。通过这种方法,可以在视频解码中减少数据量,提高性能。文章还探讨了与转换 NV21 相比的效率问题,并提供了一个完整的项目源码链接。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前面连续写过两篇 shader 实现 RGBA 转 YUV 的文章:

关于 YUV 图像的相关知识这里也贴出来一些链接,供不熟悉的同学查阅。

Shader 实现 RGBA 转 I420

I420 格式的图像在视频解码中比较常见,像前面文章中提到的,在工程中一般会选择使用 Shader 将 RGBA 转 YUV,这样再使用 glReadPixels 读取图像时可以有效降低传输数据量,提升性能,并且兼容性好。

所以,在读取 OpenGL 渲染结果时,先利用 Shader 将 RGBA 转 YUV 然后再进行读取,这种方式非常高效便捷。

例如 YUYV 格式相对 RGBA 数据量降为原来的 50% ,而采用 NV21 或者 I420 格式可以降低为原来的 37.5% 。

当然读取 OpenGL 渲染结果的方式还有很多种,要视具体的需求和使用场景而定,具体可以参考文章:

要在Linux系统下使用OpenGL显示图像,需要完成几个关键步骤:初始化窗口、创建上下文、加载纹理以及渲染纹理到屏幕上。以下是一个完整的示例流程和代码。 ### 环境准备 在开始之前,确保系统中安装了必要的开发库: ```bash sudo apt-get install libgl1-mesa-dev libglfw3-dev libpng-dev ``` 这里用到了 GLFW 和 GLAD 来管理窗口和 OpenGL 函数指针,并使用 stb_image.h 来加载图片。 ### 示例代码 #### 1. 初始化GLFW和GLAD ```c #include <glad/glad.h> #include <GLFW/glfw3.h> #include <stdio.h> #include "stb_image.h" // 窗口大小 const int WIDTH = 800; const int HEIGHT = 600; // 顶点着色器源码 const char* vertexShaderSource = "#version 330 core\n" "layout (location = 0) in vec3 aPos;\n" "layout (location = 1) in vec2 aTexCoord;\n" "out vec2 TexCoord;\n" "void main()\n" "{\n" " gl_Position = vec4(aPos, 1.0);\n" " TexCoord = aTexCoord;\n" "}\0"; // 片段着色器源码 const char* fragmentShaderSource = "#version 330 core\n" "in vec2 TexCoord;\n" "out vec4 FragColor;\n" "uniform sampler2D ourTexture;\n" "void main()\n" "{\n" " FragColor = texture(ourTexture, TexCoord);\n" "}\n\0"; unsigned int loadTexture(char const * path) { unsigned int textureID; glGenTextures(1, &textureID); int width, height, nrChannels; unsigned char *data = stbi_load(path, &width, &height, &nrChannels, 0); if (data) { GLenum format; if (nrChannels == 1) format = GL_RED; else if (nrChannels == 3) format = GL_RGB; else if (nrChannels == 4) format = GL_RGBA; glBindTexture(GL_TEXTURE_2D, textureID); glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data); glGenerateMipmap(GL_TEXTURE_2D); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); stbi_image_free(data); } else { printf("Failed to load texture %s\n", path); stbi_image_free(data); } return textureID; } int main() { // 初始化GLFW glfwInit(); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "OpenGL Image Display", NULL, NULL); if (!window) { printf("Failed to create GLFW window\n"); glfwTerminate(); return -1; } glfwMakeContextCurrent(window); // 加载GLAD if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { printf("Failed to initialize GLAD\n"); return -1; } // 设置视口 glViewport(0, 0, WIDTH, HEIGHT); // 创建并编译着色器程序 unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vertexShader, 1, &vertexShaderSource, NULL); glCompileShader(vertexShader); unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL); glCompileShader(fragmentShader); unsigned int shaderProgram = glCreateProgram(); glAttachShader(shaderProgram, vertexShader); glAttachShader(shaderProgram, fragmentShader); glLinkProgram(shaderProgram); glDeleteShader(vertexShader); glDeleteShader(fragmentShader); // 定义顶点数据 float vertices[] = { // 位置 // 纹理坐标 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, // 左下角 -1.0f, 1.0f, 0.0f, 0.0f, 1.0f // 左上角 }; unsigned int indices[] = { 0, 1, 2, 2, 3, 0 }; unsigned int VBO, VAO, EBO; glGenVertexArrays(1, &VAO); glGenBuffers(1, &VBO); glGenBuffers(1, &EBO); glBindVertexArray(VAO); glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); // 位置属性 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0); glEnableVertexAttribArray(0); // 纹理坐标属性 glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float))); glEnableVertexAttribArray(1); glBindVertexArray(0); // 加载纹理 unsigned int texture = loadTexture("your_image.png"); // 替换为你的图片路径 while (!glfwWindowShouldClose(window)) { glClearColor(0.2f, 0.3f, 0.3f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); // 激活着色器程序 glUseProgram(shaderProgram); // 绑定纹理 glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, texture); glUniform1i(glGetUniformLocation(shaderProgram, "ourTexture"), 0); // 绘制矩形 glBindVertexArray(VAO); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); glfwSwapBuffers(window); glfwPollEvents(); } glDeleteVertexArrays(1, &VAO); glDeleteBuffers(1, &VBO); glDeleteBuffers(1, &EBO); glDeleteProgram(shaderProgram); glfwDestroyWindow(window); glfwTerminate(); return 0; } ``` ### 编译与运行 确保你已经下载了 `stb_image.h` 并将其放置在项目目录中,然后使用如下命令编译: ```bash gcc main.c -o display -lglfw -lGL -lm -ldl ./display ``` ### 解释说明 - **GLFW** 用于创建窗口并处理输入事件。 - **GLAD** 负责加载 OpenGL 函数指针。 - **纹理加载** 使用 `stb_image.h` 库来读取图像文件。 - **着色器** 分为顶点着色器和片段着色器,负责将纹理映射到几何体上。 - **绘制过程** 将纹理应用到一个四边形上,并通过 OpenGL 渲染出来。 此示例展示了如何在 Linux 系统下使用 OpenGL 显示图像的基本方法,适用于入门级开发者学习和实践[^1]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

字节流动

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值