OpenGL渲染YUV420P

article/2025/9/29 7:23:44

  • 一 、YUV420P数据格式
  • 二 、GLFW渲染YUV420P
    • 2.1 定义顶点数据
    • 2.2 创建YUV三张纹理
    • 2.3上行YUV420数据
    • 2.4 渲染纹理
    • 2.5 着色器
  • 三、代码地址以及存在的问题
  • 四 、解决存在的问题

一 、YUV420P数据格式

在这里插入图片描述

图片来源于谷歌

结合上图可以看出YUV420P的特点如下:
①无论在横向还是纵向上都是两个亮度(Y)共享一组色度(UV),所以UV的宽度和高度都是Y的1/2
②在内存中有三片数据,也就是三个数据指针分别指向Y、U、V

  ffmpeg中avframe保存yuv420p的数据时是直接申请一整个的 image_size, 亮度数据y的地址是起始地址,u 在 y 的基础上偏移, v 在 u 的基础上偏移//以HD的yuv420p 8 bit数据为例uint8_t* yuv420_data = new [1920*1080*1.5]();uint8_t* data_y =  yuv420_data;uint8_t* data_u =  yuv420_data + 1920*1080;uint8_t* data_v =  data_u + 1920*1080/4;delete[] yuv420_data;

二 、GLFW渲染YUV420P

2.1 定义顶点数据

float vertex_coord_data[] = {-1.f, -1.f, 0.f,   0.f, 1.f,-1.f,  1.f, 0.f,   0.f, 0.f,1.f,  1.f, 0.f,   1.f, 0.f,1.f, -1.f, 0.f,   1.f, 1.f,};uint32_t vertx_index_data[] = {0, 1, 2,2, 3, 0};uint32_t m_vertex_buffer, m_index_buffer;glGenBuffers(1, &m_vertex_buffer);glGenBuffers(1, &m_index_buffer);glGenVertexArrays(1, &m_vertex_array);glBindVertexArray(m_vertex_array);glBindBuffer(GL_ARRAY_BUFFER, m_vertex_buffer);glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_coord_data), vertex_coord_data, GL_STATIC_DRAW);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_index_buffer);glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(vertx_index_data), vertx_index_data, 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);

2.2 创建YUV三张纹理

   // 纹理 yglGenTextures(1, &m_tex_y);glBindTexture(GL_TEXTURE_2D, m_tex_y);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_RED, m_tex_width, m_wnd_height, 0, GL_RED, GL_UNSIGNED_BYTE, nullptr);glGenerateMipmap(GL_TEXTURE_2D);// 纹理 uglGenTextures(1, &m_tex_u);glBindTexture(GL_TEXTURE_2D, m_tex_u);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_RED, m_tex_width/2, m_wnd_height/2, 0, GL_RED, GL_UNSIGNED_BYTE, nullptr);glGenerateMipmap(GL_TEXTURE_2D);// 纹理 vglGenTextures(1, &m_tex_v);glBindTexture(GL_TEXTURE_2D, m_tex_v);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_RED, m_tex_width/2, m_wnd_height/2, 0, GL_RED, GL_UNSIGNED_BYTE, nullptr);glGenerateMipmap(GL_TEXTURE_2D);

2.3上行YUV420数据

	glActiveTexture(GL_TEXTURE0);glBindTexture(GL_TEXTURE_2D, m_tex_y);glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_tex_width, m_tex_height, GL_RED, GL_UNSIGNED_BYTE, y);glActiveTexture(GL_TEXTURE1);glBindTexture(GL_TEXTURE_2D, m_tex_u);glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_tex_width/2, m_tex_height/2, GL_RED, GL_UNSIGNED_BYTE, u);glActiveTexture(GL_TEXTURE2);glBindTexture(GL_TEXTURE_2D, m_tex_v);glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_tex_width/2, m_tex_height/2, GL_RED, GL_UNSIGNED_BYTE, v);

2.4 渲染纹理

	glActiveTexture(GL_TEXTURE0);glBindTexture(GL_TEXTURE_2D, m_tex_y);glActiveTexture(GL_TEXTURE1);glBindTexture(GL_TEXTURE_2D, m_tex_u);glActiveTexture(GL_TEXTURE2);glBindTexture(GL_TEXTURE_2D, m_tex_v);glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

2.5 着色器

