OpenGL 渲染 YUYV(YUV422)

一 、YUV422数据格式

在这里插入图片描述

图片来源于谷歌

结合上图可以看出YUV422的特点如下:
①在横向上是两个亮度(Y)共享一组色度(UV), 所以UV的宽度是Y的1/2, 高度相等。
②在内存中YUV422是交替连续存储的, 即内存中只需要一个地址保存每一帧的视频数据,在内存中的分布如下:
在这里插入图片描述

图片来源于谷歌

二 、GLFW渲染YUV422

2.1 定义顶点数据

  YUV422和之前文章中渲染YUV420P一样, 定义四个顶点数据, 然后用索引来绘制两个三角形。

	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);

	glBindVertexArray(m_vertex_array);

2.2 创建YUYV纹理

  因为YUYV在内存中是交替分布(packed)的, 所以这里建一张纹理就可以了。 为了方便计算, 使用 OpenGL建纹理时指定RGBA像素格式,这样刚好存储 YUYV 一个完整的像素单元, 纹理宽度就是YUYV帧宽度的一半。

   glGenTextures(1, &m_tex_yuyv);
   glBindTexture(GL_TEXTURE_2D, m_tex_yuyv);
   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
   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);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
   glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_tex_width/2, m_tex_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
   glGenerateMipmap(GL_TEXTURE_2D);
   m_shader_parse.setInt("tex_yuyv", 0);

2.3 上行YUYV数据

   glActiveTexture(GL_TEXTURE0);
   glBindTexture(GL_TEXTURE_2D, m_tex_yuyv);
   glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_tex_width/2 , m_tex_height, GL_RGBA, GL_UNSIGNED_BYTE, yuyv);
   m_shader_parse.setInt("tex_yuyv", 0);

2.4 渲染YUYV

   glActiveTexture(GL_TEXTURE0);
   glBindTexture(GL_TEXTURE_2D, m_tex_yuyv);

   glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

2.5 着色器

  顶点着色器和YUV420P的顶点shader一致,因为数据是 YUYV 格式,然后纹理定义的是RGBA, 所以纹理采样一次就可以得到两个像素的采样值。

/顶点着色器///
#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 core

out vec4 frage_color1;

in vec2 tex_uv;

uniform sampler2D tex_yuyv;


uniform vec4 color_vec0;
uniform vec4 color_vec1;
uniform vec4 color_vec2;

uniform vec3 color_range_min;
uniform vec3 color_range_max;

uniform int rt_width;


void main()
{

   vec4 yuyv = texture2D(tex_yuyv, tex_uv);
   vec2 yy = yuyv.xz;
   vec2 cbcr = yuyv.yw;

   vec2 tex_size = textureSize(tex_yuyv, 0);
   float factor = tex_size.x/rt_width;
   float cur_y = (tex_uv.x * factor) < 0.5f ? yy.x : yy.y;
   
   vec3 yuv = vec3(cur_y, cbcr);
   yuv = clamp(yuv, color_range_min, color_range_max);

   vec3 rgb = vec3(0.f);
   rgb.r = dot(color_vec0.xyz, yuv) + color_vec0.w;
   rgb.g = dot(color_vec1.xyz, yuv) + color_vec1.w;
   rgb.b = dot(color_vec2.xyz, yuv) + color_vec2.w;

   frage_color1 = vec4(rgb, 1.f);
}

2.6 设置渲染用到的参数

之前都在 shader 里面固定了,现在改为在 CPU 计算好转换矩阵再上传到 GPU里面去

   m_shader_parse.setVec3("color_range_min", color_range_min);
   GLenum errorcode = glGetError();
   m_shader_parse.setVec3("color_range_max", color_range_max);
   errorcode = glGetError();
   m_shader_parse.setVec4("color_vec0", color_vec0);
   errorcode = glGetError();
   m_shader_parse.setVec4("color_vec1", color_vec1);
   errorcode = glGetError();
   m_shader_parse.setVec4("color_vec2", color_vec2);
   errorcode = glGetError();

   m_shader_parse.setInt("rt_width", m_tex_width);

三、代码地址以及存在的问题(已解决)

  经过上述方式用 GLFW 渲染 YUYV后, 画面变得有些模糊, 我猜原因应该是片元着色器中采样没对, 但是查了较多资料也没找到解决方法,各位老师看过后能不能纠正一下哪里不正确。
代码地址为:https://github.com/pengguoqing/samples_code/tree/master/OpenGL/yuv422

本demo的渲染效果如下, 对比字体可以发现渲染结果变模糊了

在这里插入图片描述

正确的YUV420P的渲染效果
在这里插入图片描述
原因和解决方案:shader 中使用 texture 采样时会自动进行插值,这就会导致 YUV 不是严格严格匹配的,于是模糊就发生了。完整方案在后续这篇文章中有详细介绍: OpenGL使用FBO与PBO上行纹理 (YUYV).

  • 1
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值