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

2022-11-29 18:12:07 浏览数 (1)

这个公众号会路线图式的遍历分享音视频技术音视频基础音视频工具音视频工程示例音视频工业实战关注一下成本不高,错过干货损失不小 ↓↓↓


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

8、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 上的时候,所有渲染命令会写入到纹理上,就像它是一个普通的颜色/深度或者模板缓冲一样。使用纹理的好处是,所有渲染操作的结果都会被储存为一个纹理图像,这样我们就可以简单的在着色器中使用了。

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

代码语言:javascript复制
// 创建和绑定 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 附件的例子:

代码语言:javascript复制
// 创建和绑定 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 Advanced OpenGL/05 Framebuffers/

[2]

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

[3]

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

- 完 -

0 人点赞