/顶点着色器///
#version 330 core
layout (location = 0) in vec3 vertex_pos;
layout (location = 1) in vec2 tex_pos;out vec2 tex_uv;void main()
{gl_Position = vec4(vertex_pos, 1.f);tex_uv = vec2(tex_pos);
}/像素着色器///
#version 330 coreout vec4 frage_color;in vec2 tex_uv;
uniform sampler2D tex_y;
uniform sampler2D tex_u;
uniform sampler2D tex_v;void main()
{vec3 yuv = vec3(0.f);//按照BT709的协议来转换YUV至RGByuv.x = texture2D(tex_y, tex_uv).r - 16.f/235.f;yuv.y = texture2D(tex_u, tex_uv).r - 128.f/240.f;yuv.z = texture2D(tex_v, tex_uv).r - 128.f/240.f;yuv = clamp(yuv, 0.f, 1.f);mat3 yuv_to_rgb = mat3(1.164f,  0.f,     1.793f,1.164f, -0.213f, -0.533f,1.164f,  2.112f,  0.f);vec3 rgb = yuv_to_rgb *yuv;frage_color = vec4(rgb.r, rgb.g, rgb.b, 1.f);
}

三、代码地址以及存在的问题

demo中使用封装好的 ffmpeg 来获取YUV420P数据,相关代码地址:https://github.com/pengguoqing/samples_code.git
使用demo中的代码渲染后的效果画面会变紫,我尝试了用其他YUV_RGB的转换矩阵,也是一样的会变紫,各位老师看过后能不能纠正一下哪里不正确

本demo的渲染效果如下:
在这里插入图片描述
正确渲染效果如下:
在这里插入图片描述

四 、解决存在的问题

  经过这两天的思考和排查,以及请教行业前辈终于知道原因了,具体如下:
①y、u、v 三张纹理被声明成了 uniform 类型, 所以每次更新纹理数后都需要更新一下

   glActiveTexture(GL_TEXTURE0);glBindTexture(GL_TEXTURE_2D, m_tex_y);glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_tex_width, m_tex_height, GL_RED, GL_UNSIGNED_BYTE, y);m_shader_parse.setInt("tex_y", 0);glActiveTexture(GL_TEXTURE1);glBindTexture(GL_TEXTURE_2D, m_tex_u);glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_tex_width/2, m_tex_height/2, GL_RED, GL_UNSIGNED_BYTE, u);m_shader_parse.setInt("tex_u", 1);glActiveTexture(GL_TEXTURE2);glBindTexture(GL_TEXTURE_2D, m_tex_v);glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_tex_width/2, m_tex_height/2, GL_RED, GL_UNSIGNED_BYTE, v);m_shader_parse.setInt("tex_v", 2);//或一次性通知更新//m_shader_parse.setInt("tex_y", 0);//m_shader_parse.setInt("tex_u", 1);//m_shader_parse.setInt("tex_v", 2);

② shader里面需要对左乘矩阵进行转置
  因为shader里面的向量是列向量,所以 yuv_rgb 的矩阵需要左乘 采样的 yuv纹理数据

    /*mat3 yuv_to_rgb = mat3(1.164f,  0.f,     1.793f,1.164f, -0.213f, -0.533f,1.164f,  2.112f,  0.f);*///709_YUV_to_RGBmat3 yuv_to_rgb = mat3(1.164f, 1.164f,  1.164f,0.f,    -0.213f, 2.112f,1.793f, -0.533f, 0.f);

 修改后的渲染效果正确了
在这里插入图片描述


http://chatgpt.dhexx.cn/article/HxgTkHgS.shtml

相关文章

详解 YUV 格式(I420/YUV420/NV12/NV12/YUV422)

