https://www.jianshu.com/p/c16c7e7cdf91

OpenGL 之 FBO--视频美颜的基础

0.582017.10.04 23:45:59字数 2938阅读 4548

前言

在OpenGL中,有一个非常重要的知识点就是 FBO(Frame Buffer Object)。它为做视频美颜提供了技术手段。

在网上可以找到很多介绍 FBO 的文章,但很少有将 FBO、Texture及Render Buffer 之间关系讲清楚的。

本篇文章在介绍 FBO 的同时,清楚的描述了三者之间的关系。希望本文对大家有所帮助。

FBO

OpenGL 默认把 framebuffer 当作渲染的目的地。它由窗口系统创建并管理。framebuffer Object 是个二维数组的集合,它包括 color buffers, depth buffer, stencil buffer。

OpenGL扩展,GL_ARB_framebuffer_object 提供了创建额外非可显示的 framebuffer object(FBO)的接口。FBO 称作应用程序可创建的 framebuffer 以区别默认的窗口系统提供的framebuffer。通过使用FBO,OpenGL 应用可以重定向渲染输出,让它输出到FBO而不是传统的窗口系统提供的 framebuffer.

与窗口系统提供的帧缓冲区类似,FBO包含一系列渲染目的地的集合;包括颜色,深度和模板缓冲区。 FBO中的这些逻辑缓冲区称为可附着的 frame buffer。

有两种类型的可附着的 framebuffer;纹理(Texture)和renderbuffer。如果纹理被附加到FBO,OpenGL将执行“渲染到纹理”。如果renderbuffer被附加到FBO,则OpenGL会执行“离屏渲染”。

顺便说一下,renderbuffer是在GL_ARB_framebuffer_object扩展中定义的一种新类型的存储对象。它在渲染过程中用作单个2D图像的渲染目的地。

下图显示了FBO,纹理和renderbuffer之间的连接。多个纹理对象或renderbuffer对象可以通过附着点附加到FBO上。

FBO中,

  • 有多个颜色附加点(GL_COLOR_ATTACHMENT0,...,GL_COLOR_ATTACHMENTn),
  • 一个深度附加点(GL_DEPTH_ATTACHMENT)
  • 一个模板附加点(GL_STENCIL_ATTACHMENT)。

颜色附着点的数量取决于实现,但每个FBO必须至少具有一个颜色附加点。您可以使用GL_MAX_COLOR_ATTACHMENTS查询最大数量的颜色附加点,这些数据由显卡支持。

FBO具有多个颜色附加点的原因是允许在同一时间将颜色缓冲区渲染到多个目的地。这个“多个渲染目标”(MRT)可以由GL_ARB_draw_buffers扩展完成。请注意,FBO本身不存放数据,它只有多个附着点。这有点像数据结构中的指针,它只存放指针,而不存放数据。

FBO提供了一种高效的切换机制;从FBO中分离先前的帧缓冲区,并将一个新的可附着的帧缓冲图像附加到FBO中。切换可附着的帧缓冲图像比在FBO之间切换要快得多。 FBO提供glFramebufferTexture2D()来切换2D纹理对象,并将glFramebufferRenderbuffer()切换到renderbuffer对象。

创建FBO

glGenFramebuffers()

void glGenFramebuffers(GLsizei n, GLuint* ids)
void glDeleteFramebuffers(GLsizei n, const GLuint* ids)

glGenFramebuffers() 需要2个参数:

  • 第一个是要创建的帧缓冲区的数量;
  • 第二个参数是指向GLuint变量或数组以存储单个ID或多个ID的指针。
  • 它返回未使用的framebuffer对象的ID。 ID 0表示默认的帧缓冲区,它是由窗口系统提供的帧缓冲区。
  • 而,当FBO不再使用时,可以通过调用glDeleteFramebuffers()来删除。

glBindFramebuffer()

创建FBO之后,必须先绑定FBO。

void glBindFramebuffer(GLenum target, GLuint id)
  • 第一个参数target为GL_FRAMEBUFFER;
  • 第二个参数为framebuffer对象的ID。 FBO绑定后,所有的OpenGL操作都会影响到当前绑定的FBO。
  • 对象ID,0保留给默认的窗口系统提供的帧缓冲区。因此,为了取消绑定当前帧缓冲区(FBO),请在glBindFramebuffer() 中使用ID 0。

