该原创文章首发于微信公众号:字节流动

什么是 FBO

FBO(Frame Buffer Object)即帧缓冲区对象,实际上是一个可添加缓冲区的容器,可以为其添加纹理或渲染缓冲区对象(RBO)。

FBO 本身不能用于渲染,只有添加了纹理或者渲染缓冲区之后才能作为渲染目标,它仅且提供了 3 种附着(Attachment),分别是颜色附着、深度附着和模板附着。

RBO(Render Buffer Object)即渲染缓冲区对象,是一个由应用程序分配的 2D 图像缓冲区。渲染缓冲区可以用于分配和存储颜色、深度或者模板值,可以用作 FBO 中的颜色、深度或者模板附着。

使用 FBO 作为渲染目标时,首先需要为 FBO 的附着添加连接对象,如颜色附着需要连接纹理或者渲染缓冲区对象的颜色缓冲区。

为什么用 FBO

默认情况下,OpenGL ES 通过绘制到窗口系统提供的帧缓冲区,然后将帧缓冲区的对应区域复制到纹理来实现渲染到纹理,但是此方法只有在纹理尺寸小于或等于帧缓冲区尺寸才有效。

另一种方式是通过使用连接到纹理的 pbuffer 来实现渲染到纹理,但是与上下文和窗口系统提供的可绘制表面切换开销也很大。因此,引入了帧缓冲区对象 FBO 来解决这个问题。

​Android OpenGL ES 开发中,一般使用 GLSurfaceView 将绘制结果显示到屏幕上,然而在实际应用中,也有许多场景不需要渲染到屏幕上,如利用 GPU 在后台完成一些图像转换、缩放等耗时操作,这个时候利用 FBO 可以方便实现类似需求。

使用 FBO 可以让渲染操作不用再渲染到屏幕上,而是渲染到离屏 Buffer 中,然后可以使用 glReadPixels 或者 HardwareBuffer 将渲染后的图像数据读出来,从而实现在后台利用 GPU 完成对图像的处理。

怎么用 FBO

创建并初始化 FBO 的步骤:

// 创建一个 2D 纹理用于连接 FBO 的颜色附着
glGenTextures(1, &m_FboTextureId);
glBindTexture(GL_TEXTURE_2D, m_FboTextureId);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, GL_NONE);// 创建 FBO
glGenFramebuffers(1, &m_FboId);
// 绑定 FBO
glBindFramebuffer(GL_FRAMEBUFFER, m_FboId);
// 绑定 FBO 纹理
glBindTexture(GL_TEXTURE_2D, m_FboTextureId);
// 将纹理连接到 FBO 附着
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_FboTextureId, 0);
// 分配内存大小
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_RenderImage.width, m_RenderImage.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
// 检查 FBO 的完整性状态
if (glCheckFramebufferStatus(GL_FRAMEBUFFER)!= GL_FRAMEBUFFER_COMPLETE) {LOGCATE("FBOSample::CreateFrameBufferObj glCheckFramebufferStatus status != GL_FRAMEBUFFER_COMPLETE");return false;
}
// 解绑纹理
glBindTexture(GL_TEXTURE_2D, GL_NONE);
// 解绑 FBO
glBindFramebuffer(GL_FRAMEBUFFER, GL_NONE);

使用 FBO 的一般步骤:

