先来整理下本人对FBO的理解。FBO是FrameBuffer Object的首字母简称。翻译过来就是帧缓冲区的意思。根据我的理解,OpenGL作为图形API,可以看做是画笔,帧缓冲区可以比作画布。我们使用OpenGL在帧缓冲区上“作画”(渲染)。
首先,我们需要建立OpenGL Context,即获得一套“作画”的工具。以Win32 OpenGL为例,我们需要如下流程去建立一个标准的OpenGL Context:
1、创建一个窗口类(WNDCLASS/WNDCLASSEX)。
2、注册窗口类(RegisterClass)。
3、调用CreateWindow/CreateWindowEx创建新窗口,返回窗口句柄 HWND。
4、创建设备描述符PIXELFORMATDESCRIPTOR pfd。该结构体中包含即将要创建的OpenGL context的各种信息,包括是否支持双缓冲区、像素位数、Z-buffer位数等各种缓冲区位宽。
5、获取新生成窗口的Device Context句柄HDC hDC,通过调用GetDC(HWND)获得。
6、调用ChoosePixelFormat(hDC,&pfd)选择合适的像素格式,如果成功,返回像素格式的索引GLuint nPixelFormat。
7、利用上一步的结果调用SetPixelFormat(hDC,nPixelFormat,&pfd),设置像素格式。
8、调用wglCreateContext(hDC)获得 OpenGL Context 句柄 HGLRC hRC;
9、设置上一步获得的OpenGL Context为当前的Context,通过调用wglMakeCurrent(hDC,hRC);
不总结不知道,一总结发现一步一步下来,步骤还是挺多的。要注意的一点是,上述9步中大部分函数调用是为了生成/申请相关窗口或者句柄,为了提高程序的鲁棒性,需要及时检查返回值的有效性。
注意:在上述第4步中,我们在设备描述符中通过参数设置了各个缓冲区的位宽。位宽为0当然就是没有这个缓冲区的意思啦。
在建立了OpenGL渲染环境之后,相当于获得了一只画笔,而此时我们有一块默认的画布,即我们的屏幕,default framebuffer。我们渲染的目的地就是我们的屏幕,我们画出来的东西,会显示在屏幕上。这个default framebuffer 是与一系列缓冲区相关联的(具体有哪些缓冲区,多少位的缓冲区,是建立OpenGL Context的时候用户自定义的。一般来讲,必要的是颜色缓冲区,深度缓冲区。模板缓冲区、累加缓冲区这俩哥们儿可选。)。我们需要颜色缓冲区来存储我们渲染物体的颜色,需要深度缓冲区来进行深度测试,等等。具体的渲染过程是如何进行的,就是OpenGL Render Pipeline的东西了。在此不细表。
之后随着新需求的出现和OpenGL的发展,off-screen render技术出现了,即离屏渲染(离线渲染)。我去,off-screen render,离线渲染,乍一听好牛逼的样子,其实很简单。我们把物体直接渲染到屏幕上,就是“在线渲染”。同理,我们把物体渲染到别的地方,不渲染到屏幕上,那不就是“离线渲染”了么。 那我们不渲染到屏幕,渲染到哪儿去呢?
OpenGL 从某版本之后,引入了Framebuffer Object。 XXXX Object,我们见过很多了,像Vertex Buffer Object, Vertex Array Object。这次的Framebuffer Object是什么东西呢?
根据本人的理解,FBO(Framebuffer Object)就是OpenGL模拟default framebuffer的功能和结构创建的一种可以作为“画布”使用的Object。也就是说,你生成一个FBO,根据你的渲染需要,捯饬捯饬,然后把你想渲染的东西渲染到你刚生成的这个FBO里面,而不是直接渲染到屏幕上,就是这个样子。Default framebuffer 有很多支撑渲染行为的缓冲区,FBO也可以有,但是要你手动去生成、设置和绑定。
值得注意的是FBO的角色更像是一个管理者,管理着所有支撑渲染的RenderBuffers和Textures,OpenGL没有为FBO分配内存空间去存储渲染所需的几何、像素数据等。但是,FBO有很多Attachment Point,顾名思义,我们把真正起作用的、具有实际内存空间占用的Renderbuffer和Texutures依附在FBO上,FBO起到管理的作用。这点跟VAO有点类似,是一批量“状态”的集合。VAO的事情我觉得有必要再整理下思路,但是此处暂且按下不表。
最近爱上了Viso作图,如下:
貌似不能插pdf,只能插图片。不清晰,此处有pdf:
下面简单说下两种情况的用法:
下面这段代码说明如何将Renderbuffer与FBO绑定。
1 GLuint hFbo,hTex,hDepth,hColor; 2 3 glGenFramebuffers(1,&hFbo); 4 glBindFramebuffer(GL_FRAMEBUFFER,hFbo); 5 6 glGenRenderbuffers(1,&hDepth); 7 glBindRenderbuffer(GL_RENDERBUFFER,hDepth); 8 9 //为当前的Renderbuffer分配空间,格式为GL_DEPTH_COMPONENT,顾名思义 10 //这个Renderbuffer肯定是与depth test相关的。11 glRenderbufferStorage(GL_RENDERBUFFER,GL_DEPTH_COMPONENT,m_width,m_height);12 13 //将当前的Renderbuffer与Framebuffer Object的GL_DEPTH_ATTACHMENT的连接点14 //连接起来。再顾名思义,肯定是与Depth test相关的。15 glFramebufferRenderbuffer(GL_FRAMEBUFFER,GL_DEPTH_ATTACHMENT,GL_RENDERBUFFER,hDepth);16 17 glGenRenderbuffers(1,&hColor);18 glBindRenderbuffer(GL_RENDERBUFFER,hColor);19 20 //为当前的Renderbuffer分配空间,格式为GL_RGBA21 glRenderbufferStorage(GL_RENDERBUFFER,GL_RGBA,m_width,m_height);22 23 //与FBO的GL_COLOR_ATTACHMENT0连接起来。24 glFramebufferRenderbuffer(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_RENDERBUFFER,hColor);25 26 //检查Framebuffer的完整性,十分必要!!!!!27 //十分必要!!!!!28 GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);29 if( status != GL_FRAMEBUFFER_COMPLETE)30 printf("Framebuffer incomplete!\n");31 else32 printf("Framebuffer complete!\n");
下面这段代码说明如何将Texture与FBO绑定。
1 GLuint hFbo,hTex,hDepth,hColor; 2 3 glGenFramebuffers(1,&hFbo); 4 glBindFramebuffer(GL_FRAMEBUFFER,hFbo); 5 6 glGenRenderbuffers(1,&hDepth); 7 glBindRenderbuffer(GL_RENDERBUFFER,hDepth); 8 9 //为当前的Renderbuffer分配空间,格式为GL_DEPTH_COMPONENT,顾名思义 10 //这个Renderbuffer肯定是与depth test相关的。11 glRenderbufferStorage(GL_RENDERBUFFER,GL_DEPTH_COMPONENT,m_width,m_height);12 13 //将当前的Renderbuffer与Framebuffer Object的GL_DEPTH_ATTACHMENT的连接点14 //连接起来。再顾名思义,肯定是与Depth test相关的。15 glFramebufferRenderbuffer(GL_FRAMEBUFFER,GL_DEPTH_ATTACHMENT,GL_RENDERBUFFER,hDepth);16 17 glActiveTexture(GL_TEXTURE0);18 glGenTextures(1,&hTex);19 glBindTexture(GL_TEXTURE_2D,hTex);20 21 //为纹理分配空间。22 glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,m_width,m_height,0,GL_RGBA,GL_UNSIGNED_BYTE,NULL);23 24 //与FBO的GL_COLOR_ATTACHMENT0绑定,Color。。color。。25 glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,hTex,0);26 27 28 //检查Framebuffer的完整性,十分必要!!!!!29 //十分必要!!!!!30 GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);31 if( status != GL_FRAMEBUFFER_COMPLETE)32 printf("Framebuffer incomplete!\n");33 else34 printf("Framebuffer complete!\n");