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

2024-05-04 03:32
文章标签 基础 视频 opengl 美颜 fbo

本文主要是介绍OpenGL 之 FBO--视频美颜的基础,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

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--视频美颜的基础的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/958255

相关文章

用js控制视频播放进度基本示例代码

《用js控制视频播放进度基本示例代码》写前端的时候,很多的时候是需要支持要网页视频播放的功能,下面这篇文章主要给大家介绍了关于用js控制视频播放进度的相关资料,文中通过代码介绍的非常详细,需要的朋友可... 目录前言html部分:JavaScript部分:注意:总结前言在javascript中控制视频播放

Python基于wxPython和FFmpeg开发一个视频标签工具

《Python基于wxPython和FFmpeg开发一个视频标签工具》在当今数字媒体时代,视频内容的管理和标记变得越来越重要,无论是研究人员需要对实验视频进行时间点标记,还是个人用户希望对家庭视频进行... 目录引言1. 应用概述2. 技术栈分析2.1 核心库和模块2.2 wxpython作为GUI选择的优

C#基础之委托详解(Delegate)

《C#基础之委托详解(Delegate)》:本文主要介绍C#基础之委托(Delegate),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1. 委托定义2. 委托实例化3. 多播委托(Multicast Delegates)4. 委托的用途事件处理回调函数LINQ

0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型的操作流程

《0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeekR1模型的操作流程》DeepSeekR1模型凭借其强大的自然语言处理能力,在未来具有广阔的应用前景,有望在多个领域发... 目录0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型,3步搞定一个应

Java如何获取视频文件的视频时长

《Java如何获取视频文件的视频时长》文章介绍了如何使用Java获取视频文件的视频时长,包括导入maven依赖和代码案例,同时,也讨论了在运行过程中遇到的SLF4J加载问题,并给出了解决方案... 目录Java获取视频文件的视频时长1、导入maven依赖2、代码案例3、SLF4J: Failed to lo

Python实现多路视频多窗口播放功能

《Python实现多路视频多窗口播放功能》这篇文章主要为大家详细介绍了Python实现多路视频多窗口播放功能的相关知识,文中的示例代码讲解详细,有需要的小伙伴可以跟随小编一起学习一下... 目录一、python实现多路视频播放功能二、代码实现三、打包代码实现总结一、python实现多路视频播放功能服务端开

Python实现视频转换为音频的方法详解

《Python实现视频转换为音频的方法详解》这篇文章主要为大家详细Python如何将视频转换为音频并将音频文件保存到特定文件夹下,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1. python需求的任务2. Python代码的实现3. 代码修改的位置4. 运行结果5. 注意事项

Python视频处理库VidGear使用小结

《Python视频处理库VidGear使用小结》VidGear是一个高性能的Python视频处理库,本文主要介绍了Python视频处理库VidGear使用小结,文中通过示例代码介绍的非常详细,对大家的... 目录一、VidGear的安装二、VidGear的主要功能三、VidGear的使用示例四、VidGea

MySQL中my.ini文件的基础配置和优化配置方式

《MySQL中my.ini文件的基础配置和优化配置方式》文章讨论了数据库异步同步的优化思路,包括三个主要方面:幂等性、时序和延迟,作者还分享了MySQL配置文件的优化经验,并鼓励读者提供支持... 目录mysql my.ini文件的配置和优化配置优化思路MySQL配置文件优化总结MySQL my.ini文件

流媒体平台/视频监控/安防视频汇聚EasyCVR播放暂停后视频画面黑屏是什么原因?

视频智能分析/视频监控/安防监控综合管理系统EasyCVR视频汇聚融合平台,是TSINGSEE青犀视频垂直深耕音视频流媒体技术、AI智能技术领域的杰出成果。该平台以其强大的视频处理、汇聚与融合能力,在构建全栈视频监控系统中展现出了独特的优势。视频监控管理系统EasyCVR平台内置了强大的视频解码、转码、压缩等技术,能够处理多种视频流格式,并以多种格式(RTMP、RTSP、HTTP-FLV、WebS