NDK OpenGL ES 3.0 开发(五):FBO 离屏渲染
该原创文章首发于微信公众号:字节流动
什么是 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);
示例:
- 创建并初始化 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;}
- 编译链接 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;}}
- 离屏渲染和普通渲染
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 离屏渲染相关推荐
- NDK OpenGL ES 3.0 开发(一):绘制一个三角形
该原创文章首发于微信公众号:字节流动 什么是 OpenGLES OpenGLES 全称 OpenGL for Embedded Systems ,是三维图形应用程序接口 OpenGL 的子集,本质上是 ...
- NDK OpenGL ES 3.0 开发(二十):3D 模型
该原创文章首发于微信公众号:字节流动 OpenGLES 3D 模型 OpenGLES 3D 模型本质上是由一系列三角形在 3D 空间(OpenGL 坐标系)中构建而成,另外还包含了用于描述三角形表面的 ...
- NDK OpenGL ES 3.0 开发(二十二):PBO
该原创文章首发于微信公众号:字节流动 PBO 是什么 OpenGL PBO(Pixel Buffer Object),被称为像素缓冲区对象,主要被用于异步像素传输操作.PBO 仅用于执行像素传输,不连 ...
- NDK OpenGL ES 3.0 开发(八):坐标系统
该原创文章首发于微信公众号:字节流动 OpenGL 坐标系统 我们知道 OpenGL 坐标系中每个顶点的 x,y,z 坐标都应该在 -1.0 到 1.0 之间,超出这个坐标范围的顶点都将不可见. 将一 ...
- NDK OpenGL ES 3.0 开发(七):Transform Feedback
该原创文章首发于微信公众号:字节流动 什么是 Transform Feedback Transform Feedback(变换反馈)是在 OpenGLES3.0 渲染管线中,顶点处理阶段结束之后,图元 ...
- NDK OpenGL ES 3.0 开发(四):VBO、EBO 和 VAO
该原创文章首发于微信公众号:字节流动 VBO 和 EBO VBO(Vertex Buffer Object)是指顶点缓冲区对象,而 EBO(Element Buffer Object)是指图元索引缓冲 ...
- NDK OpenGL ES 3.0 开发(十七):相机基础滤镜
该原创文章首发于微信公众号:字节流动 相机基础滤镜 上文中我们通过 ImageReader 获取到 Camera2 预览的 YUV 数据,然后利用 OpenGLES 渲染实现相机预览,这一节将利用 G ...
- win7下搭建opengl es 2.0开发环境
原文 http://codingnow.cn/opengles/1501.html 主题 OpenGL ES Windows 7 1. 下载AMD的OpenGL ES2.0的模拟器 ,下载地址: ...
- OpenGL ES 3.0(五)纹理
我们可以为每个顶点添加颜色来增加图形的细节,从而创建出有趣的图像.但是,如果想让图形看起来更真实,我们就必须有足够多的顶点,从而指定足够多的颜色.这将会产生很多额外开销,因为每个模型都会需求更多的顶点 ...
- OpenGL ES 3.0 开发(三):YUV 渲染
该原创文章首发于微信公众号:字节流动 YUV 渲染原理 前面文章一文掌握 YUV 图像的基本处理介绍了 YUV 常用的基本格式,本文以实现 NV21/NV12 的渲染为例. 前文提到,YUV 图不能直 ...
最新文章
- Oracle高级查询之OVER (PARTITION BY ..)
- [MySQL实践] 实践记录
- 如何在Kubernetes中暴露服务访问
- TCP三次握手、糊涂窗口、粘包问题
- monkey的具体使用及详细说明
- 图神经网络(一)图信号处理与图卷积神经网络(5)图卷积神经网络
- 浅谈面向对象思想下的 C 语言
- mysql操作常用技巧
- 有了这个运维方案,让IT信息化人员头疼的系统宕机再也没出现
- window下遍历并修改文件
- c语言100块钱买100只鸡算法,JS计算输出100元钱买100只鸡问题的解决方法
- vb net excel 剪贴板 粘贴_利用剪贴板强化 Excel 计算
- scala学习手记2 - scala中的循环
- 运动目标跟踪(十二)--KCF跟踪及CSK,CN对比
- python按字节读文件-使用Python进行二进制文件读写(转)
- 深圳大学C C++ 数据结构题目答案清单完整题库(含期中模拟 期末模拟 期末真题 考前练习)
- ykhmi是什么触摸屏软件_一体机使用中常见问题-中达优控|YKHMI|触摸屏|一体机|云组态-深圳市中达优控科技有限公司 —— YKHMI|触摸屏|一体机|云组态...
- Abaqus帮助文档翻译——Abaqus/CAE主窗口介绍
- 串行通信接口详细描述
- 新倩女幽魂服务器维护,《倩女幽魂Online》更新公告(版本1.0.23)
热门文章
- 给不给你升职,领导关心的是什么?
- java仿制百度云盘_基于jsp的仿百度网盘-JavaEE实现仿百度网盘 - java项目源码
- 八戒,别以为你站在路灯下就是夜明猪了!
- 推荐系统遇上深度学习(十二)--推荐系统中的EE问题及基本Bandit算法
- popupwindow拦截点击物理返回键
- 架构师之殇:一个反对“平台团队”的案例
- VRP基础介绍:VRP基础
- 如何正确地在Spring Data JPA和Jackson中用上Java 8的时间相关API(即JSR 310也即java.time包下的众神器)...
- 使用PlotNeuralNet绘制自己的网络结构图
- 阿里云上部署kafka--遇到的坑