1. 程式人生 > >FBO和FBOA離線快取物件方便高效實現累積快取功能

FBO和FBOA離線快取物件方便高效實現累積快取功能

FBO是不使用視窗系統提供的顏色前後臺快取和輔助快取,深度快取,模板快取
而是在OGL層面在GPU中開闢這些快取,這些快取有兩種型別,TextureBuffer(存放紋理顏色快取), RenderFrameBuffer(存放深度和模板快取,也可以存放顏色快取),
這些快取叫FBOA(attach)。FBO只是一個索引機制,類似VAO。FBO->FBOA渲染到指定的快取區(同樣在GPU中),FBO->視窗系統硬體快取,FBO->是通過繫結操作實現的:
但是也要建立FBO
1).建立渲染快取物件Renderbuffer Object.
2).建立紋理快取物件Textures Object.
3).建立FBO
4).將FBOA繫結到FBO
// attach the texture to FBO color attachment point  
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,  
                          GL_TEXTURE_2D, textureId, 0);  
  
// attach the renderbuffer to depth attachment point  
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,  
                             GL_RENDERBUFFER_EXT, rboId);  
5).將渲染目標幫到到不可顯示的應用程式FBOA(紋理快取,或渲染快取中),而不是可顯示的視窗系統硬體快取。
// set rendering destination to FBO  
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboId); 
6).呼叫drawCall繪製物體
7).FBO渲染完,切換回視窗系統
// switch back to window-system-provided framebuffer  
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);  


FBO加快了渲染到紋理的速度,而不是渲染到後臺快取,再從後臺快取中拷貝到紋理快取中(unity的rendertexture不知道是不是這個機制實現的)。
可以直接渲染到OGL的,離屏的浮點型別的FBOA紋理快取,深度快取,模板快取(深度和模板也是為了紋理處理);通過對這些紋理組合,可以實現累積快取的效果,
且更加方便控制和高效,因此不需要累積快取的存在了。

物體過濾和豐富影象效果的實現:

物體繪製過濾處理:

很多時候繪製一次物體和影象得不到想要的效果。需要進行多道渲染演算法,也就是Shader中的多個Pass。 通過將資訊記錄在深度快取中,或者記錄在應用程式中。 啟用深度測試,深度快取寫入,啟用模板測試和模板快取寫入,禁止顏色快取寫入,通過繪製表面翻轉,關閉一些狀態開啟一些狀態 得到模板快取中的值。 開啟顏色快取寫入,然後根據模板快取中的值,繪製物體,得到想要的過濾效果。

豐富的影象效果:

多道渲染,結合浮點數的Shader過濾,移動,旋轉,縮放,映象變換,FBO幀快取物件對影象的讀寫快取(累積緩衝區), 可以得到很多豐富的影象效果。 參考文章: http://blog.csdn.net/xiajun07061225/article/details/7283929/

Overview:

    在OpenGL渲染管線中,幾何資料和紋理經過多次轉化和多次測試,最後以二維畫素的形式顯示在螢幕上。OpenGL管線的最終渲染目的地被稱作幀快取(framebuffer)。幀緩衝是一些二維陣列和OpenG所使用的儲存區的集合:顏色快取、深度快取、模板快取和累計快取。一般情況下,幀快取完全由window系統生成和管理,由OpenGL使用。這個預設的幀快取被稱作“window系統生成”(window-system-provided)的幀快取。

在OpenGL擴充套件中,GL_EXT_framebuffer_object提供了一種建立額外的不能顯示的幀快取物件的介面。為了和預設的“window系統生成”的幀快取區別,這種幀緩衝成為應用程式幀快取(application-createdframebuffer)。通過使用幀快取物件(FBO),OpenGL可以將顯示輸出到引用程式幀快取物件,而不是傳統的“window系統生成”幀快取。而且,它完全受OpenGL控制。