Renderbuffer

另外,renderBuffer对象是新引入的用于离屏渲染。它允许将场景直接渲染到renderbuffer对象,而不是渲染到纹理对象。

Renderbuffer只是一个包含可渲染内部格式的单个映像的数据存储对象。它用于存储没有相应纹理格式的OpenGL逻辑缓冲区,如模板或深度缓冲区。

glGenRenderbuffers()

void glGenRenderbuffers(GLsizei n, GLuint* ids)
void glDeleteRenderbuffers(GLsizei n, const Gluint* ids)

一旦创建了一个renderbuffer,它返回非零正整数。 ID 0为OpenGL保留。

glBindRenderbuffer()

void glBindRenderbuffer(GLenum target, GLuint id)

与其他OpenGL对象相同,您必须在引用之前绑定当前的renderbuffer对象。 renderbuffer对象的目标参数应为GL_RENDERBUFFER。

glRenderbufferStorage()

void glRenderbufferStorage(GLenum  target,GLenum  internalFormat,GLsizei width,GLsizei height)

当创建一个renderbuffer对象时,它没有任何数据存储,所以我们必须为它分配一个内存空间。这可以通过使用glRenderbufferStorage()来完成。

  • 第一个参数必须是GL_RENDERBUFFER;
  • 第二个参数是可渲染颜色(GL_RGB,GL_RGBA等),可渲染深度(GL_DEPTH_COMPONENT)或可渲染模板(GL_STENCIL_INDEX);
  • width和height是以像素为单位的renderbuffer图像的尺寸。

宽度和高度应小于GL_MAX_RENDERBUFFER_SIZE,否则会生成GL_INVALID_VALUE错误。

glGetRenderbufferParameteriv()

void glGetRenderbufferParameteriv(GLenum target,GLenum param,GLint* value)

您还可以获取当前绑定的renderbuffer对象的各种参数。

  • 目标应该是GL_RENDERBUFFER;
  • 第二个参数是参数的名称;
  • 最后一个是指向整数变量的指针,用于存储返回的值。

renderbuffer参数的可用名称为:

GL_RENDERBUFFER_WIDTH
GL_RENDERBUFFER_HEIGHT
GL_RENDERBUFFER_INTERNAL_FORMAT
GL_RENDERBUFFER_RED_SIZE
GL_RENDERBUFFER_GREEN_SIZE
GL_RENDERBUFFER_BLUE_SIZE
GL_RENDERBUFFER_ALPHA_SIZE
GL_RENDERBUFFER_DEPTH_SIZE
GL_RENDERBUFFER_STENCIL_SIZE

将图像附加到FBO

FBO本身不存放作何数据。相反,我们必须在FBO上附加可附加的framebuffer图像(纹理或renderbuffer对象)。该机制允许FBO快速切换(拆除和附加)FBO中的可附着的帧缓冲图像。切换可附加的帧缓冲区比在FBO之间切换要快得多。并且,它可以节省不必要的数据副本和内存消耗。例如,纹理可以附加到多个FBO,并且其图像可以由多个FBO共享。

将2D纹理图像附加到FBO

glFramebufferTexture2D(GLenum target,GLenum attachmentPoint,GLenum textureTarget,GLuint textureId,GLint  level)

glFramebufferTexture2D()是将2D纹理图像附加到FBO。

  • 第一个参数必须是GL_FRAMEBUFFER;
  • 第二个参数是连接纹理图像的连接点。 FBO具有多个颜色附加点(GL_COLOR_ATTACHMENT0,...,GL_COLOR_ATTACHMENTn),GL_DEPTH_ATTACHMENT和GL_STENCIL_ATTACHMENT;
  • 第三个参数“textureTarget”在大多数情况下是GL_TEXTURE_2D;
  • 第四个参数是纹理对象的标识符;
  • 最后一个参数是要附加的纹理的mipmap级别。

如果textureId参数设置为0,则纹理图像将从FBO中分离。如果纹理对象被删除时仍然附着在FBO上,则纹理图像将自动从当前绑定的FBO中分离。但是,如果它附加到多个FBO并被删除,那么它将仅从绑定的FBO分离,但不会与任何其他无约束的FBO分离。

