OpenGL使用FBO与PBO上行纹理 (YUYV)
本文记录在OpenGL中使用 帧缓冲 (FBO)和像素缓冲(PBO)来上行视频帧数据(YUYV), 并最终渲染显示出来。在之前的文章中介绍了渲染 YUV420 和 YUYV 的流程,不过都是用的默认的帧缓冲,即创建一个和视频幅面一样的 GLFW 窗口, 但是在实际开发中会遇到各种幅面的素材,,此时默认的 GLFW 帧缓冲就不满足要求了, 所以需要借助自定义的 FBO 来完成各种幅面视频帧的上行。
- 一、 帧缓冲(FBO)
- 二、 像素缓冲(PBO)
- 三、 关于YUYV纹理的采样
- 四、 测试 Demo
- 五、 总结
本文相关详细代码地址: https://github.com/pengguoqing/samples_code/tree/master/OpenGL
一、 帧缓冲(FBO)
关于帧缓冲, LearnOpenGL 官方网站已经介绍得比较详细了, 这里就不再赘述。上行视频帧数据主要就是用到帧缓冲的颜色缓冲。首先根据视频帧的幅面为帧缓冲创建一个同幅面的纹理附件,FBO 创建纹理附件和渲染缓冲附件的流程都大体相似,所以就直接封装了一个简单的 CXFBO的 C++ 类,只需要简单的设置纹理附件的宽高参数即可,使用方式如下:
CXFbo yuy2fbo;yuy2fbo.InitFbo(imgwidth, imgheight);yuy2fbo.UnBindFbo();
初始化的部分代码如下:
glGenTextures(1, &m_colortex);glBindTexture(GL_TEXTURE_2D, m_colortex);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_width, m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_colortex, 0);
二、 像素缓冲(PBO)
关于PBO这篇文章有非常详细的介绍: http://www.songho.ca/opengl/gl_pbo.html, 这里贴出原文中一张能简单清晰的阐述 PBO 原理的示意图:
比如 CPU 解码出一帧 YUV 数据后, 如果使用 PBO上行的话那么CPU 只需要运算将 YUV 数据拷贝至 像素缓冲区这一步, 后续流程都由利用 GPU 强大的并行运行能力将像素缓冲区数据加载到GPU显存中。 与不使用 PBO相比, CPU 只需要进行一次拷贝即可, 否则整个过程都由 CPU 控制,时钟周期这一缺点就显露出来了。这里贴出封装的 PBO初始化流程和调用接口:
public:bool Init(uint32_t width, uint32_t height, PBOTYPE type, GLenum pixfmt);void Map(uint8_t** ptr, uint32_t* linesize) const;void UnMap() const;void Bind() const;void UnBind() const;
bool CXPbo::Init(uint32_t width, uint32_t height, PBOTYPE type, GLenum pixfmt)
{m_width = width;m_hegit = height;m_pixfmt = pixfmt;switch (type){case PBOTYPE::kDynamic:m_rwtype = GL_PIXEL_UNPACK_BUFFER;break;case PBOTYPE::kStage:m_rwtype = GL_PIXEL_PACK_BUFFER;break;default:return false;break;}glGenBuffers(1, &m_pbo);if (!m_pbo){return false;}glBindBuffer(m_rwtype, m_pbo);m_pbosize = m_width * GetPixfmtBpp(pixfmt) / 8;m_pbosize = (m_pbosize + 3) & 0xFFFFFFFC;m_pbosize *= m_hegit;glBufferData(m_rwtype, m_pbosize, nullptr, GL_STREAM_DRAW);glBindBuffer(m_rwtype, 0);return true;
}
三、 关于YUYV纹理的采样
在之前的文章中 OpenGL渲染YUYV, 因为默认的帧缓冲和实际的素材的幅面相同, 而且又是用的 RGBA 来存储一组完整的 YUYV 数据, 所以 shader 中使用 texture直接采样时会自动进行插值,这就会导致 YUV 不是 严格严格匹配的,正确的做法是需要使用 textuerLod来加载原始纹理,以保证YUV的严格匹配,shader 如下:
vec3 SampleYUYV(vec2 pos)
{ivec2 size = textureSize(image, 0);ivec2 actualpos = ivec2(pos.x, pos.y);vec2 actualuv = (vec2(actualpos.xy)+0.5)/size;vec4 yuyv = textureLod(image, actualuv, 0);float leftover = fract(pos.x);float y = (leftover<0.5f) ? yuyv.x : yuyv.z;vec3 yuv = vec3(y, yuyv.yw);return YUV_to_RGB(yuv);
}
四、 测试 Demo
首先准备一张 1280 * 960 幅面的 YUYV 数据,然后用 GLFW 创建一个 960 * 540 (16:9)的窗口用于显示。代码中使用宏来配置是否使用 PBO模式
#define USE_PBO true
渲染管线的 pass 如下:
while (!glfwWindowShouldClose(winhandle)){processInput(winhandle);glViewport(0, 0, imgwidth, imgheight);yuy2fbo.BindFbo();glClearColor(0.2f, 0.3f, 0.3f, 1.0f);glClear(GL_COLOR_BUFFER_BIT);uploadshader.use();UploadYUYV(yuy2pbo, imgdata, uploadtex);glBindVertexArray(uploadvao);glBindTexture(GL_TEXTURE_2D, uploadtex);glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);glBindTexture(GL_TEXTURE_2D, 0);yuy2fbo.UnBindFbo();glBindVertexArray(0);glClearColor(0.2f, 0.3f, 0.3f, 1.0f);glClear(GL_COLOR_BUFFER_BIT);glViewport(0, 0, wndwidth, wndheight);rendershader.use();glBindVertexArray(rendervao);yuy2fbo.BindColorTexture();glGenerateMipmap(GL_TEXTURE_2D);glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);yuy2fbo.UnBindColorTexture();glfwSwapBuffers(winhandle);glfwPollEvents();}
渲染结果如下:
使用 PBO后的纹理上行效率如下:
使用 glTexSubImage2D直接上行纹理的效率如下:
可以发现效率差别还是蛮大的。
五、 总结
① 使用 PBO进行纹理上行的效率要由于 glTexSubImage2D,但是测试的时候发现假如把 glMapBuffer的整个时间算上的话,两者其实是相差不多的。所以实际开发中可能需要 多个 PBO来交替上行纹理。
②渲染管线中存在多个 Pass 需要借助 帧缓冲来实现。
OpenGL使用FBO与PBO上行纹理 (YUYV)相关推荐
- OPENGL VBO,FBO和PBO
转载自:http://hi.baidu.com/bfiyubfpgibjpqq/item/0897eb6a7c7f2d0aa0cf0f22 VBO:顶点缓冲对像. VBO其实没有用到GPU运算,也就是 ...
- 音视频开发之旅(38) -使用FBO实现渲染到纹理(Render to texture)
目录 FBO基本知识 FBO实现渲染到纹理的流程 实践 遇到的问题 资料 收获 在之前的学习实践中我们把图片.视频.图形等渲染到屏幕时,采用的是直接屏幕上即默认的帧缓冲区,如果我们在渲染时不想直接渲染 ...
- OpenGL入门(四)之纹理Texture
本系列文章为Learn OpenGL个人学习总结! OpenGL入门(一)之认识OpenGL和创建Window OpenGL入门(二)之渲染管线pipeline,VAO.VBO和EBO OpenGL入 ...
- OpenGL学习笔记(十三):将纹理贴图应用到四边形上,对VAO/VBO/EBO/纹理/着色器的使用方式进行总结
原博主博客地址:http://blog.csdn.net/qq21497936 本文章博客地址:http://blog.csdn.net/qq21497936/article/details/7919 ...
- OpenGL ES VAO、VBO、EBO、FBO、PBO、TBO、UBO
面试中经常被问到的 OpenGL ES 对象,你知道的有哪些? 该原创文章首发于微信公众号:字节流动 VBO 和 EBO VBO(Vertex Buffer Object)是指顶点缓冲区对象,而 EB ...
- VBO、FBO、PBO 学习总结贴 (OpenGL ES)
这是几篇好文链接: VBO: http://blog.sina.com.cn/s/blog_4062094e0100aluv.html PBO: http://blog.sina.com.cn/s/b ...
- Android OpenGL ES 3.0 PBO像素缓冲区对象
1.什么是PBO OpenGL PBO(Pixel Buffer Object),被称为像素缓冲区对象,主要被用于异步像素传输操作.PBO 仅用于执行像素传输,不连接到纹理,且与 FBO (帧缓冲区对 ...
- opengl es java_java – 在Android OpenGL ES App中加载纹理
1)您应该根据需要分配尽可能多的纹理名称.一个用于您正在使用的每个纹理. 加载纹理是一项非常繁重的操作,会使渲染管道停顿.所以,你永远不应该在游戏循环中加载纹理.您应该在呈现纹理的应用程序状态之前具有 ...
- Qt OpenGL 旗帜效果(飘动的纹理)
这次教程中,我将教大家如何创建一个飘动的旗帜.我们所要创建的旗帜,说白了就是一个以正弦波方式运动的纹理映射图像.虽然不会很难,但效果确实很不错,希望大家能喜欢.当然这次教程是基于第06课的,希望大家确 ...
最新文章
- python高效 二分法查找
- Linux Platform Device and Driver
- JavaScript中的正则
- 原来 JS 也支持跟 Lua 语意一样的内嵌函数的闭包概念
- 你知道该如何搭建 AI 智能问答系统吗?
- Unity 官方教程2
- 大数据平台需求调研大纲模板
- 笔记本的计算机打开没有硬盘分区,电脑开机找不到(没有)活动分区怎么办?4个解决方法!...
- 收藏夹整理——程序员笔试面试
- 抓住那头牛(宽搜bfs)
- Teams app 的 SSO 机制
- 微信直连支付通道刷脸支付用户开通步骤
- 综述论文要写英文摘要吗_关于小论文的一些撰写建议!
- Android文本输入框EditText属性和方法说明
- Arcpy基础入门-2、arcpy的批处理功能
- Limit CPUID MAX
- 脚本防检测防封实战教程
- STC12C5A60S2自主适应时钟延时函数
- python爬虫m3u8#EXTM3U #EXT-X-VERSION:3 #EXT-X-TARGETDURATION:8 #EXT-X-MEDIA-SEQUENCE:0 #EXTINF:5.0000
- php7序列化,PHP内核层解析反序列化漏洞