- 一 、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);
修改后的渲染效果正确了