附加到Renderbuffer图像到FBO

void glFramebufferRenderbuffer(GLenum target,GLenum attachmentPoint,GLenum renderbufferTarget,GLuint renderbufferId)

可以通过调用glFramebufferRenderbuffer()来附加renderbuffer。

  • 第一和第二个参数与glFramebufferTexture2D()相同;
  • 第三个参数必须是GL_RENDERBUFFER;
  • 最后一个参数是renderbuffer对象的ID。

如果renderbufferId参数设置为0,则renderbuffer图像将从FBO中的附加点分离。如果renderbuffer对象被删除时,仍然附着在FBO上,那么它将自动从绑定的FBO中分离出来。但是,它不会与任何其他无约束的FBO分离。

检查FBO状态

一旦可连接的图像(纹理和renderbuffer)附加到FBO,并且在执行FBO操作之前,必须使用glCheckFramebufferStatus()验证FBO状态是否完整。如果FBO未完成,则任何绘图和读取命令(glBegin(),glCopyTexImage2D()等)将失败。

GLenum glCheckFramebufferStatus(GLenum target)

glCheckFramebufferStatus()验证当前绑定的FBO上的所有附加图像和帧缓冲区参数。而且,这个函数不能在glBegin()/ glEnd()对中调用。目标参数应为GL_FRAMEBUFFER。在检查FBO后返回非零值。如果满足所有要求和规则,则返回GL_FRAMEBUFFER_COMPLETE。否则,它返回一个相关的错误值,它告诉什么规则被违反。

FBO完整性规则为:

  • framebuffer可附加图像的宽度和高度必须不为零。
  • 如果图像附加到颜色附着点,则图像必须具有可呈现颜色的内部格式。 (GL_RGBA,GL_DEPTH_COMPONENT,GL_LUMINANCE等)
  • 如果图像附加到GL_DEPTH_ATTACHMENT,则图像必须具有深度可渲染内部格式。 (GL_DEPTH_COMPONENT,GL_DEPTH_COMPONENT24等)
  • 如果图像附加到GL_STENCIL_ATTACHMENT,则图像必须具有模板可渲染内部格式。 (GL_STENCIL_INDEX,GL_STENCIL_INDEX8等)
  • FBO必须至少安装一张图片。
  • 附加FBO的所有图像必须具有相同的宽度和高度。
  • 附加颜色附件点的所有图像必须具有相同的内部格式。

请注意,即使满足上述所有条件,OpenGL驱动程序也可能不支持某些内部格式和参数的组合。如果OpenGL驱动程序不支持特定的实现,那么glCheckFramebufferStatus()返回GL_FRAMEBUFFER_UNSUPPORTED。

例子

有时,需要动态生成动态纹理。最常见的示例是生成镜像/反射效果,动态多维数据集/环境映射和阴影贴图。动态纹理可以通过将场景渲染到纹理来实现。渲染到纹理的一种传统方法是像一般的draw缓冲区,然后使用glCopyTexSubImage2D()将framebuffer图像复制到纹理。

使用FBO,我们可以将场景直接渲染到纹理,所以我们不必使用窗口系统提供的帧缓存。更进一步,我们可以消除额外的数据拷贝(从帧缓存到纹理)。

使用FBO还有另一个优势。在传统情况下,如果纹理分辨率大于渲染窗口的大小,则窗口区域中的区域将被剪切。然而,FBO并不受这类问题的影响。您可以创建一个大于显示窗口的framebuffer-renderable图像。

以下代码是在渲染循环开始之前设置FBO和可附加的可帧缓冲图像。请注意,不仅纹理图像附加到FBO,而且renderBuffer图像也附加到FBO的深度附着点上。我们实际上并没有使用这种深度缓冲区,但是FBO本身需要深度测试。如果我们不将此可渲染深度图像附加到FBO,则由于缺少深度测试,渲染输出将被损坏。如果在FBO渲染期间也需要模板测试,则附加的渲染缓存图像应附加到GL_STENCIL_ATTACHMENT。

