« Shadow Volume 阴影锥技术之探ⅣOpenGL怎样近似进行同时到FBO和屏幕的渲染 »

学一学,FBO

FBO这个名字应该记住,同时还得记住VBO,PBO——这些算得上OpenGL的高级技术了,但是可以说,用处很广。从拓展到即将的核心,证明了它们的价值。这里我主要讲讲FBO(因为最近只用到FBO嘛嘿),全名Frame Buffer Object,目前主要用于离屏渲染技术。——ZwqXin.com

不知道是否有人曾经对D3D的RenderTarget技术垂延三尺呢?我可没有哦(明明是因为不了解,汗~),不过OpenGL其实也有类似的技术,名为FBO(帧缓存对象)。还记得以前模仿例子做渲染到纹理,用glCopyTexImage2D,心里总是替opengl捏捏汗,空白纹理,对拷,每帧用一次,哇,FPS!(没那么夸张吧...)现在有了FBO知识,就不想再用glCopyTexImage2D来拷屏幕了,FBO有那么好吗?

本文来源于 ZwqXin (http://www.zwqxin.com/), 转载请注明
      原文地址:http://www.zwqxin.com/archives/opengl/learn-fbo.html

谈起缓存,你或许想起OpenGL五大缓存(who? ),但是这5种是象素格式中本来包含的。FBO里面的东西其实跟它们本质一样,都是一块特定的内存,你可以把一些屏幕信息给它保管(更厉害的在于,象素格式针对屏幕象素,所以位于屏幕视野外部分的信息你不会得到,但是FBO里面的东西不受这种限制)。这里提到“FBO里面的东西”,为什么不直接说FBO呢?不一样的。FBO本身不是一块内存,没有空间,真正存储东西,可实际读写的是依附于FBO的东西:纹理(texture)和渲染缓存(renderbuffer)。橙书上说纹理是一种复杂组织的数据格式,但是你只要了解它本质也是一块内存区域好了(确切一点:缓存)。至于renderbuffer,有depth-renderbuffer和stencil-renderbuffer等等。FBO就好象一个管理这些资源的管理设备那样(情况如同纹理对象管理纹理,所以都叫Object),这个设备有好多个用来标识上述资源的标签(GL_COLOR_ATTACHMENTi_EXT,GL_DEPTH_ATTACHMENT_EXT等等)。至于这个设备怎么工作呢?

GameDev上有篇精彩的文章,OpenGL FrameBuffer Object,分为两部分,讲述了FBO中基础的两个用法,分次渲染和一次性渲染(MRT)。建议想了解FBO的同学看看这篇文章,认真看完了,你就入门一半了;看完后把作者给的例子代码下载下来调试,观摩,完后你就入门4分之3了,在自己的框架上再按着上面的敲一次代码,自己实现一次,改改参数改改代码位置实验一下,然后你就入门99%了。(当然你可以搜搜中文相关的。)本Blog不打算像其他博客那样给你来次用法详解(大哥,你还是看上面的文章吧),我说说其他的。

FBO出现之前,我们是怎么离屏渲染的呢?1.前面提到的glCopyTexImage2D;2.glDrawBuffers(size, *p)。譬如你想实现这样一个功能:汽车倒后镜。或许你首先把相机放在倒后镜那位置,往车后方向一摄,把相片作为纹理贴到这个倒后镜上。当然了,车后情况随时变化,所以你得每帧更新。以前的话,渲染过程一开始,把视觉(相机)放在倒后镜位置,渲染一次(不显示出来),然后把结果从屏幕帧缓存区读出,通过glCopyTexImage2D拷贝到一张空纹理上,接下来恢复驾驶者视觉(相机回放),把那纹理贴倒后镜,正常渲染场景——然后马上又开始下一帧。不得不说的是拷贝过程(glReadPixels)很浪费时间。FBO,你做的是同样的步骤,不过用一个glDrawBuffer()命令把倒后镜所看到的场景直接映射到一张由FBO绑定的纹理上,速度就上来了。

屏幕信息呢?譬如GLSL的fragment shader处理每个象素,每个象素都有一个特别的信息,但又不仅仅颜色。熟悉GLSL的话知道,要使用gl_data[i]来把这样的信息输出。譬如gl_data[0]输出颜色,gl_data[1]输出特别的信息,但是输出到哪里呢?传统是用一个辅助缓存(Auxiliary Buffer,OPENGL五大缓存种类之一,有好几个,AUX0,AUX1...),渲染时用把p[]={GL_BACK_LEFT,GL_AUX0}数组放入glDrawBuffers(2,p)就能一边渲染原图象(按gl_data[0]的输出)到屏幕后缓冲,一边输出特定信息(按gl_data[1]的输出)到辅助缓存0号了。貌似这样很好,因为连FBO都不支持同时屏幕输出和输出到FBO,但是你要怎样使用辅助缓存0号(GL_AUX0)里的内容?.....还不是得glCopyTexImage2D到一张专门存储这种信息的纹理!FBO则不一样,首先,与前面一样,用绑定到FBO的一个纹理存储这种数据,接下来就可以直接用此纹理了;其次,即使不支持同时屏幕输出和输出到FBO,也还是有办法近似的,这个我找了整晚英文资料可以总结出一两个办法,详见我后面的文章。

整个过程是这样的:在预处理中,新建一个FBO对象,用Bind绑定到当前(这些BIND之类函数一般是表示“你接下来要处理这个对象啦”的意思),给FBO输入渲染缓存或纹理,检查FBO状态是否正确,再脱离绑定。渲染过程,在需要它时再一次绑定,指定把接下来的内容渲染到它里面的哪一个渲染缓存或纹理......脱离绑定,使用之。

连我都觉得表意一塌糊涂,所以还是看那文章吧。注意,这里检查状态(glCheckFramebufferStatusEXT)很有必要,不然你连FBO起不起作用都不知道。在渲染到FBO后开始真正的屏幕渲染时,记得先脱离绑定glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0),否则还是继续渲染到FBO的,你可以把这个0看作代表“屏幕”这个主缓冲的代号;因为你渲染到FBO期间改变的东西是会保留下来的(OPENGL是个状态机嘛),你得去除这些影响,包括使用glPushAttrib之类函数;FBO绑定后开始渲染时还要记得glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT|.....);因为上面所说的这些改变包括象素格式,我们需要隔绝屏幕渲染时候的像素信息影响FBO渲染,因此你不想受渲染到FBO前的象素信息影响的话,像每帧开始时那样用glClear吧。

