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

什么是 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. 编程之美-队列中取最大值操作问题
  2. SQL Server 中关于 @@error 的一个小误区
  3. 电脑卡顿,最先升级这个硬件,运行速度可快速提升!
  4. 人工智能技术将重塑银行业
  5. 设计讨论:设计什么样的框架?
  6. [php基础]PHP环境变量$_SERVER和系统常量详细说明
  7. python下载numpy库_python怎么下载numpy
  8. K210学习笔记——语音识别
  9. 随机森林和多元线性回归R语言实现代码
  10. cubieboard mysql_Cubieboard开发笔记
  11. 删除SATA硬盘安全删除硬件图标
  12. 安卓Android手机校园外卖订餐系统毕业设计
  13. a different object with the same identifier value was already associated whith
  14. java日期的计算(当月剩余天数、获取日期当月的天数)
  15. requests模块
  16. 用Android Studio编写简易闹钟(一)
  17. CFileDialog文件对话框
  18. 计算机网络——计算机网络中的安全
  19. 广西大学计算机专业课代码,广西大学的代码是什么 广西大学代码
  20. 引导图(TapTargetView)

热门文章

  1. 深入浅出TensorFlow2函数——tf.data.Dataset.shuffle
  2. 不玩手机的步步高玩大数据:一条短信让你多买一只澳洲大龙虾
  3. unity 2022大三期末大作业 3D立体魔方游戏(附下载链接)
  4. html 实现3d效果代码,CSS3 3D环境实现立体 魔方效果代码
  5. 怎么找计算机驱动程序不正常怎么办,网卡驱动程序不正常怎么办,教您解决电脑网卡驱动程序不正常...
  6. Windows自带性能监控工具Perfmon使用介绍
  7. 计算机中求声音传输时间公式,计算机常用计算公式汇总
  8. LeetCode1419-数青蛙
  9. java 图文混排_图文混排的几种实现方案
  10. XPDL学习与分享 一