...
// create a texture object
GLuint textureId;
glGenTextures(1, &textureId);
glBindTexture(GL_TEXTURE_2D, textureId);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); // automatic mipmap
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, TEXTURE_WIDTH, TEXTURE_HEIGHT, 0,GL_RGBA, GL_UNSIGNED_BYTE, 0);
glBindTexture(GL_TEXTURE_2D, 0);// create a renderbuffer object to store depth info
GLuint rboId;
glGenRenderbuffers(1, &rboId);
glBindRenderbuffer(GL_RENDERBUFFER, rboId);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT,TEXTURE_WIDTH, TEXTURE_HEIGHT);
glBindRenderbuffer(GL_RENDERBUFFER, 0);// create a framebuffer object
GLuint fboId;
glGenFramebuffers(1, &fboId);
glBindFramebuffer(GL_FRAMEBUFFER, fboId);// attach the texture to FBO color attachment point
glFramebufferTexture2D(GL_FRAMEBUFFER,        // 1. fbo target: GL_FRAMEBUFFER GL_COLOR_ATTACHMENT0,  // 2. attachment pointGL_TEXTURE_2D,         // 3. tex target: GL_TEXTURE_2DtextureId,             // 4. tex ID0);                    // 5. mipmap level: 0(base)// attach the renderbuffer to depth attachment point
glFramebufferRenderbuffer(GL_FRAMEBUFFER,      // 1. fbo target: GL_FRAMEBUFFERGL_DEPTH_ATTACHMENT, // 2. attachment pointGL_RENDERBUFFER,     // 3. rbo target: GL_RENDERBUFFERrboId);              // 4. rbo ID// check FBO status
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if(status != GL_FRAMEBUFFER_COMPLETE)fboUsed = false;// switch back to window-system-provided framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, 0);
...

渲染到纹理的渲染过程与普通绘图几乎相同。我们只需要将渲染目的地从窗口系统提供的framebuffer切换到FBO即可。

...
// set rendering destination to FBO
glBindFramebuffer(GL_FRAMEBUFFER, fboId);// clear buffers
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);// draw a scene to a texture directly
draw();// unbind FBO
glBindFramebuffer(GL_FRAMEBUFFER, 0);// trigger mipmaps generation explicitly
// NOTE: If GL_GENERATE_MIPMAP is set to GL_TRUE, then glCopyTexSubImage2D()
// triggers mipmap generation automatically. However, the texture attached
// onto a FBO should generate mipmaps manually via glGenerateMipmap().
glBindTexture(GL_TEXTURE_2D, textureId);
glGenerateMipmap(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);
...

请注意,glGenerateMipmap()也作为FBO扩展的一部分包含,以便在修改基本级别的纹理图像之后显式生成mipmap。如果GL_GENERATE_MIPMAP设置为GL_TRUE,则glTex {Sub} Image2D()和glCopyTex {Sub} Image2D()触发自动mipmap生成(在OpenGL 1.4或更高版本中)。但是,由于FBO不调用glCopyTex {Sub} Image2D()来修改纹理,因此FBO操作不会自动生成基本层次的纹理。因此,必须显式地调用glGenerateMipmap()来生成mipmap。

另外,如果您需要对纹理进行后处理,则可以与像素缓冲区对象(PBO)组合,以有效地修改纹理。

小结

本文主要介绍了OpenGL中FBO是什么,同时讲清了 FBO与 纹理及Render Buffer之间的关系。最后通过实例说明了 FBO的使用。

OpenGL 之 FBO--视频美颜的基础相关推荐

  1. Android音视频开发---0基础入门准备篇

    文章目录 0,题记 1,学习路线 2,学习目标 任务列表 基础知识 FFmpeg 3,开始音视频开发必备的技能 1,C语言. 2,Linux. 3,基本的Linux编译执行命令,以及vim操作. 4, ...

  2. photoshop cc2017全套视频课程 从基础到实战案例PS海报-王诚-专题视频课程

    photoshop cc2017全套视频课程 从基础到实战案例PS海报-161人已学习 课程介绍         从基础掌握全套最重要的PS技能,基础技能讲课+实例教学. 彻底掌握PHOTOSHOP基 ...

  3. 视频教程-photoshop cc2017全套视频课程 从基础到实战案例PS海报-Photoshop

    photoshop cc2017全套视频课程 从基础到实战案例PS海报 中国电商服务联盟品牌讲师.中国国际互联网节品牌顾问. 12年视觉设计经验,5年视觉讲师经验.电商品牌视觉策划讲师 .曾任知名电商 ...

  4. 直播平台的视频美颜sdk是什么?

    直播平台的视频美颜sdk是什么,可以做什么?简而言之,直播美颜sdk是将直播平台的视频美颜效果做成一个sdk,给用户提供美颜效果选择,同时提供不同的视频分辨率,可以让用户在观看直播时有更好的体验.那么 ...

  5. 视频美颜SDK是怎么做到全局美颜的?

    "爱美之心人皆有之"这句话哪怕放在现在依然不过时,即便实在Web3.0前夕的互联网中,"高颜值"依旧是大家的追求.当下,无论是在短视频还是直播平台,高颜值的帅哥 ...

  6. 视频美颜SDK动态处理技术与静态处理技术

    如今,美颜算法迎来了进一步的迭代,大家对美颜的要求也越来越高,曾经只要求对静态图片进行美化,而今大多都是些实时视频直播的场景,可想而知这跨度有多大.本篇文章小编将为大家讲解一下视频美颜SDK动态处理技 ...

  7. 视频美颜sdk中人脸识别的主要实现流程

    小编在之前文章中多次提到过视频美颜sdk的关键技术之一"人脸识别",此技术经过漫长的发展周期,直到今天已经迭代了非常多的实现方式,今天小编就为大家讲解一下视频美颜sdk人脸识别的几 ...

  8. python自动化教程_Python自动化开发实战视频课程-全新基础篇_Python教程

    教程名称:Python自动化开发实战视频课程-全新基础篇 课程目录: 0001.51CTO学院-01Pythons10 day1 开课前言- _* N, R+ w/ T 0001.51CTO学院-01 ...

  9. Java 入门课程视频实战-0基础 上线了,猜拳游戏,ATM实战,欢迎围观

    Java 入门课程视频实战-0基础 已经上传完了.欢迎小伙伴们过来围观 直接进入: http://edu.csdn.net/course/detail/196 课程文件夹例如以下: 1 初识Java  ...

最新文章

  1. 几个常用的数据库连接字符串
  2. Caffe训练ImageNet
  3. s插件——SlimScroll滚动美化插件
  4. python移动文件中某个内容_如果python中的某些文件类型,则移动文件并创建目录...
  5. matlab计算hessian矩阵
  6. python以下导入包的格式错误的是_ICMP python上的错误数据包
  7. 机器学习(Andrew Ng)作业代码(Exercise 1~2)
  8. linux dstat io,linux dstat工具
  9. C语言旅途之用for循环与break求最大素数(质数)
  10. Docker端口映射只有Ipv6端口导致Ipv4访问不通
  11. CorelDRAW2022订阅版安装序列号
  12. [Go]基于Go语言的Web路由转发,多个网站共享一个端口(新版本,支持WebSocket)
  13. 12306 Android 8,铁行火车票12306
  14. wordpress 安装插件
  15. Python学习笔记 | 编码和文件读写
  16. Oracle 大表数据删除/清理方法小结
  17. RADIUS服务器介绍
  18. SQL(HIVE -HUE)剔除的三种方式
  19. S3C2440 开发板实战(10):signal async 异步通信
  20. 白盒测试之测试覆盖率浅析

热门文章

  1. mpf7_CBOE VIX Volatility derivatives_SP 500 Index_near_next option_Why log return_Kurtosis_Skew_OLS
  2. QT - 20230707
  3. 首届AMZ123亚马逊卖家大会,邀约赛盈分销一同助力卖家通观全局,共赢未来
  4. 基于 NGINX 的 WAF 配置方法之ModSecurity
  5. 通关率不到0.1%的小游戏《羊了个羊》为什么这么火?
  6. 中国蓄能器活塞市场趋势报告、技术动态创新及市场预测
  7. 为什么说Google没了节操
  8. NOIP2015提高组初赛难点整理
  9. 找不到对象怎么办,十年程序员带你了解java对象的创建
  10. cypher 查询语句