写得够长了,接下来谈谈一些应用吧。
怎样近似进行同时到FBO和屏幕的渲染

本文来源于 ZwqXin (http://www.zwqxin.com/), 转载请注明
      原文地址:http://www.zwqxin.com/archives/opengl/learn-fbo.html

  • quote 1.ForestM
  • 你好!看了你的文章觉得非常受教,最近也在用FBO写程序,但是depth texture总是失败,而且用glReadPixels读深度值总是非常接近1,想请教下是什么原因。
    zwqxin 于 2012-12-7 21:57:50 回复
    数值应该是正常的,深度值的倒数才是线性的。
  • 2012-12-6 22:45:27 回复该留言
  • quote 2.ForestM
  • 这个阴影弄了整整三天= =
    后来我在网上查到了计算公式。。
    按照博主贴的代码写了一段,但是不明白shadow2DProj这个函数到底是怎么回事。。
    然后如果有物体消失的时候那一刹会屏闪,然后画在FBO的图形会显示出来。这是什么状况。。
    但是总的来说算是写完了。最后用了混合。因为不知道如果不用混合的话那些贴图怎么办。。。博主有什么办法吗?。。
  • 2012-12-8 18:22:45 回复该留言

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

IE下本页面显示有问题?

→点击地址栏右侧【兼容视图】←

日历

Search

网站分类

最新评论及回复

最近发表

Powered By Z-Blog 1.8 Walle Build 100427

Copyright 2008-2013 ZwqXin. All Rights Reserved. Theme edited from ipati.