转载自:划水型派大星 YUV (YCbCr)是一种像素格式,常见于视频编码与静态图像。与 RGB 格式(红-绿-蓝)相反,YUV 分别由一个称为 Y(相当于灰度)的“亮度”分量(…

ffmpeg学习笔记(2)-YUV420算法原理

ffmpeg学习笔记(2)——YUV420算法原理 一、YUV像素格式 YUV的组成格式如下图所示 Y:明亮度,即灰度值 U、V:颜色 YUV格式将亮度信息和色彩信息分离,没有色彩信息依然能够显示图像(没有色彩信息),很好的解…

音视频编解码: YUV存储格式中的YUV420P,YUV420SP,NV12, NV21理解(转)

概述 之前介绍了YUV码流的采样格式,下面分析下YUV码流的存储格式,YUV码流的存储格式与采样格式息息相关。总的来讲,YUV存储格式主要分为两种: planar 平面格式 指先连续存储所有像素点的 Y 分量,然后存储 U 分量&…

图像编码YUV420格式

目录 一、YUV简介 二、RGB 到 YUV 的转换 三、YUV采样 YUV 4:4:4 YUV 4:2:2 YUV 4:2:0 四、YUV 存储格式 基于 YUV 4:2:2 采样的格式 YUYV 格式 UYVY 格式 YUV 422P 格式 基于 YUV 4:2:0 采样的格式 YU12 和 YV12 格式 NV12 和 NV21 格式 一、YUV简介 YUV&#x…

YUV420数据格式详解

YUV简介 YUV格式有两大类:planar和packed。 对于planar的YUV格式,先连续存储所有像素点的Y,紧接着存储所有像素点的U,随后是所有像素点的V。 对于packed的YUV格式,每个像素点的Y,U,V是连续交叉存储的。 YUV分为三个分…

python 生产者消费者模式

生产者消费者模式概述 在并发编程中使用生产者和消费者模式能够解决大不多的并发问题。该模式通过平衡生产线程和消费线程的工作能力来提高程序的整体处理数据的速度。 为什么使用生产者消费者模式 在线程世界里,生产者就是生产数据的线程,消费者就是…

生产者-消费者模式

概述 生产者-消费者模式在系统交互方面,有几个特点: 1、系统解耦 2、解决并发问题 3、不需要关心对方系统何时处理数据,处理结果如何 下面用几个简单例子逐步说明。 简单例子 假设有两个系统,A系统和B系统,B系统需要依赖A系统产生的数据,也就是说,A系统产生数据后,必须把数…

生产者消费者模式及简单的运用场景

先考虑一个问题:服务端接受多个客户端提交的视频文件进行转码的操作,应该怎么设计? 由于转码比较花费时间,所以我们排除同步的想法。而转码需要用到的外部软件(exe文件),不能同时被多个线程用到…

Java多线程系列—生产者消费者模式的多种实现(04)

生产者消费者模式 我们先来看看什么是生产者消费者模式,生产者消费者模式是程序设计中非常常见的一种设计模式,被广泛运用在解耦、消息队列等场景。 在现实世界中,我们把生产商品的一方称为生产者,把消费商品的一方称为消费者,有时生产者的生产速度特别快,但消费者的消…

生产者消费者模式的Java实现

一.概述 1. 什么是“生产者消费者模式”? 生产线程负责生产,消费线程负责消费 生产线程和消费线程要达到均衡。 生产满了就不能继续生产了,必须让消费线程进行消费 消费完了就不能再消费了,必须让生产线程进行生产 这是一种特殊的…

实现生产者消费者模式的三种方式

什么是生产者消费者模式 简单来说,生产者消费者模式就是缓冲区。 那么这么做有两个好处,一个是解耦,第二个是平衡生产能力和消费能力的差,因为生产者和消费者的速度是不一样的,有了这个缓冲区就可以平衡这样一个落差&…

生产者消费者模式最佳实践

测试环境:ubuntu18.04opencv4.2Qt 一个生产者-消费者模式下的视频处理框架。基础结构:视频读取类线程不断读取视频帧,处理类线程对图像进行处理,之后通过信号与槽机制在主线程中显示。特点:视频读取、处理为独立线程&a…

生产者、消费者模式

架构设计:生产者/消费者模式[0]:概述 今天打算来介绍一下“生产者/消费者模式”,这玩意儿在很多开发领域都能派上用场。由于该模式很重要,打算分几个帖子来介绍。今天这个帖子先来扫盲一 把。如果你对这个模式已经比较…

生产者和消费者模式详解

★简介 生产者消费者模式并不是GOF提出的23种设计模式之一,23种设计模式都是建立在面向对象的基础之上的,但其实面向过程的编程中也有很多高效的编程模式,生产者消费者模式便是其中之一,它是我们编程过程中最常用的一种设计模式。…

生产者消费者模式+代码实现

在并发编程中使用生产者和消费者模式能够解决绝大多数并发问题。该模式通过平衡生产线程和消费线程的工作能力来提高程序的整体处理数据的速度。 为什么要使用生产者和消费者模式 在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程。在…

生产者消费者模式详解(转载)

★简介 在实际的软件开发过程中,经常会碰到如下场景:某个模块负责产生数据,这些数据由另一个模块来负责处理(此处的模块是广义的,可以是类、函数、线程、进程等)。产生数据的模块,就形象地称为生…

多线程之生产者消费者模式

文章目录 基本组成阻塞队列有界队列与无界队列ArrayBlockingQueueLinkedBlockingQueueSynchronousQueue 流量控制与信号量(Semaphore)双缓冲与Exchanger 基本组成 生产者:生产者的任务是生产产品,产品可以是数据,也可以是任务。(将产品存入传…

java 生产者消费者模式

java的生产者消费者模式,有三个部分组成,一个是生产者,一个是消费者,一个是缓存。 这么做有什么好处呢? 1.解耦(去依赖),如果是消费者直接调用生产者,那如果生产者的代码变动了,消费…

生产者消费者模式浅析

由于最近工作中,涉及到生产者消费者设计模式,对此有一些体会,所以总结一下,与大家分享。 什么是生产者消费者模式 在工作中,大家可能会碰到这样一种情况:某个模块负责产生数据,这些数据由另一个…

java实现生产者消费者模式

一: 什么是生产者消费者模型 生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费…