相似於window系統提供的幀快取,一個FBO也包含一些儲存顏色、深度和模板資料的區域。(注意:沒有累積快取)我們把FBO中這些邏輯快取稱之為“幀快取關聯影象”,它們是一些能夠和一個幀快取物件關聯起來的二維陣列畫素。

有兩種型別的“幀快取關聯影象”:紋理影象(texture images)和渲染快取影象(renderbuffer images)。如果紋理物件的影象資料關聯到幀快取,OpenGL執行的是“渲染到紋理”(render to texture)操作。如果渲染快取的影象資料關聯到幀快取,OpenGL執行的是離線渲染(offscreen rendering)。

這裡要提到的是,渲染快取物件是在GL_EXT_framebuffer_object擴充套件中定義的一種新的儲存型別。在渲染過程中它被用作儲存單幅二維影象。

下面這幅圖顯示了幀快取物件、紋理物件和渲染快取物件之間的聯絡。多多個紋理物件或者渲染快取物件能夠通過關聯點關聯到一個幀快取物件上。


在一個幀快取物件中有多個顏色關聯點(GL_COLOR_ATTACHMENT0_EXT,...,GL_COLOR_ATTACHMENTn_EXT),一個深度關聯點(GL_DEPTH_ATTACHMENT_EXT),和一個模板關聯點(GL_STENCIL_ATTACHMENT_EXT)。每個FBO中至少有一個顏色關聯點,其數目與實體顯示卡相關。可以通過GL_MAX_COLOR_ATTACHMENTS_EXT來查詢顏色關聯點的最大數目。FBO有多個顏色關聯點的原因是這樣可以同時將顏色而換成渲染到多個FBO關聯區。這種“多渲染目標”(multiple rendertargets,MRT)可以通過GL_ARB_draw_buffers擴充套件實現。需要注意的是:FBO本身並沒有任何影象儲存區,只有多個關聯點。

FBO提供了一種高效的切換機制;將前面的幀快取關聯影象從FBO分離,然後把新的幀快取關聯影象關聯到FBO。在幀快取關聯影象之間切換比在FBO之間切換要快得多。FBO提供了glFramebufferTexture2DEXT()來切換2D紋理物件和glFramebufferRenderbufferEXT()來切換渲染快取物件。

建立FBO

建立FBO和產生VBO類似。

glGenFramebuffersEXT()

Void gelGenFramebuffersEXT(GLsizei n,GLuint* ids);

void glDeleteFramebuffersEXT(GLsizei n, const GLuint* ids);

glGenFramebuffersEXT()需要兩個引數:第一個是要建立的幀快取的數目,第二個是指向儲存一個或者多個ID的變數或陣列的指標。它返回未使用的FBO的ID。ID為0表示預設幀快取,即window系統提供的幀快取。

當FBO不再被使用時,FBO可以通過呼叫glDeleteFrameBuffersEXT()來刪除。

glBindFramebufferEXT()

一旦一個FBO被建立,在使用它之前必須繫結。

void glBindFramebufferEXT(GLenum target, GLuint id)

第一個引數target應該是GL_FRAMEBUFFER_EXT,第二個引數是FBO的ID號。一旦FBO被繫結,之後的所有的OpenGL操作都會對當前所繫結的FBO造成影響。ID號為0表示預設幀快取,即預設的window提供的幀快取。因此,在glBindFramebufferEXT()中將ID號設定為0可以解綁定當前FBO。

渲染快取物件(Renderbuffer Object)

另外,渲染快取是為離線渲染而新引進的。它允許將一個場景直接渲染到一個渲染快取物件中,而不是渲染到紋理物件中。渲染快取物件是用於儲存單幅影象的資料儲存區域。該影象按照一種可渲染的內部格式儲存。它用於儲存沒有相關紋理格式的OpenGL邏輯快取,比如模板快取或者深度快取。

glGenRenderbuffersEXT()

void glGenRenderbuffersEXT(GLsizei n, GLuint* ids)

