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

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

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

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

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

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

​NDK OpenGLES 开发中,一般使用 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);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
使用 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
示例:

创建并初始化 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);

// 创建并初始化 FBO
glGenFramebuffers(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
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
编译链接 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 data
glGenBuffers(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 Ids
glGenVertexArrays(2, m_VaoIds);
// 初始化用于普通渲染的 VAO
// Normal rendering VAO
glBindVertexArray(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 VAO
glBindVertexArray(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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
离屏渲染和普通渲染
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 rendering
glBindFramebuffer(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 rendering
glViewport(0, 0, screenW, screenH);
glUseProgram(m_ProgramObj);
GO_CHECK_GL_ERROR();
glBindVertexArray(m_VaoIds[0]);(http://www.amjmh.com)
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);

}

NDK OpenGLES3.0 开发(五):FBO 离屏渲染的更多相关文章

  1. 在VC++6.0开发中实现全屏显示

    全屏显示是一些应用软件程序必不可少的功能.比如在用VC++编辑工程源文件或编辑对话框等资源时,选择菜单“View\Full Screen”,即可进入全屏显示状态,按“Esc”键后会退出全屏显示状态. ...

  2. 离屏渲染学习笔记 /iOS圆角性能问题

    离屏渲染学习笔记 一.概念理解 OpenGL中,GPU屏幕渲染有以下两种方式: On-Screen Rendering 意为当前屏幕渲染,指的是GPU的渲染操作是在当前用于显示的屏幕缓冲区中进行. O ...

  3. 〖Linux〗使用Qt5.2.0开发Android的NDK应用程序

    2013年12月11日,Qt发布了其新的Qt版本:Qt5.2.0: 利用这个新的版本,我们可以很轻松地制作出Android手机的NDK应用程序. 开发环境:Ubuntu13.10 x86_64 下载链 ...

  4. Swift3.0服务端开发(五) 记事本的开发(iOS端+服务端)

    前边以及陆陆续续的介绍了使用Swift3.0开发的服务端应用程序的Perfect框架.本篇博客就做一个阶段性的总结,做一个完整的实例,其实这个实例在<Swift3.0服务端开发(一)>这篇 ...

  5. 从0开发3D引擎(五):函数式编程及其在引擎中的应用

    目录 上一篇博文 函数式编程的优点与缺点 优点 缺点 为什么使用Reason语言 函数式编程学习资料 引擎中相关的函数式编程知识点 数据 不可变数据 可变数据 函数 纯函数 高阶函数 柯西化 参考资料 ...

  6. 【Visual C++】游戏开发五十六 浅墨DirectX教程二十三 打造游戏GUI界面(一)

    本系列文章由zhmxy555(毛星云)编写,转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/16384009 作者:毛星云 ...

  7. Easyui + asp.net mvc + sqlite 开发教程(录屏)适合入门

    Easyui + asp.net mvc + sqlite 开发教程(录屏)适合入门 第一节: 前言(技术简介) EasyUI 是一套 js的前端框架 利用它可以快速的开发出好看的 前端系统 web ...

  8. android 5.0开发环境搭建

    Android 5.0 是 Google 于 2014 年 10 月 15 日发布的全新 Android 操作系统.本文将就最新的Android 5.0 开发环境搭建做详细介绍. 工具/原料 jdk- ...

  9. WPF Multi-Touch 开发:高级触屏操作(Manipulation)

    原文 WPF Multi-Touch 开发:高级触屏操作(Manipulation) 在上一篇中我们对基础触控操作有了初步了解,本篇将继续介绍触碰控制的高级操作(Manipulation),在高级操作 ...

随机推荐

  1. Vue学习笔记(一) 利用idea 搭建 vue 项目

    环境准备工作: 安装node.js 环境  -- 略 安装vue-li  全局安装vue-cli,在命令行中执行npm install -g vue-cli idea准备工作: 安装vue.js Fi ...

  2. vue入门:(事件处理)

    基本应用 修饰符 为什么要在HTML中使用事件监听 Demo 一.基本应用 1.通过v-on指令绑定事件,例如: <button v-on:click="">提交< ...

  3. vue中ref-父主动取值值;

    多用月input标签 定义的时候 直接写ref=“id” <el-input placeholder="请输入内容" style="width: 150px&quo ...

  4. nginx php-fpm环境搭建权限问题

    如果nginx的work process和php-fpm的运行权限相同,在logrotate的影响下,会导致被上传webshell后 被修改accesslog 故安全配置: nginx.conf: u ...

  5. vue 编辑

    点击文字修改 <div class="baseInfo"> <p class="title">基本信息</p> <p ...

  6. java中的运算符与表达式

    运算符与表达式 运算符分类: 0.赋值运算符 = 1.算数运算符 + - * / % 2.比较运算符 < > == <= >= != 3.逻辑运算符 & | ! &am ...

  7. go语言入门(5)工程管理

    在工程中不会简单到只有一个源代码文件,且源文件之间会有相互的依赖关系,早期Go语言使用makefile作为工程管理的临时方案,后来的Go命令行工具的革命性之处在于彻底消除了工程文件的概念,完全用目录结 ...

  8. Delphi DLL文件的静态调用

  9. 第七章 路由 70 路由-vue-router的基本使用

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8&quo ...

  10. nginx之root和alias区别

    alias实现虚拟目录 alias与root的用法区别 最基本的区别:alias指定的目录是准确的,root是指定目录的上级目录,并且该上级目录要含有location指定名称的同名目录.另外,根据前文 ...