// 绑定 FBO
glBindFramebuffer(GL_FRAMEBUFFER, m_FboId);// 选定离屏渲染的 Program,绑定 VAO 和图像纹理,进行绘制(离屏渲染)
// m_ImageTextureId 为另外一个用于纹理映射的图片纹理
glUseProgram(m_FboProgramObj);
glBindVertexArray(m_VaoIds[1]);
glActiveTexture(GL_TEXTURE0);
// 绑定图像纹理
glBindTexture(GL_TEXTURE_2D, m_ImageTextureId);
glUniform1i(m_FboSamplerLoc, 0);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, (const void *)0);
glBindVertexArray(0);
glBindTexture(GL_TEXTURE_2D, 0);// 解绑 FBO
glBindFramebuffer(GL_FRAMEBUFFER, 0);// 完成离屏渲染后,结果图数据便保存在我们之前连接到 FBO 的纹理 m_FboTextureId 。
// 我们再拿 FBO 纹理 m_FboTextureId 做一次普通渲染便可将之前离屏渲染的结果绘制到屏幕上。
// 这里我们编译连接了 2 个 program ,一个用作离屏渲染的 m_FboProgramObj,一个用于普通渲染的 m_ProgramObj//选定另外一个着色器程序,以 m_FboTextureId 纹理作为输入进行普通渲染
glUseProgram(m_ProgramObj);
glBindVertexArray(m_VaoIds[0]);
glActiveTexture(GL_TEXTURE0);
//绑定 FBO 纹理
glBindTexture(GL_TEXTURE_2D, m_FboTextureId);
glUniform1i(m_SamplerLoc, 0);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, (const void *)0);
glBindTexture(GL_TEXTURE_2D, GL_NONE);
glBindVertexArray(GL_NONE);

示例:

  1. 创建并初始化 FBO