void glDeleteRenderbuffersEXT(GLsizei n, const Gluint* ids)

一旦一個渲染快取被建立,它返回一個非零的正整數。ID為0是OpenGL保留值。

glBindRenderbufferEXT()

void glBindRenderbufferEXT(GLenum target, GLuint id)

和OpenGL中其他物件一樣,在引用渲染快取之前必須綁定當前渲染快取物件。他target引數應該是GL_RENDERBUFFER_EXT。

glRenderbufferStorageEXT()

void glRenderbufferStorageEXT(GLenum target, GLenum internalFormat,

                             GLsizei width, GLsizei height)

當一個渲染快取被建立,它沒有任何資料儲存區域,所以我們還要為他分配空間。這可以通過用glRenderbufferStorageEXT()實現。第一個引數必須是GL_RENDERBUFFER_EXT。第二個引數可以是用於顏色的(GL_RGB,GL_RGBA,etc.),用於深度的(GL_DEPTH_COMPONENT),或者是用於模板的格式(GL_STENCIL_INDEX)。Width和height是渲染快取影象的畫素維度。

width和height必須比GL_MAX_RENDERBUFFER_SIZE_EXT小,否則將會產生GL_UNVALID_VALUE錯誤。

glGetRenderbufferParameterivEXT()

void glGetRenderbufferParameterivEXT(GLenum target, GLenum param,GLint* value);

我們也可以得到當前繫結的渲染快取物件的一些引數。Target應該是GL_RENDERBUFFER_EXT,第二個引數是所要得到的引數名字。最後一個是指向儲存返回值的整型量的指標。渲染快取的變數名有如下:

GL_RENDERBUFFER_WIDTH_EXT

GL_RENDERBUFFER_HEIGHT_EXT

GL_RENDERBUFFER_INTERNAL_FORMAT_EXT

GL_RENDERBUFFER_RED_SIZE_EXT

GL_RENDERBUFFER_GREEN_SIZE_EXT

GL_RENDERBUFFER_BLUE_SIZE_EXT

GL_RENDERBUFFER_ALPHA_SIZE_EXT

GL_RENDERBUFFER_DEPTH_SIZE_EXT

GL_RENDERBUFFER_STENCIL_SIZE_EXT

將影象和FBO關聯

FBO本身沒有影象儲存區。我們必須幀快取關聯影象(紋理或渲染物件)關聯到FBO。這種機制允許FBO快速地切換(分離和關聯)幀快取關聯影象。切換幀快取關聯影象比在FBO之間切換要快得多。而且,它節省了不必要的資料拷貝和記憶體消耗。比如,一個紋理可以被關聯到多個FBO上,影象儲存區可以被多個FBO共享。

把2D紋理影象關聯到FBO

glFramebufferTexture2DEXT(GLenum target,

                          GLenumattachmentPoint,

                         GLenum textureTarget,

                         GLuint textureId,

                         GLint  level)

glFramebufferTexture2DEXT()把一幅紋理影象關聯到一個FBO。第一個引數一定是GL_FRAMEBUFFER_EXT,第二個引數是關聯紋理影象的關聯點。第三個引數textureTarget在多數情況下是GL_TEXTURE_2D。第四個引數是紋理物件的ID號。最後一個引數是要被關聯的紋理的mipmap等級

如果引數textureId被設定為0,那麼紋理影象將會被從FBO分離。如果紋理物件在依然關聯在FBO上時被刪除,那麼紋理物件將會自動從當前幫的FBO上分離。然而,如果它被關聯到多個FBO上然後被刪除,那麼它將只被從繫結的FBO上分離,而不會被從其他非繫結的FBO上分離。

把渲染快取物件關聯到FBO

void glFramebufferRenderbufferEXT(GLenum target,

                                 GLenum attachmentPoint,

                                 GLenum renderbufferTarget,

                                 GLuint renderbufferId)

