前言
上一篇文章学习了 VBO 顶点缓冲, 它解决了多次向 GPU 内存拷贝顶点数据带来性能损耗的问题
这里思考另一个问题, 当数据通过渲染管线输出到屏幕时, 我们如何拿到输出的数据呢?
带着这个问题, 我们了解一下 FBO 的相关知识
一. 什么是帧缓冲?
帧缓冲 (Framebuffer Object, 简称 FBO) 即用于存放渲染管线输出数据帧的缓冲
一个帧缓冲由颜色附件, 深度附件和模板附件组成
- 纹理可以作为颜色附件和深度附件使用
- 渲染缓冲可以作为深度附件和模板附件使用
Android 自带的帧缓冲使用 Surface/SurfaceTexture 描述, 对应一个 NativeWindow
二. 使用场景
- 在图像输出到屏幕之前, 需要对图像进行加工
- 滤镜
- 水印
- 不希望数据直接输出到屏幕, 希望能够拦截输出的数据
- 将数据发送到 MediaCodec 进行硬编
三. 操作流程
一) 创建缓冲帧
// 创建 fbo
int[] fboIds = new int[1];
GLES20.glGenBuffers(1, fboIds, 0);
int fboId = fboIds[0];
通过 glGenBuffers 函数, 便可以创建一个缓冲帧的对象, 通过传出参数 fboIds 输出
二) 绑定附件
1. 纹理附件
将纹理附件添加到缓冲帧上时, 所有渲染命令都会输出到纹理中, 所有的渲染结果都会存储为纹理图像
创建纹理对象
// 创建纹理
GLES20.glTexImage2D(
GLES20.GL_TEXTURE_2D,
0,
GLES20.GL_RGBA,
// 这里传入画布的宽高
width, height,
0,
GLES20.GL_RGBA,
GLES20.GL_UNSIGNED_BYTE,
null
);
添加纹理附件
// 为 fbo 添加附件
GLES20.glFramebufferTexture2D(
GLES20.GL_FRAMEBUFFER,
GLES20.GL_COLOR_ATTACHMENT0, // 颜色附件
GLES20.GL_TEXTURE_2D,
mTextureId,
0
);
- target: 一般为 GLES20.GL_FRAMEBUFFER
- attachment: 附件类型有如下三种
- 颜色附件: GL_COLOR_ATTACHMENT0
- 深度附件: GL_DEPTH_ATTACHMENT
- 模板附件: GL_STENCIL_ATTACHMENT
- textarget: 纹理的类型
- texture: 纹理的 id
- level: Mipmap level, 一般传 0
2. 渲染缓冲附件
除了可以绑定纹理附件之外, OpenGL 还引入了渲染缓冲对象, 它以原生渲染格式存储数据, 不会进行针对特定纹理格式的转换
因为存储的是原生数据, 因此进行 glSwapBuffers, 进行数据交换时比纹理附件更加高效
创建渲染缓冲对象
int[] renderbuffers = new int[1];
GLES20.glGenRenderbuffers(1, renderbuffers, 0);
int renderbuffer = renderbuffers[0];
添加渲染缓冲附件
// 绑定到当前渲染缓冲对象
GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER, renderbufferId);
// 初始化渲染数据存储
GLES20.glRenderbufferStorage(GLES20.GL_RENDERBUFFER, GLES20.GL_DEPTH_ATTACHMENT, width, height);
// 将渲染缓冲添加到 FBO 上
GLES20.glFramebufferRenderbuffer(GLES20.GL_FRAMEBUFFER, GLES20.GL_DEPTH_ATTACHMENT, GLES20.GL_RENDERBUFFER, renderbufferId);
三) 绘制到缓冲帧
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFramebufferId);
...... // 在此之间的代码将绘制到缓冲帧上面去
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
可以看到绘制到缓冲帧的方式非常简单, 只需要进行绑定 fboId 和解绑即可, 在其之间的代码都将渲染到缓冲帧上
渲染到缓冲帧上之后, 我们可以根据自己的场景自行判断是否需要输出到屏幕上, 因此即使没有屏幕作为交换介质, 依旧可以获取渲染的数据, 帧缓冲又称之为离屏渲染(off-screen rendering)
总结
通过对 FBO 的学习, 让我们简单的了解到了帧缓冲的使用方式, 以及使用场景
- 它的工作流程类似于一个 Hook, 相当于在输出到屏幕之前先将准备好的数据 Hook 到 FBO 上, 由它进行加工处理
FBO 的使用频率是非常高的, 我们可以通过 fbo 实现对纹理添加水印这样子类似的功能, 可以在之前的纹理绘制的基础上进行拓展
参考
- https://learnopengl-cn.readthedocs.io/zh/latest/04%20Advanced%20OpenGL/05%20Framebuffers/
- https://glumes.com/post/opengl/opengl-framebuffer-object-usage/