bool FBOSample::CreateFrameBufferObj()
{// 创建并初始化 FBO 纹理glGenTextures(1, &m_FboTextureId);glBindTexture(GL_TEXTURE_2D, m_FboTextureId);glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);glBindTexture(GL_TEXTURE_2D, GL_NONE);// 创建并初始化 FBOglGenFramebuffers(1, &m_FboId);glBindFramebuffer(GL_FRAMEBUFFER, m_FboId);glBindTexture(GL_TEXTURE_2D, m_FboTextureId);glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_FboTextureId, 0);glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_RenderImage.width, m_RenderImage.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);if (glCheckFramebufferStatus(GL_FRAMEBUFFER)!= GL_FRAMEBUFFER_COMPLETE) {LOGCATE("FBOSample::CreateFrameBufferObj glCheckFramebufferStatus status != GL_FRAMEBUFFER_COMPLETE");return false;}glBindTexture(GL_TEXTURE_2D, GL_NONE);glBindFramebuffer(GL_FRAMEBUFFER, GL_NONE);return true;}
  1. 编译链接 2 个着色器程序,创建 VAO、VBO 和图像纹理
void FBOSample::Init()
{//顶点坐标GLfloat vVertices[] = {-1.0f, -1.0f, 0.0f,1.0f, -1.0f, 0.0f,-1.0f,  1.0f, 0.0f,1.0f,  1.0f, 0.0f,};//正常纹理坐标GLfloat vTexCoors[] = {0.0f, 1.0f,1.0f, 1.0f,0.0f, 0.0f,1.0f, 0.0f,};//fbo 纹理坐标与正常纹理方向不同,原点位于左下角GLfloat vFboTexCoors[] = {0.0f, 0.0f,1.0f, 0.0f,0.0f, 1.0f,1.0f, 1.0f,};GLushort indices[] = { 0, 1, 2, 1, 3, 2 };char vShaderStr[] ="#version 300 es                            \n""layout(location = 0) in vec4 a_position;   \n""layout(location = 1) in vec2 a_texCoord;   \n""out vec2 v_texCoord;                       \n""void main()                                \n""{                                          \n""   gl_Position = a_position;               \n""   v_texCoord = a_texCoord;                \n""}                                          \n";// 用于普通渲染的片段着色器脚本,简单纹理映射char fShaderStr[] ="#version 300 es\n""precision mediump float;\n""in vec2 v_texCoord;\n""layout(location = 0) out vec4 outColor;\n""uniform sampler2D s_TextureMap;\n""void main()\n""{\n""    outColor = texture(s_TextureMap, v_texCoord);\n""}";// 用于离屏渲染的片段着色器脚本,取每个像素的灰度值char fFboShaderStr[] ="#version 300 es\n""precision mediump float;\n""in vec2 v_texCoord;\n""layout(location = 0) out vec4 outColor;\n""uniform sampler2D s_TextureMap;\n""void main()\n""{\n""    vec4 tempColor = texture(s_TextureMap, v_texCoord);\n""    float luminance = tempColor.r * 0.299 + tempColor.g * 0.587 + tempColor.b * 0.114;\n""    outColor = vec4(vec3(luminance), tempColor.a);\n""}"; // 输出灰度图// 编译链接用于普通渲染的着色器程序m_ProgramObj = GLUtils::CreateProgram(vShaderStr, fShaderStr, m_VertexShader, m_FragmentShader);// 编译链接用于离屏渲染的着色器程序m_FboProgramObj = GLUtils::CreateProgram(vShaderStr, fFboShaderStr, m_FboVertexShader, m_FboFragmentShader);if (m_ProgramObj == GL_NONE || m_FboProgramObj == GL_NONE){LOGCATE("FBOSample::Init m_ProgramObj == GL_NONE");return;}m_SamplerLoc = glGetUniformLocation(m_ProgramObj, "s_TextureMap");m_FboSamplerLoc = glGetUniformLocation(m_FboProgramObj, "s_TextureMap");// 生成 VBO ,加载顶点数据和索引数据// Generate VBO Ids and load the VBOs with dataglGenBuffers(4, m_VboIds);glBindBuffer(GL_ARRAY_BUFFER, m_VboIds[0]);glBufferData(GL_ARRAY_BUFFER, sizeof(vVertices), vVertices, GL_STATIC_DRAW);glBindBuffer(GL_ARRAY_BUFFER, m_VboIds[1]);glBufferData(GL_ARRAY_BUFFER, sizeof(vTexCoors), vTexCoors, GL_STATIC_DRAW);glBindBuffer(GL_ARRAY_BUFFER, m_VboIds[2]);glBufferData(GL_ARRAY_BUFFER, sizeof(vFboTexCoors), vFboTexCoors, GL_STATIC_DRAW);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_VboIds[3]);glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);GO_CHECK_GL_ERROR();// 生成 2 个 VAO,一个用于普通渲染,另一个用于离屏渲染// Generate VAO IdsglGenVertexArrays(2, m_VaoIds);// 初始化用于普通渲染的 VAO// Normal rendering VAOglBindVertexArray(m_VaoIds[0]);glBindBuffer(GL_ARRAY_BUFFER, m_VboIds[0]);glEnableVertexAttribArray(VERTEX_POS_INDX);glVertexAttribPointer(VERTEX_POS_INDX, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (const void *)0);glBindBuffer(GL_ARRAY_BUFFER, GL_NONE);glBindBuffer(GL_ARRAY_BUFFER, m_VboIds[1]);glEnableVertexAttribArray(TEXTURE_POS_INDX);glVertexAttribPointer(TEXTURE_POS_INDX, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), (const void *)0);glBindBuffer(GL_ARRAY_BUFFER, GL_NONE);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_VboIds[3]);GO_CHECK_GL_ERROR();glBindVertexArray(GL_NONE);// 初始化用于离屏渲染的 VAO// FBO off screen rendering VAOglBindVertexArray(m_VaoIds[1]);glBindBuffer(GL_ARRAY_BUFFER, m_VboIds[0]);glEnableVertexAttribArray(VERTEX_POS_INDX);glVertexAttribPointer(VERTEX_POS_INDX, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (const void *)0);glBindBuffer(GL_ARRAY_BUFFER, GL_NONE);glBindBuffer(GL_ARRAY_BUFFER, m_VboIds[2]);glEnableVertexAttribArray(TEXTURE_POS_INDX);glVertexAttribPointer(TEXTURE_POS_INDX, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), (const void *)0);glBindBuffer(GL_ARRAY_BUFFER, GL_NONE);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_VboIds[3]);GO_CHECK_GL_ERROR();glBindVertexArray(GL_NONE);// 创建并初始化图像纹理glGenTextures(1, &m_ImageTextureId);glBindTexture(GL_TEXTURE_2D, m_ImageTextureId);glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);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_RenderImage.width, m_RenderImage.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, m_RenderImage.ppPlane[0]);glBindTexture(GL_TEXTURE_2D, GL_NONE);GO_CHECK_GL_ERROR();if (!CreateFrameBufferObj()){LOGCATE("FBOSample::Init CreateFrameBufferObj fail");return;}}
  1. 离屏渲染和普通渲染
void FBOSample::Draw(int screenW, int screenH)
{// 离屏渲染glPixelStorei(GL_UNPACK_ALIGNMENT,1);glViewport(0, 0, m_RenderImage.width, m_RenderImage.height);// Do FBO off screen renderingglBindFramebuffer(GL_FRAMEBUFFER, m_FboId);glUseProgram(m_FboProgramObj);glBindVertexArray(m_VaoIds[1]);glActiveTexture(GL_TEXTURE0);glBindTexture(GL_TEXTURE_2D, m_ImageTextureId);glUniform1i(m_FboSamplerLoc, 0);GO_CHECK_GL_ERROR();glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, (const void *)0);GO_CHECK_GL_ERROR();glBindVertexArray(0);glBindTexture(GL_TEXTURE_2D, 0);glBindFramebuffer(GL_FRAMEBUFFER, 0);// 普通渲染// Do normal renderingglViewport(0, 0, screenW, screenH);glUseProgram(m_ProgramObj);GO_CHECK_GL_ERROR();glBindVertexArray(m_VaoIds[0]);glActiveTexture(GL_TEXTURE0);glBindTexture(GL_TEXTURE_2D, m_FboTextureId);glUniform1i(m_SamplerLoc, 0);GO_CHECK_GL_ERROR();glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, (const void *)0);GO_CHECK_GL_ERROR();glBindTexture(GL_TEXTURE_2D, GL_NONE);glBindVertexArray(GL_NONE);}

渲染结果图:

联系与交流

技术交流、获取源码可以扫码添加我的微信:Byte-Flow ,领取视频教程

NDK OpenGL ES 3.0 开发(五):FBO 离屏渲染相关推荐

  1. NDK OpenGL ES 3.0 开发(一):绘制一个三角形

    该原创文章首发于微信公众号:字节流动 什么是 OpenGLES OpenGLES 全称 OpenGL for Embedded Systems ,是三维图形应用程序接口 OpenGL 的子集,本质上是 ...

  2. NDK OpenGL ES 3.0 开发(二十):3D 模型

    该原创文章首发于微信公众号:字节流动 OpenGLES 3D 模型 OpenGLES 3D 模型本质上是由一系列三角形在 3D 空间(OpenGL 坐标系)中构建而成,另外还包含了用于描述三角形表面的 ...

  3. NDK OpenGL ES 3.0 开发(二十二):PBO

    该原创文章首发于微信公众号:字节流动 PBO 是什么 OpenGL PBO(Pixel Buffer Object),被称为像素缓冲区对象,主要被用于异步像素传输操作.PBO 仅用于执行像素传输,不连 ...

  4. NDK OpenGL ES 3.0 开发(八):坐标系统

    该原创文章首发于微信公众号:字节流动 OpenGL 坐标系统 我们知道 OpenGL 坐标系中每个顶点的 x,y,z 坐标都应该在 -1.0 到 1.0 之间,超出这个坐标范围的顶点都将不可见. 将一 ...

  5. NDK OpenGL ES 3.0 开发(七):Transform Feedback

    该原创文章首发于微信公众号:字节流动 什么是 Transform Feedback Transform Feedback(变换反馈)是在 OpenGLES3.0 渲染管线中,顶点处理阶段结束之后,图元 ...

  6. NDK OpenGL ES 3.0 开发(四):VBO、EBO 和 VAO

    该原创文章首发于微信公众号:字节流动 VBO 和 EBO VBO(Vertex Buffer Object)是指顶点缓冲区对象,而 EBO(Element Buffer Object)是指图元索引缓冲 ...

  7. NDK OpenGL ES 3.0 开发(十七):相机基础滤镜

    该原创文章首发于微信公众号:字节流动 相机基础滤镜 上文中我们通过 ImageReader 获取到 Camera2 预览的 YUV 数据,然后利用 OpenGLES 渲染实现相机预览,这一节将利用 G ...

  8. win7下搭建opengl es 2.0开发环境

    原文  http://codingnow.cn/opengles/1501.html 主题 OpenGL ES Windows 7 1. 下载AMD的OpenGL ES2.0的模拟器 ,下载地址:  ...

  9. OpenGL ES 3.0(五)纹理

    我们可以为每个顶点添加颜色来增加图形的细节,从而创建出有趣的图像.但是,如果想让图形看起来更真实,我们就必须有足够多的顶点,从而指定足够多的颜色.这将会产生很多额外开销,因为每个模型都会需求更多的顶点 ...

  10. OpenGL ES 3.0 开发(三):YUV 渲染

    该原创文章首发于微信公众号:字节流动 YUV 渲染原理 前面文章一文掌握 YUV 图像的基本处理介绍了 YUV 常用的基本格式,本文以实现 NV21/NV12 的渲染为例. 前文提到,YUV 图不能直 ...

最新文章

  1. Oracle高级查询之OVER (PARTITION BY ..)
  2. [MySQL实践] 实践记录
  3. 如何在Kubernetes中暴露服务访问
  4. TCP三次握手、糊涂窗口、粘包问题
  5. monkey的具体使用及详细说明
  6. 图神经网络(一)图信号处理与图卷积神经网络(5)图卷积神经网络
  7. 浅谈面向对象思想下的 C 语言
  8. mysql操作常用技巧
  9. 有了这个运维方案,让IT信息化人员头疼的系统宕机再也没出现
  10. window下遍历并修改文件
  11. c语言100块钱买100只鸡算法,JS计算输出100元钱买100只鸡问题的解决方法
  12. vb net excel 剪贴板 粘贴_利用剪贴板强化 Excel 计算
  13. scala学习手记2 - scala中的循环
  14. 运动目标跟踪(十二)--KCF跟踪及CSK,CN对比
  15. python按字节读文件-使用Python进行二进制文件读写(转)
  16. 深圳大学C C++ 数据结构题目答案清单完整题库(含期中模拟 期末模拟 期末真题 考前练习)
  17. ykhmi是什么触摸屏软件_一体机使用中常见问题-中达优控|YKHMI|触摸屏|一体机|云组态-深圳市中达优控科技有限公司 —— YKHMI|触摸屏|一体机|云组态...
  18. Abaqus帮助文档翻译——Abaqus/CAE主窗口介绍
  19. 串行通信接口详细描述
  20. 新倩女幽魂服务器维护,《倩女幽魂Online》更新公告(版本1.0.23)

热门文章

  1. 给不给你升职,领导关心的是什么?
  2. java仿制百度云盘_基于jsp的仿百度网盘-JavaEE实现仿百度网盘 - java项目源码
  3. 八戒,别以为你站在路灯下就是夜明猪了!
  4. 推荐系统遇上深度学习(十二)--推荐系统中的EE问题及基本Bandit算法
  5. popupwindow拦截点击物理返回键
  6. 架构师之殇:一个反对“平台团队”的案例
  7. VRP基础介绍:VRP基础
  8. 如何正确地在Spring Data JPA和Jackson中用上Java 8的时间相关API(即JSR 310也即java.time包下的众神器)...
  9. 使用PlotNeuralNet绘制自己的网络结构图
  10. 阿里云上部署kafka--遇到的坑