[转]OpenGL核心技术之帧缓冲

本文详细介绍了OpenGL中的帧缓冲技术,包括其原理、应用和创建过程。通过帧缓冲,开发者可以实现游戏后处理渲染效果,如Bloom、Blur等。文章通过实例展示了如何创建帧缓冲对象,附加纹理和渲染缓冲,并使用帧缓冲进行离屏渲染,以及如何通过帧缓冲实现颜色反相和模糊等后处理特效。此外,还提供了帧缓冲相关的顶点和片段着色器代码,以及C++处理帧缓冲的核心代码,帮助读者理解并掌握帧缓冲在3D游戏开发中的应用。
摘要由CSDN通过智能技术生成

笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,国家专利发明人;已出版书籍:《手把手教你架构3D游戏引擎》电子工业出版社和《Unity3D实战核心技术详解》电子工业出版社等。

CSDN视频网址:http://edu.csdn.net/lecturer/144

本篇博文主要是给读者解密关于游戏后处理渲染效果的原理,后处理渲染效果在Unity,UE4虚幻引擎等商业引擎 使用的非常多,

比如Bloom,Blur,SSAO,PSSM,HDR等等都属于后处理渲染效果,它们的实现其实就是应用帧缓冲技术实现的,本篇博文主要是

围绕帧缓冲给读者介绍其实现原理以及应用案例。

在前面给读者介绍了几种不同的屏幕缓冲:用于写入颜色值的颜色缓冲,用于写入深度信息的深度缓冲,以及允许我们基于一些条件丢弃指定片段的模板缓冲。本篇博客主要是给读者介绍帧缓冲,什么是帧缓冲?其实就是把前面介绍的这几种缓冲结合起来叫做帧缓冲(Framebuffer),它被储存于内存中。OpenGL给了我们自己定义帧缓冲的自由,我们可以选择性的定义自己的颜色缓冲、深度和模板缓冲。本篇博客主要是给读者介绍帧缓冲,我们前面介绍的渲染操作都是在默认的帧缓冲之上进行的,当你创建了你的窗口的时候默认帧缓冲就被创建和配置好了,通过创建我们自己的帧缓冲我们能够获得一种额外的渲染方式。通过帧缓冲可以将你的场景渲染到一个不同的帧缓冲中,可以使我们能够在场景中创建镜子这样的效果,或者做出一些炫酷的特效。首先我们会讨论它们是如何工作的,然后我们将利用帧缓冲来实现一些炫酷的效果。

我们在引擎渲染中经常会使用一些后处理效果,这些后处理效果就是在帧缓冲中进行的。下面我们就告诉读者帧缓冲是如何工作的?

我们可以使用一个叫做glGenFramebuffers的函数来创建一个帧缓冲对象(简称FBO):

 

GLuint fbo;
glGenFramebuffers(1, &fbo);

 

首先我们要创建一个帧缓冲对象,把它绑定到当前帧缓冲,做一些操作,然后解绑帧缓冲。我们使用glBindFramebuffer来绑定帧缓冲:

glBindFramebuffer(GL_FRAMEBUFFER, fbo);

 

绑定到GL_FRAMEBUFFER目标后,接下来所有的读、写帧缓冲的操作都会影响到当前绑定的帧缓冲。也可以把帧缓冲分开绑定到读或写目标上,分别使用GL_READ_FRAMEBUFFER或GL_DRAW_FRAMEBUFFER来做这件事。如果绑定到了GL_READ_FRAMEBUFFER,就能执行所有读取操作,

像glReadPixels这样的函数使用了,绑定到GL_DRAW_FRAMEBUFFER上,就允许进行渲染、清空和其他的写入操作。在此给读者总结一下构建一个完整的帧

缓冲满足的条件:

建构一个完整的帧缓冲必须满足以下条件:

  • 我们必须往里面加入至少一个附件(颜色、深度、模板缓冲)。
  • 其中至少有一个是颜色附件。
  • 所有的附件都应该是已经完全做好的(已经存储在内存之中)。
  • 每个缓冲都应该有同样数目的样本。

上面的条件提到了样本,如果你不知道什么是样本也不用担心,我们会在后面的博文中讲到。

我们需要为帧缓冲创建一些附件(Attachment),还需要把这些附件附加到帧缓冲上。当我们做完所有上面提到的条件的时候我们就可以用 glCheckFramebufferStatus 带上 GL_FRAMEBUFFER 这个参数来检查是否真的成功做到了。然后检查当前绑定的帧缓冲,返回了这些规范中的哪个值。如果返回的是 GL_FRAMEBUFFER_COMPLETE就对了:

 

if(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE)
  // Execute victory dance

后续所有渲染操作将渲染到当前绑定的帧缓冲的附加缓冲中,由于我们的帧缓冲不是默认的帧缓冲,渲染命令对窗口的视频输出不会产生任何影响。出于这个原因,它被称为离屏渲染(off-screen rendering),就是渲染到一个另外的缓冲中。为了让所有的渲染操作对主窗口产生影响我们必须通过绑定为0来使默认帧缓冲被激活:

 

 

glBindFramebuffer(GL_FRAMEBUFFER, 0);

当我们做完所有帧缓冲操作,不要忘记删除帧缓冲对象:

 

 

 

 

glDeleteFramebuffers(1, &fbo);


现在在执行完成检测前,我们需要把一个或更多的附件附加到帧缓冲上。一个附件就是一个内存地址,这个内存地址里面包含一个为帧缓冲准备的缓冲,它可以是个图像。当创建一个附件的时候我们有两种方式可以采用:纹理或渲染缓冲(renderbuffer)对象。

 

接下来介绍纹理当把一个纹理附加到帧缓冲上的时候,所有渲染命令会写入到纹理上,就像它是一个普通的颜色/深度或者模板缓冲一样。使用纹理的好处是,所有渲染操作的结果都会被储存为一个纹理图像,这样我们就可以简单的在着色器中使用了。

创建一个帧缓冲的纹理和创建普通纹理差不多:

GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 800, 600, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

这里主要的区别是我们把纹理的维度设置为屏幕大小(尽管不是必须的),我们还传递NULL作为纹理的data参数。对于这个纹理,

 

我们只分配内存,而不去填充它。纹理填充会在渲染到帧缓冲的时候去做。

 

如果你打算把整个屏幕渲染到一个或大或小的纹理上,你需要用新的纹理的尺寸作为参数再次调用glViewport(要在渲染到你的帧缓冲之前

做好),否则只有一小部分纹理或屏幕能够绘制到纹理上。现在我们已经创建了一个纹理,最后一件要做的事情是把它附加到帧缓冲上:

glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D, texture, 0);

glFramebufferTexture2D函数需要传入下列参数:

  • target:我们所创建的帧缓冲类型的目标(绘制、读取或两者都有)。
  • attachment:我们所附加的附件的类型。现在我们附加的是一个颜色附件。需要注意,最后的那个0是暗示我们可以附加1个以上颜色的附件。
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

海洋_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值