通過呼叫glFramebufferRenderbufferEXT()可以關聯渲染快取影象。前兩個引數和glFramebufferTexture2DEXT()一樣。第三個引數只能是GL_RENDERBUFFER_EXT,最後一個引數是渲染快取物件的ID號。

如果引數renderbufferId被設定為0,渲染快取影象將會從FBO的關聯點分離。如果渲染快取影象在依然關聯在FBO上時被刪除,那麼紋理物件將會自動從當前繫結的FBO上分離,而不會從其他非繫結的FBO上分離。

檢查FBO狀態

一旦關聯影象(紋理和渲染快取)被關聯到FBO上,在執行FBO的操作之前,你必須檢查FBO的狀態,這可以通過呼叫glCheckFramebufferStatusEXT()實現。如果這個FBObuilding完整,那麼任何繪製和讀取命令(glBegin(),glCopyTexImage2D(), etc)都會失敗。

GLenum glCheckFramebufferStatusEXT(GLenum target)

glCheckFramebufferStatusEXT()檢查當前幀快取的關聯影象和幀快取引數。這個函式不能在glBegin()/glEnd()之間呼叫。Target引數必須為GL_FRAMEBUFFER_EXT。它返回一個非零值。如果所有要求和準則都滿足,它返回GL_FRAMEBUFFER_COMPLETE_EXT。否則,返回一個相關錯誤程式碼告訴我們哪條準則沒有滿足。

FBO完整性準則有:

(1)幀快取關聯影象的寬度和高度必須非零。

(2)如果一幅影象被關聯到一個顏色關聯點,那麼這幅影象必須有顏色可渲染的內部格式(GL_RGBA, GL_DEPTH_COMPONENT, GL_LUMINANCE, etc)。

(3)如果一幅被影象關聯到GL_DEPTH_ATTACHMENT_EXT,那麼這幅影象必須有深度可渲染的內部格式(GL_DEPTH_COMPONENT,GL_DEPTH_COMPONENT24_EXT, etc)。

(4)如果一幅被影象關聯到GL_STENCIL_ATTACHMENT_EXT,那麼這幅影象必須有模板可渲染的內部格式(GL_STENCIL_INDEX,GL_STENCIL_INDEX8_EXT, etc)。

(5)FBO至少有一幅影象關聯。

(6)被關聯到FBO的縮影影象必須有相同的寬度和高度。

(7)被關聯到顏色關聯點上的所有影象必須有相同的內部格式。

注意:即使以上所有條件都滿足,你的OpenGL驅動也可能不支援某些格式和引數的組合。如果一種特別的實現不被OpenGL驅動支援,那麼glCheckFramebufferStatusEXT()返回GL_FRAMEBUFFER_UNSUPPORTED_EXT。

示例:渲染到紋理


包括渲染到紋理、只渲染到深度快取和使用模板快取渲染物件的輪廓。

有時候,你需要產生動態紋理。比較常見的例子是產生鏡面反射效果、動態環境貼圖和陰影等效果。動態紋理可以通過把場景渲染到紋理來實現。渲染到紋理的一種傳統方式是將場景繪製到普通的幀快取上,然後呼叫glCopyTexSubImage2D()拷貝幀快取影象至紋理。

使用FBO,我們能夠將場景直接渲染到紋理,所以我們不必使用window系統提供的幀快取。並且,我們能夠去除額外的資料拷貝(從幀快取到紋理);。

這個demo實現了使用FBO和不使用FBO兩種情況下渲染到紋理的操作,並且比較了效能差異。除了能夠獲得性能上的提升,使用FBO的還有另外一個優點。在傳統的渲染到紋理的模式中(不使用FBO),如果紋理解析度比渲染視窗的尺寸大,超出視窗區域的部分將被剪下掉。然後,使用FBO就不會有這個問題。你可以產生比顯示視窗大的幀快取渲染影象。

