TheOpenGLextension,GL_ARB_framebuffer_objectprovidesaninterfacetocreateadditionalnon-displayableframebufferobjects(FBO).Thisframebufferiscalledapplication-createdframebufferinordertodistinguishfromthedefaultwindow-system-providedframebuffer.Byusingframebufferobject(FBO),anOpenGLapplicationcanredirecttherenderingoutputtotheapplication-createdframebufferobject(FBO)otherthanthetraditionalwindow-system-providedframebuffer.And,itisfullycontrolledbyOpenGL.在OpenGL扩展中,GL_EXT_framebuffer_object提供了一种创建额外的不能显示的帧缓存对象的接口。为了和默认的“window系统生成”的帧缓存区别,这种帧缓冲成为应用程序帧缓存(application-createdframebuffer)。通过使用帧缓存对象(FBO),OpenGL可以将显示输出到引用程序帧缓存对象,而不是传统的“window系统生成”帧缓存。而且,它完全受OpenGL控制。
Similartowindow-system-providedframebuffer,aFBOcontainsacollectionofrenderingdestinations;color,depthandstencilbuffer.(NotethataccumulationbufferisnotdefinedinFBO.)TheselogicalbuffersinaFBOarecalledframebuffer-attachableimages,whichare2Darraysofpixelsthatcanbeattachedtoaframebufferobject.相似于window系统提供的帧缓存,一个FBO也包含一些存储颜色、深度和模板数据的区域。(注意:没有累积缓存)我们把FBO中这些逻辑缓存称之为“帧缓存关联图像”,它们是一些能够和一个帧缓存对象关联起来的二维数组像素。
Therearetwotypesofframebuffer-attachableimages;textureimagesandrenderbufferimages.Ifanimageofatextureobjectisattachedtoaframebuffer,OpenGLperforms"rendertotexture".Andifanimageofarenderbufferobjectisattachedtoaframebuffer,thenOpenGLperforms"offscreenrendering".有两种类型的“帧缓存关联图像”:纹理图像(textureimages)和渲染缓存图像(renderbufferimages)。如果纹理对象的图像数据关联到帧缓存,OpenGL执行的是“渲染到纹理”(rendertotexture)操作。如果渲染缓存的图像数据关联到帧缓存,OpenGL执行的是离线渲染(offscreenrendering)。
Bytheway,renderbufferobjectisanewtypeofstorageobjectdefinedinGL_ARB_framebuffer_objectextension.Itisusedasarenderingdestinationforasingle2Dimageduringrenderingprocess.这里要提到的是,渲染缓存对象是在GL_EXT_framebuffer_object扩展中定义的一种新的存储类型。在渲染过程中它被用作存储单幅二维图像。
Thefollowingdiagramshowstheconnectivityamongtheframebufferobject,textureobjectandrenderbufferobject.Multipletextureobjectsorrenderbufferobjectscanbeattachedtoaframebufferobjectthroughtheattachmentpoints.下面这幅图显示了帧缓存对象、纹理对象和渲染缓存对象之间的联系。多多个纹理对象或者渲染缓存对象能够通过关联点关联到一个帧缓存对象上。
Framebufferobject(FBO)providesanefficientswitchingmechanism;detachthepreviousframebuffer-attachableimagefromaFBO,andattachanewframebuffer-attachableimagetotheFBO.Switchingframebuffer-attachableimagesismuchfasterthanswitchingbetweenFBOs.FBOprovidesglFramebufferTexture2D()toswitch2Dtextureobjects,andglFramebufferRenderbuffer()toswitchrenderbufferobjects.FBO提供了一种高效的切换机制;将前面的帧缓存关联图像从FBO分离,然后把新的帧缓存关联图像关联到FBO。在帧缓存关联图像之间切换比在FBO之间切换要快得多。FBO提供了glFramebufferTexture2DEXT()来切换2D纹理对象和glFramebufferRenderbufferEXT()来切换渲染缓存对象。
voidglGenFramebuffers(GLsizein,GLuint*ids)voidglDeleteFramebuffers(GLsizein,constGLuint*ids)glGenFramebuffers()requires2parameters;thefirstoneisthenumberofframebufferstocreate,andthesecondparameteristhepointertoaGLuintvariableoranarraytostoreasingleIDormultipleIDs.ItreturnstheIDsofunusedframebufferobjects.ID0meansthedefaultframebuffer,whichisthewindow-system-providedframebuffer.glGenFramebuffersEXT()需要两个参数:第一个是要创建的帧缓存的数目,第二个是指向存储一个或者多个ID的变量或数组的指针。它返回未使用的FBO的ID。ID为0表示默认帧缓存,即window系统提供的帧缓存。
And,FBOmaybedeletedbycallingglDeleteFramebuffers()whenitisnotusedanymore.当FBO不再被使用时,FBO可以通过调用glDeleteFrameBuffersEXT()来删除。
glBindFramebuffer()OnceaFBOiscreated,ithastobeboundbeforeusingit.一旦一个FBO被创建,在使用它之前必须绑定。
voidglBindFramebuffer(GLenumtarget,GLuintid)Thefirstparameter,target,shouldbeGL_FRAMEBUFFER,andthesecondparameteristheIDofaframebufferobject.OnceaFBOisbound,allOpenGLoperationsaffectontothecurrentboundframebufferobject.TheobjectID0isreservedforthedefaultwindow-systemprovidedframebuffer.Therefore,inordertounbindthecurrentframebuffer(FBO),useID0inglBindFramebuffer().第一个参数target应该是GL_FRAMEBUFFER_EXT,第二个参数是FBO的ID号。一旦FBO被绑定,之后的所有的OpenGL操作都会对当前所绑定的FBO造成影响。ID号为0表示缺省帧缓存,即默认的window提供的帧缓存。因此,在glBindFramebufferEXT()中将ID号设置为0可以解绑定当前FBO。
Inaddition,renderbufferobjectisnewlyintroducedforoffscreenrendering.Itallowstorenderascenedirectlytoarenderbufferobject,insteadofrenderingtoatextureobject.Renderbufferissimplyadatastorageobjectcontainingasingleimageofarenderableinternalformat.ItisusedtostoreOpenGLlogicalbuffersthatdonothavecorrespondingtextureformat,suchasstencilordepthbuffer.
voidglGenRenderbuffers(GLsizein,GLuint*ids)voidglDeleteRenderbuffers(GLsizein,constGluint*ids)Oncearenderbufferiscreated,itreturnsnon-zeropositiveinteger.ID0isreservedforOpenGL.一旦一个渲染缓存被创建,它返回一个非零的正整数。ID为0是OpenGL保留值。
voidglBindRenderbuffer(GLenumtarget,GLuintid)SameasotherOpenGLobjects,youhavetobindthecurrentrenderbufferobjectbeforereferencingit.ThetargetparametershouldbeGL_RENDERBUFFERforrenderbufferobject.和OpenGL中其他对象一样,在引用渲染缓存之前必须绑定当前渲染缓存对象。他target参数应该是GL_RENDERBUFFER_EXT。
voidglRenderbufferStorage(GLenumtarget,GLenuminternalFormat,GLsizeiwidth,GLsizeiheight)Whenarenderbufferobjectiscreated,itdoesnothaveanydatastorage,sowehavetoallocateamemoryspaceforit.ThiscanbedonebyusingglRenderbufferStorage().ThefirstparametermustbeGL_RENDERBUFFER.Thesecondparameterwouldbecolor-renderable(GL_RGB,GL_RGBA,etc.),depth-renderable(GL_DEPTH_COMPONENT),orstencil-renderableformats(GL_STENCIL_INDEX).Thewidthandheightarethedimensionoftherenderbufferimageinpixels.当一个渲染缓存被创建,它没有任何数据存储区域,所以我们还要为他分配空间。这可以通过用glRenderbufferStorageEXT()实现。第一个参数必须是GL_RENDERBUFFER_EXT。第二个参数可以是用于颜色的(GL_RGB,GL_RGBA,etc.),用于深度的(GL_DEPTH_COMPONENT),或者是用于模板的格式(GL_STENCIL_INDEX)。Width和height是渲染缓存图像的像素维度。ThewidthandheightshouldbelessthanGL_MAX_RENDERBUFFER_SIZE,otherwise,itgeneratesGL_INVALID_VALUEerror.width和height必须比GL_MAX_RENDERBUFFER_SIZE_EXT小,否则将会产生GL_UNVALID_VALUE错误。
voidglGetRenderbufferParameteriv(GLenumtarget,GLenumparam,GLint*value)Youalsogetvariousparametersofthecurrentlyboundrenderbufferobject.targetshouldbeGL_RENDERBUFFER,andthesecondparameteristhenameofparameter.Thelastisthepointertoanintegervariabletostorethereturnedvalue.Theavailablenamesoftherenderbufferparametersare;我们也可以得到当前绑定的渲染缓存对象的一些参数。Target应该是GL_RENDERBUFFER_EXT,第二个参数是所要得到的参数名字。最后一个是指向存储返回值的整型量的指针。渲染缓存的变量名有如下:
GL_RENDERBUFFER_WIDTHGL_RENDERBUFFER_HEIGHTGL_RENDERBUFFER_INTERNAL_FORMATGL_RENDERBUFFER_RED_SIZEGL_RENDERBUFFER_GREEN_SIZEGL_RENDERBUFFER_BLUE_SIZEGL_RENDERBUFFER_ALPHA_SIZEGL_RENDERBUFFER_DEPTH_SIZEGL_RENDERBUFFER_STENCIL_SIZEAttachingimagestoFBOFBOitselfdoesnothaveanyimagestorage(buffer)init.Instead,wemustattachframebuffer-attachableimages(textureorrenderbufferobjects)totheFBO.ThismechanismallowsthatFBOquicklyswitch(detachandattach)theframebuffer-attachableimagesinaFBO.Itismuchfastertoswitchframebuffer-attachableimagesthantoswitchbetweenFBOs.And,itsavesunnecessarydatacopiesandmemoryconsumption.Forexample,atexturecanbeattachedtomultipleFBOs,anditsimagestoragecanbesharedbymultipleFBOs.FBO本身没有图像存储区。我们必须帧缓存关联图像(纹理或渲染对象)关联到FBO。这种机制允许FBO快速地切换(分离和关联)帧缓存关联图像。切换帧缓存关联图像比在FBO之间切换要快得多。而且,它节省了不必要的数据拷贝和内存消耗。比如,一个纹理可以被关联到多个FBO上,图像存储区可以被多个FBO共享。
voidglFramebufferRenderbuffer(GLenumtarget,GLenumattachmentPoint,GLenumrenderbufferTarget,GLuintrenderbufferId)ArenderbufferimagecanbeattachedbycallingglFramebufferRenderbuffer().ThefirstandsecondparametersaresameasglFramebufferTexture2D().ThethirdparametermustbeGL_RENDERBUFFER,andthelastparameteristheIDoftherenderbufferobject.通过调用glFramebufferRenderbufferEXT()可以关联渲染缓存图像。前两个参数和glFramebufferTexture2DEXT()一样。第三个参数只能是GL_RENDERBUFFER_EXT,最后一个参数是渲染缓存对象的ID号。IfrenderbufferIdparameterissetto0,therenderbufferimagewillbedetachedfromtheattachmentpointintheFBO.IfarenderbufferobjectisdeletedwhileitisstillattachedinaFBO,thenitwillbeautomaticallydetachedfromtheboundFBO.However,itwillnotbedetachedfromanyothernon-boundFBOs.如果参数renderbufferId被设置为0,渲染缓存图像将会从FBO的关联点分离。如果渲染缓存图像在依然关联在FBO上时被删除,那么纹理对象将会自动从当前绑定的FBO上分离,而不会从其他非绑定的FBO上分离。
WhenyourendertoaFBO,anti-aliasingisnotautomaticallyenabledevenifyouproperlycreateaOpenGLrenderingcontextwiththemultisamplingattribute(SAMPLEBUFFERS_ARB)forwindow-system-providedframebuffer.
Inordertoactivatemultisampleanti-aliasingmodeforrenderingtoaFBO,youneedtoprepareandattachmultisampleimagestoaFBO'scolorand/ordepthattachementpoints.
FBOextensionprovidesglRenderbufferStorageMultisample()tocreatearenderbufferimageformultisampleanti-aliasingrenderingmode.
voidglRenderbufferStorageMultisample(GLenumtarget,GLsizeisamples,GLenuminternalFormat,GLsizeiwidth,GLsizeiheight)Itaddsnewparameter,samplesontopofglRenderbufferStorage(),whichisthenumberofmultisamplesforanti-aliasedrenderingmode.Ifitis0,thennoMSAAmodeisenabledandglRenderbufferStorage()iscalledinstead.YoucanquerythemaximumnumberofsampleswithGL_MAX_SAMPLEStokeninglGetIntegerv().
ThefollowingcodeistocreateaFBOwithmultisamplecolorbufferanddepthbufferimages.NotethatifmultipleimagesareattachedtoaFBO,thenallimagesmusthavethesamenumberofmultisamples.Otherwise,theFBOstatusisincomplete.
voidglBlitFramebuffer(GLintsrcX0,GLintsrcY0,GLintsrcX1,GLintsrcY1,//sourcerectangleGLintdstX0,GLintdstY0,GLintdstX1,GLintdstY1,//destinationrectGLbitfieldmask,GLenumfilter)glBlitFramebuffer()copiesarectangleofimagesfromthesource(GL_READ_BUFFER)tothedestinationframebuffer(GL_DRAW_BUFFER).The"mask"parameteristospecifywhichbuffersarecopied,GL_COLOR_BUFFER_BIT,GL_DEPTH_BUFFER_BITand/orGL_STENCIL_BUFFER_BIT.Thelastparameter,"filter"istospecifytheinterpolationmodeifthesourceanddestinationrectanglesarenotsame.ItiseitherGL_NEARESTorGL_LINEAR.
ThefollowingcodeistotransferamultisampledimagefromaFBOtoanothernon-multisampledFBO.NoticeitrequiresanadditionalFBOtogettheresultofMSAArendering.PleaseseefboMsaa.zipfordetailstoperformrender-to-texturewithMSAA.
//copyrenderedimagefromMSAA(multi-sample)tonormal(single-sample)//NOTE:Themultisamplesatapixelinreadbufferwillbeconverted//toasinglesampleatthetargetpixelindrawbuffer.glBindFramebuffer(GL_READ_FRAMEBUFFER,fboMsaaId);//srcFBO(multi-sample)glBindFramebuffer(GL_DRAW_FRAMEBUFFER,fboId);//dstFBO(single-sample)glBlitFramebuffer(0,0,width,height,//srcrect0,0,width,height,//dstrectGL_COLOR_BUFFER_BIT,//buffermaskGL_LINEAR);//scalefilterCheckingFBOStatusOnceattachableimages(texturesandrenderbuffers)areattachedtoaFBOandbeforeperformingFBOoperation,youmustvalidateiftheFBOstatusiscompleteorincompletebyusingglCheckFramebufferStatus().IftheFBOisnotcomplete,thenanydrawingandreadingcommand(glBegin(),glCopyTexImage2D(),etc)willbefailed.
一旦关联图像(纹理和渲染缓存)被关联到FBO上,在执行FBO的操作之前,你必须检查FBO的状态,这可以通过调用glCheckFramebufferStatusEXT()实现。如果这个FBObuilding完整,那么任何绘制和读取命令(glBegin(),glCopyTexImage2D(),etc)都会失败。
TherulesofFBOcompletenessare:
FBO完整性准则有:
Notethateventhoughalloftheaboveconditionsaresatisfied,yourOpenGLdrivermaynotsupportsomecombinationsofinternalformatsandparameters.IfaparticularimplementationisnotsupportedbyOpenGLdriver,thenglCheckFramebufferStatus()returnsGL_FRAMEBUFFER_UNSUPPORTED.注意:即使以上所有条件都满足,你的OpenGL驱动也可能不支持某些格式和参数的组合。如果一种特别的实现不被OpenGL驱动支持,那么glCheckFramebufferStatusEXT()返回GL_FRAMEBUFFER_UNSUPPORTED_EXT。
ThesamplecodeprovidessomeutilityfunctionstoreporttheinformationofthecurrentFBO;printFramebufferInfo()andcheckFramebufferStatus().
Thisextensionprovidesanewcommand,DiscardFramebufferEXT,whichcausesthecontentsofthenamedframebufferattachableimagestobecomeundefined.Thecontentsofthespecifiedbuffersareundefineduntilasubsequentoperationmodifiesthecontent,andonlythemodifiedregionisguaranteedtoholdvalidcontent.Effectiveusageofthiscommandmayprovideanimplementationwithnewoptimizationopportunities.SomeOpenGLESimplementationscacheframebufferimagesinasmallpooloffastmemory.Beforerendering,theseimplementationsmustloadtheexistingcontentsofoneormoreofthelogicalbuffers(color,depth,stencil,etc.)intothismemory.Afterrendering,someorallofthesebuffersarelikewisestoredbacktoexternalmemorysotheircontentscanbeusedagaininthefuture.Inmanyapplications,someorallofthelogicalbuffersareclearedatthestartofrendering.Ifso,theefforttoloadorstorethosebuffersiswasted.
Evenwithoutthisextension,ifaframeofrenderingbeginswithafull-screenClear,anOpenGLESimplementationmayoptimizeawaytheloadingofframebuffercontentspriortorenderingtheframe.Withthisextension,anapplicationcanuseDiscardFramebufferEXTtosignalthatframebuffercontentswillnolongerbeneeded.InthiscaseanOpenGLESimplementationmayalsooptimizeawaythestoringbackofframebuffercontentsafterrenderingtheframe.
1)ShouldDiscardFramebufferEXT'sargumentbealistofCOLOR_ATTACHMENTxenums,orshoulditusethesamebitfieldfromClearandBlitFramebuffer
RESOLVED:We'lluseasizedlistofframebufferattachments.Thiswillgiveussomefuture-proofingforwhenMRTsandmultisampledFBOsaresupported.
2)Whathappensiftheappdiscardsonlyoneofthedepthandstencilattachments,butthosearebackedbythesamepacked_depth_stencilbuffera)Generateanerrorb)Bothimagesbecomeundefinedc)Neitherimagebecomesundefinedd)OnlyoneoftheimagesbecomesundefinedRESOLVED:(b)whichsortoffallsoutofIssue4.
3)HowshouldDiscardFramebufferEXTinteractwiththedefaultframebuffera)Generateanerrorb)Ignorethehintsilentlyc)ThecontentsofthespecifiedattachmentsbecomeundefinedRESOLVED:(c),withappropriatewordingtomapFBOattachmentstothecorrespondingdefaultframebuffer'slogicalbuffers
4)Whathappenswhenyoudiscardanattachmentthatdoesn'texistThisisthecasewhereaframebufferiscompletebutdoesn'thave,forexample,astencilattachment,yettheapptriestodiscardthestencilattachment.a)Generateanerrorb)Ignorethehintsilently
RESOLVED:(b)fortworeasons.First,thisisjustahintanyway,andifwerequirederrordetection,thensuddenlyanimplementationcan'ttriviallyignoreit.Second,thisisconsistentwithClear,whichignoresspecifiedbuffersthataren'tpresent.
Sometimes,youneedtogeneratedynamictexturesonthefly.Themostcommonexamplesaregeneratingmirroring/reflectioneffects,dynamiccube/environmentmapsandshadowmaps.Dynamictexturingcanbeaccomplishedbyrenderingthescenetoatexture.Atraditionalwayofrender-to-textureistodrawascenetotheframebufferasnormal,andthencopytheframebufferimagetoatexturebyusingglCopyTexSubImage2D().有时候,你需要产生动态纹理。比较常见的例子是产生镜面反射效果、动态环境贴图和阴影等效果。动态纹理可以通过把场景渲染到纹理来实现。渲染到纹理的一种传统方式是将场景绘制到普通的帧缓存上,然后调用glCopyTexSubImage2D()拷贝帧缓存图像至纹理。
UsingFBO,wecanrenderascenedirectlyontoatexture,sowedon'thavetousethewindow-system-providedframebufferatall.Furthermore,wecaneliminateanadditionaldatacopy(fromframebuffertotexture).使用FBO,我们能够将场景直接渲染到纹理,所以我们不必使用window系统提供的帧缓存。并且,我们能够去除额外的数据拷贝(从帧缓存到纹理);。
Thisdemoprogramperformsrendertotextureoperationwith/withoutFBO,andcomparestheperformancedifference.Otherthanperformancegain,thereisanotheradvantageofusingFBO.Ifthetextureresolutionislargerthanthesizeoftherenderingwindowintraditionalrender-to-texturemode(withoutFBO),thentheareaoutofthewindowregionwillbeclipped.However,FBOdoesnotsufferfromthisclippingproblem.Youcancreateaframebuffer-renderableimagelargerthanthedisplaywindow.这个demo实现了使用FBO和不使用FBO两种情况下渲染到纹理的操作,并且比较了性能差异。除了能够获得性能上的提升,使用FBO的还有另外一个优点。在传统的渲染到纹理的模式中(不使用FBO),如果纹理分辨率比渲染窗口的尺寸大,超出窗口区域的部分将被剪切掉。然后,使用FBO就不会有这个问题。你可以产生比显示窗口大的帧缓存渲染图像。
ThefollowingcodesistosetupaFBOandframebuffer-attachableimagesbeforetherenderingloopisstarted.NotethatnotonlyatextureimageisattachedtotheFBO,butalso,arenderbufferimageisattachedtothedepthattachmentpointoftheFBO.Wedonotactuallyusethisdepthbuffer,however,theFBOitselfneedsitfordepthtest.Ifwedon'tattachthisdepthrenderableimagetotheFBO,thentherenderingoutputwillbecorruptedbecauseofmissingdepthtest.IfstenciltestisalsorequiredduringFBOrendering,thenadditionalrenderbufferimageshouldbeattachedtoGL_STENCIL_ATTACHMENT.以下代码在渲染循环开始之前,对FBO和帧缓存关联图像进行了初始化。注意只有一幅纹理图像被关联到FBO,但是,一个深度渲染图像被关联到FBO的深度关联点。实际上我们并没有使用这个深度缓存,但是FBO本身需要它进行深度测试。如果我们不把这个深度可渲染的图像关联到FBO,那么由于缺少深度测试渲染输出结果是不正确的。如果在FBO渲染期间模板测试也是必要的,那么也需要把额外的渲染图像和GL_STENCIL_ATTACHMENT_EXT关联起来。
opengles2.0渲染到纹理的方法有三种:
pbuffer跟framebuffer功能是一样的,都是用来做渲染到一个off-screensurface上的,但是如果要做的是渲染到一个纹理上,还是使用framebuffer,效率高些。pbuffer的用途是:渲染到纹理上,随后这个纹理可以给其他API用的,比如openVG。创建pbuffer的过程跟创建窗口surface差不多的:
EGLSurfaceeglCreatePbufferSurface(EGLDisplaydisplay,EGLConfigconfig,constEGLint*attribList);需要在attribList指定一些pbuffer的属性。选择config的时候需要指定:EGL_SURFACE_TYPE:EGL_PBUFFER_BIT
频繁的在自己创建的fbo和窗口系统创建的vbo之间切换,比较影响性能。不要在每一帧都去创建,销毁fbo,vbo对象。要一次创建多次使用。如果一个纹理attach到一个fbo的attachmentpoint,就要尽量避免调用glTexImage2D或glTexSubImage2D,glCopyTexImage2D等去修改纹理的值。
移动端十年老人,主要做IM、音视频、AI方向,目前在做鸿蒙化适配,欢迎这些方向的同学交流:wodekouwei