« 学一学,FBOShadow Volume 阴影锥技术之探Ⅴ »

OpenGL怎样近似进行同时到FBO和屏幕的渲染

好吧,你想像上一篇文章(学一学,FBO)所提及的把p={GL_BACK_LEFT,GL_AUX0}传入glDrawbuffers(2,*p)那样,同时进行“在线”渲染和“离线”渲染,而不是分开地先FBO后屏幕或者先屏幕后FBO渲染两次?这里是我找到的方法。尽管只是近似,但效果应该不错了(希望OPENGL日后可以用某种技法实现真正的“同时”)。——ZwqXin.com

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

首先,说一下“屏幕”的概念。我这里所说屏幕(screen)通常用来指渲染窗口所占的那部分“屏幕”,可能是因为平时开渲染窗口多是开全屏,或者至少占满窗口吧,英语站譬如gamedev上也有很多人直接用“屏幕”来指代那个渲染窗口整体。事实上更准确一点(一点...)的叫法是main frame buffer(主-帧缓存)。因为那些像素显示的颜色直接来自颜色缓冲,加之深度蒙扳等等,所以“屏幕”这个概念本身也可用来指代一块供像素信息存储的缓存,当然广义点说,这个缓存的内容就包括像素格式设置中启用的各种像素格式吧(请指出我的可能的理解错误),但是我们还是专指我们常说的那些缓存吧——由OPENGL向windows系统(举例...举例)请求生成的用于保存像素信息的特定内存区域,完全启用后包括颜色缓存color-buffer,深度缓存depth-buffer,蒙扳缓存stencil-buffer,累积缓存accumulation-buffer和辅助缓存auxiliary-buffer(P.S.注意正常使用记得在像素设置函数中开启)。其中颜色缓存color-buffer直接形成我们所见之屏幕颜色,在双缓冲机制下又分前左、右,后左、右四个。

至于,FBO内的渲染缓存,如前面的文章所述,是一种相当于OPENGL自个儿申请并玩乐(控制)的内存,因为它不直接经过我们的系统,因此与windows控制的显示器也基本无缘了(所以我猜想D3D一定有……)——我曾经天真地以为能够把屏幕作为渲染缓存(renderbuffer)交给FBO(其实是昨晚),使在FBO中渲染的内容能通过MRT(mutiple render target)同时显示在屏幕上——我错了,我错了55。记住,现在的FBO不能直接跟屏幕/main frame buffer打交道。

FBO能够模拟屏幕。如果某个FBO ≈ 屏幕,那么把一个绑定到该FBO的“颜色渲染缓存color-render-buffer”(对应FBO的GL_COLOR_ATTACHMENTi_EXT管理标签[目标,Targer],下同)≈ 颜色缓存color-buffer; 绑定到FBO的“深度渲染缓存color-render-buffer”(GL_DEPTH_ATTACHMENT_EXT)≈ 深度缓存depth-buffer;绑定到FBO的“蒙扳渲染缓存color-render-buffer”(GL_STENCIL_ATTACHMENT_EXT)≈ 蒙扳缓存stencil-buffer,不就是一个“屏幕”了吗?只不过这个“屏幕”没有经过windows批准,无法在显示屏上“显示”而已。(注意,GL_COLOR_ATTACHMENTi_EXT随i最多可有好几个,MAX值根据硬件情况不同;另外暂时没有对应其他缓存的render-buffer。l另外FBO还有texture这种手段!)

那么假如我现在要渲染一个场景并把场景的深度信息保存到一张纹理上,怎么办呢?难道要渲染两次?天啊,不会吧。不会的……除非你不会我接下来要讲的方法。好了,我知道罗嗦,但先希望你别弄混屏幕渲染中像素的depth-buffer和一个FBO对象中那个depth-render-buffer,它们现阶段最多也只是≈而已,所以别奢望能直接把屏幕渲染过程中产生的那个depth-buffer投入FBO中,FBO不认它的;同样,别奢望屏幕能认得FBO渲染过程中产生的depth-render-buffer。要时刻把它们看成是互相独立的。虽然相互独立,但未免没有一些取巧的手法联系它们。以下方法搜得比较累,请好心人告知更的方法:

1. Screen-Size-QUAD with the texture  created by FBO
用一张不透明的全屏幕矩形遮盖整个渲染窗口(/“屏幕”),而且是挡在眼睛前面。这.....这,当你准备开口说不可思仪突然掩住嘴巴,我知道你已经领悟了。是的,“屏幕”到底也就是一块缓存,屏幕颜色到底也就这缓存里的一块,而且这个缓存还是固定大小的呢(屏幕大小,譬如我的机器是1024*768象素)!而FBO中绑定的纹理可大可小,比屏幕大得多也行,小得多也行(当然你别太离谱了)。何必来张跟屏幕一样大小的纹理(1024*768),作屏幕截图呢?还有什么犹豫的吗?在FBO中作MRT渲染,在shader中把gl_Data[0]写入屏幕颜色(这里,直接等于gl_Color得了);在gl_Data[1]中写入深度信息(也不难,不过最好还是把深度值除以视景体设置中确定得总深度吧,让结果处于[0,1]区间,这是shader的知识了~)。在应用FBO初始化中,按MRT步骤把p = {GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT}(假设前后已经各自绑定了一个纹理)传入glDrawBuffers(2, p); 这样在应用中直接可以绑定此FBO渲染场景,渲染完场景后(还记得注意哪里吗)画一个全屏矩形,用之前第一个纹理(gl_Data[0]写入的那个,假设纹理句柄为img)作为此矩形的纹理:

  1. .....
  2. glEnable(GL_LIGHTING);
  3.  
  4. glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, FBO);//应用绑定FBO
  5. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  6.  
  7. glUseProgram(shader1);//应用shader
  8. ....//渲染场景
  9. glUseProgram(0);
  10.  
  11. glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);//脱离绑定
  12.  
  13. glMatrixMode(GL_PROJECTION);//转入正投影
  14. glPushMatrix();
  15. glLoadIdentity();
  16. glOrtho( 0, VB_WIDTH, VB_HEIGHT, 0, -1, 1 );//屏幕大小
  17. glMatrixMode(GL_MODELVIEW);
  18. glPushMatrix();
  19. glLoadIdentity();
  20.  
  21. glEnable(GL_TEXTURE_2D);
  22. glBindTexture(GL_TEXTURE_2D, img);
  23.  
  24. glBegin(GL_QUADS);
  25. glTexCoord2f(0, 1);glVertex2f(0,0);   
  26. glTexCoord2f(0, 0);glVertex2f(0,VB_HEIGHT);
  27. glTexCoord2f(1, 0);glVertex2f(VB_WIDTH, VB_HEIGHT);
  28. glTexCoord2f(1, 1);glVertex2f(VB_WIDTH,0);  
  29. glEnd();
  30.  
  31. glDisable(GL_TEXTURE_2D);
  32.  
  33. glMatrixMode( GL_PROJECTION );//回来
  34. glPopMatrix();
  35. glMatrixMode( GL_MODELVIEW );   
  36. glPopMatrix();    

2.glBlitFramebufferEXT
bilt,意思是“传图”,其作用是在两个或多个帧缓存对象之间对拷。都这个函数最初也是个拓展,现在嘛,也EXT了,既然都EXT了,证明它够强大,下一步要入标准了。其标准用法如下(前面FBO内渲染过程一样,把上面“脱离绑定”-“回来”一段替换成下面的的):

 

  1.       glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, FBO);
  2.       glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, 0);
  3.  
  4. glBlitFramebufferEXT(0, 0, VB_WIDTH, VB_HEIGHT, 0, 0, VB_WIDTH, VB_HEIGHT, 
  5.                            GL_COLOR_BUFFER_BIT, GL_NEAREST);
  6.  
  7.       glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, 0);
  8.       glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, 0);

 

一目了然:前面GL_READ_FRAMEBUFFER_EXT指定绑定要读取内容的帧对象,GL_DRAW_FRAMEBUFFER_EXT指定要传入的帧对象——这里是,从FBO到0,记得上篇文章说“0就是屏幕代号”吗?呵呵。glBlitFramebufferEXT,开始传图了,前8个参数是传出传入的帧缓存对象的大小,明显这里只传绑定到此FBO的第一个纹理到屏幕缓存(main frame buffer),GL_COLOR_BUFFER_BIT指明传的是颜色类型缓存,GL_NEAREST明显是插值选项,注意这里只能用GL_NEAREST。最后两行是例行的解除绑定。好了!完成呢。

两种方法有什么不同呢?首先,我都是第一次用,谈不上太多。而且用例这么简单,效率比较就算了吧。但我建议用第2种:1.简单。2.看到上面我故意写多了个glEnable(GL_LIGHTING)了吗?当然它能影响画的矩形,但是你希望它影响吗,你希望它影响的是那个FBO中渲染的场景而不是这个“矩形”吧,所以这里要小心状态量的启动/禁止;但是传图的话,因为没有生成新的几何体,所以就没有上述担忧情况。

哇。又是一篇自我满意的文章。希望你能把想法与我交流啦。这里是ZwqXin: www.zwqxin.com, 我的3D旅途记录簿。(纪念今天2009.2.8.BLOG相继开始被GOOGLE,BAIDU收录啦。)

以下是上述例子的演示图,使用了“传图”技术,我在shader里把gl_Data[]顺序调转一下因此传出到屏幕的是压为[0,1]区间的深度信息,并保存到蓝色通道。现在,你能判别出哪个球远哪个球近吗?
通过bilt技术实现近似同时渲染到FBO和屏幕

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

  • quote 1.loga
  • 刚开始学习opengl,各种不明白不理解。
    zwqxin 于 2012-5-8 22:41:00 回复
    恩,慢慢学吧~~谁刚开始都是这样的嘛。
  • 2012-5-7 22:09:45 回复该留言
  • quote 2.I飞影
  • 文章里面说depth-render-buffer并不是屏幕渲染中像素的depth-buffer,那这个depth-render-buffer存储的是什么?有什么用处?
    zwqxin 于 2012-9-5 20:55:21 回复
    一般用来存储深度信息。譬如渲染到离线缓冲区(FBO)的场景深度。
  • 2012-9-5 11:07:45 回复该留言

发表评论:

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

IE下本页面显示有问题?

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

日历

Search

网站分类

最新评论及回复

最近发表

Powered By Z-Blog 1.8 Walle Build 100427

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