以下程式碼在渲染迴圈開始之前,對FBO和幀快取關聯影象進行了初始化。注意只有一幅紋理影象被關聯到FBO,但是,一個深度渲染影象被關聯到FBO的深度關聯點。實際上我們並沒有使用這個深度快取,但是FBO本身需要它進行深度測試。如果我們不把這個深度可渲染的影象關聯到FBO,那麼由於缺少深度測試渲染輸出結果是不正確的。如果在FBO渲染期間模板測試也是必要的,那麼也需要把額外的渲染影象和GL_STENCIL_ATTACHMENT_EXT關聯起來。

[cpp] view plain copy  print?
  1. // create a texture object
  2. GLuint textureId;  
  3. glGenTextures(1, &textureId);  
  4. glBindTexture(GL_TEXTURE_2D, textureId);  
  5. glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);  
  6. glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);  
  7. glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);  
  8. glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);  
  9. glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); // automatic mipmap
  10. glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, TEXTURE_WIDTH, TEXTURE_HEIGHT, 0,  
  11.              GL_RGBA, GL_UNSIGNED_BYTE, 0);  
  12. glBindTexture(GL_TEXTURE_2D, 0);  
  13. // create a renderbuffer object to store depth info
  14. GLuint rboId;  
  15. glGenRenderbuffersEXT(1, &rboId);  
  16. glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, rboId);  
  17. glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT,  
  18.                          TEXTURE_WIDTH, TEXTURE_HEIGHT);  
  19. glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);  
  20. // create a framebuffer object
  21. GLuint fboId;  
  22. glGenFramebuffersEXT(1, &fboId);  
  23. glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboId);  
  24. // attach the texture to FBO color attachment point
  25. glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,  
  26.                           GL_TEXTURE_2D, textureId, 0);  
  27. // attach the renderbuffer to depth attachment point
  28. glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,  
  29.                              GL_RENDERBUFFER_EXT, rboId);  
  30. // check FBO status
  31. GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);  
  32. if(status != GL_FRAMEBUFFER_COMPLETE_EXT)  
  33.     fboUsed = false;  
  34. // switch back to window-system-provided framebuffer
  35. glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);  
  36. ...  

渲染到紋理的過程和普通的繪製過程基本一樣。我們只需要把渲染的目的地由window系統提供的幀快取改成不可顯示的應用程式建立的幀快取(FBO)就可以了。

[cpp] view plain copy  print?
  1. ...  
  2. // set rendering destination to FBO
  3. glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboId);  
  4. // clear buffers
  5. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);  
  6. // draw a scene to a texture directly
  7. draw();  
  8. // unbind FBO
  9. glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);  
  10. // trigger mipmaps generation explicitly
  11. // NOTE: If GL_GENERATE_MIPMAP is set to GL_TRUE, then glCopyTexSubImage2D()
  12. // triggers mipmap generation automatically. However, the texture attached
  13. // onto a FBO should generate mipmaps manually via glGenerateMipmapEXT().
  14. glBindTexture(GL_TEXTURE_2D, textureId);  
  15. glGenerateMipmapEXT(GL_TEXTURE_2D);  
  16. glBindTexture(GL_TEXTURE_2D, 0);  
  17. ...  

注意到,glGenerateMipmapEXT()也是作為FBO擴充套件的一部分,用來在改變了紋理影象的基級之後顯式生成mipmap的。如果GL_GENERATE_MIPMAP被設定為GL_TRUE,那麼glTex{Sub}Image2D()和 glCopyTex{Sub}Image2D()將會啟用自動mipmap生成(在OpenGL版本1.4或者更高版本中)。然後,當紋理基級被改變時,FBO操作不會自動產生mipmaps。因為FBO不會呼叫glCopyTex{Sub}Image2D()來修改紋理。因此,要產生mipmap,glGenerateMipmapEXT()必須被顯示呼叫。