一看就懂的 OpenGL 基础概念(4):各种 O 之 FBO丨音视频基础

在前面的文章里,我们介绍了 OpenGL 在图形渲染应用中的角色,OpenGL 的渲染架构、状态机、渲染管线,以及 OpenGL 要在设备上实现渲染的桥梁 EDL,OpenGL 开发中帮助我们提升性能的 VBO/EBO/VAO 对象等内容。接下来我们来介绍另外一种重要的对象:FBO。

FBO

上面我们介绍了通过 VBO、EBO 和 VAO 管理渲染过程中的数据来优化渲染性能,接下来我们来介绍另一个重要的 XXO:帧缓冲区对象 FBO(Frame Buffer Object)

FBO 是用来做什么的呢?

在建立了 OpenGL 的渲染环境后,我们相当于有了一只画笔和一块默认的画布,这块画布就是我们的屏幕,是一块默认的帧缓冲区(Default Frame Buffer)。我们渲染的目的地是我们的屏幕,我们画出来的东西会显示在屏幕上。这个默认的帧缓冲区是与一系列缓冲区相关联的,具体有哪些缓冲区,多少位的缓冲区,是建立 OpenGL Context 的时候用户自定义的。一般来讲,必要的是颜色缓冲区和深度缓冲区,模板缓冲区、累加缓冲区是可选的。

后来随着新需求的需要,离屏渲染(Off-screen Render)技术开始出现,相较于直接渲染到屏幕,离屏渲染是先把物体绘制到『其他地方』而非屏幕上,而 OpenGL 则在某个版本引入了 FBO 可以支持离屏渲染。我们可以认为 OpenGL 的 FBO 就相当于是模拟了默认帧缓冲区的功能和结构创建了一种可以作为『画布』使用的 Object。也就是说,你可以把你想渲染的东西渲染到你生成的 FBO 里,而不是直接渲染到屏幕上。上面说的默认帧缓冲区关联的一系列其他缓冲区,FBO 也是可以有的,只是需要我们自己去创建、设置和绑定。

FBO 虽然也叫缓冲区对象,但是它并不是一个真正的缓冲区,因为 OpenGL 并没有为它分配存储空间去存储渲染所需的几何、像素数据,我们可以认为它是一个指针的集合,这些指针指向了颜色缓冲区、深度缓冲区、模板缓冲区、累积缓冲区等这些真正的缓冲区对象,我们把这里的『指向关系』叫做附着,而 FBO 中的附着点类型有:颜色附着深度附着模板附着。这些附着点指向的缓冲区通常包含在某些对象里,我们把这些对象叫做附件,附件的类型有:纹理(Texture)渲染缓冲区对象(Render Buffer Object,RBO)

图片
FBO 的附件和附着点
  • 纹理(Texture)是一个可以往上绘制细节的 2D 图片(甚至也有 1D 和 3D 的纹理),你可以想象纹理是一张绘有砖块的纸,无缝折叠贴合到你的 3D 的房子上,这样你的房子看起来就像有砖墙外表一样了。除了图像以外,纹理也可以被用来储存大量的数据,这些数据可以发送到着色器上进行计算和处理。
  • 渲染缓冲区对象(Render Buffer Object,RBO)则是一个由应用程序分配的 2D 图像缓冲区,可以分配和存储颜色、深度或者模板值,可以用作 FBO 中的颜色、深度或者模板附着。

FBO 是 OpenGL 渲染管线的最终目标,但其实 FBO 本身不直接用于渲染,而是要为其绑定好附件后才能作为渲染目标。所以,建构一个完整的 FBO 需要满足下列条件:

  • 必须往 FBO 里面加入至少一个附件(颜色、深度、模板缓冲);
  • 其中至少有一个是颜色附件;
  • 所有的附件都应该是已经完全做好的(已经存储在内存之中);
  • 每个缓冲都应该有同样数目的样本。

1)使用纹理附件

当把一个纹理(Texture)附加到 FBO 上的时候,所有渲染命令会写入到纹理上,就像它是一个普通的颜色/深度或者模板缓冲一样。使用纹理的好处是,所有渲染操作的结果都会被储存为一个纹理图像,这样我们就可以简单的在着色器中使用了。

下面是一个简单的使用纹理附件的例子:

// 创建和绑定 FBO:
GLuint fbo;
glGenFramebuffers(1, &fbo); // 创建 FBO
glBindFramebuffer(GL_FRAMEBUFFER, fbo); // 绑定 FBO,注意:如果这里用 glBindFramebuffer(GL_FRAMEBUFFER, 0) 则是激活默认的帧缓冲区

// 创建纹理:
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_width, m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); // 创建纹理和分配存储空间。传入 NULL 作为纹理的 data 参数,不填充数据,填充纹理数据会在渲染到 FBO 时去做。
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0);

// 将纹理添加为 FBO 的附件,连接在颜色附着点:
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);

// 检测 FBO:
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE)
    printf("Frame buffer incomplete!n");
else
    printf("Frame buffer complete!n");

// ...省略其他代码...

2)使用 RBO 附件

下面是一个简单的使用 RBO 附件的例子:

// 创建和绑定 FBO:
GLuint fbo;
glGenFramebuffers(1, &fbo); // 创建 FBO
glBindFramebuffer(GL_FRAMEBUFFER, fbo); // 绑定 FBO,注意:如果这里用 glBindFramebuffer(GL_FRAMEBUFFER, 0) 则是激活默认的帧缓冲区

// 创建 RBO:
GLuint rbo;
glGenRenderbuffers(1, &rbo); // 创建 RBO
glBindRenderbuffer(GL_RENDERBUFFER, rbo); // 绑定 RBO,所有后续渲染缓冲操作都会影响到当前的渲染缓冲对象
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA, m_width, m_height); // 为 RBO 的颜色缓冲区分配存储空间

// 将 RBO 添加为 FBO 的附件,连接在颜色附着点:
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbo);

// 检测 FBO:
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE)
    printf("Frame buffer incomplete!n");
else
    printf("Frame buffer complete!n");

// ...省略其他代码...

参考:

  • Learn OpenGL | 帧缓冲[1]
  • FBO 离屏渲染[2]
  • 关于 FBO 的一些理解[3]

参考资料

[1]Learn OpenGL | 帧缓冲: https://learnopengl-cn.readthedocs.io/zh/latest/04%20Advanced%20OpenGL/05%20Framebuffers/

[2]FBO 离屏渲染: https://blog.csdn.net/Kennethdroid/article/details/98883854[3]

关于 FBO 的一些理解: https://www.cnblogs.com/chandler00x/p/3886062.html

本文来源与公众号:关键帧Keyframe

版权声明:本文内容转自互联网,本文观点仅代表作者本人。本站仅提供信息存储空间服务,所有权归原作者所有。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至1393616908@qq.com 举报,一经查实,本站将立刻删除。

(0)

相关推荐

发表回复

登录后才能评论