概要
在OpenGL的渲染管線中,幾何數據和紋理通過一系列變換和測試,最終被渲染成屏幕上的二維像素。那些用於存儲顏色值和測試結果的二維數組的幾何被稱為幀緩沖區(frame buffer)。這些二維數組按用途劃分,可分為顏色緩沖區(color buffer),深度緩沖區(depth buffer),模版緩沖區(stencil buffer)和累加緩沖區(accumulation buffer)。當我們創建了一個可供OpenGL繪制用的窗體後,窗體系統會為我們生成一個默認的幀緩沖區,這個幀緩沖區完全是由窗體系統來管理的,且僅用於將渲染後的圖像輸出到窗口的顯示區域。
然而,我們可以使用OpenGL提供的GL_EXT_framebuffer_object擴展功能來創建額外的幀緩沖區。GL_EXT_framebuffer_object擴展功能中,提出了幀緩沖區對象(framebuffer object,縮寫為FBO,下文中將使用FBO來代表幀緩沖區對象)的概念,用於對幀緩沖區進行建模。這樣一來,無論是窗體系統創建的幀緩沖區,還是用於屏外渲染的幀緩沖區,都是FBO的實例。有了FBO,程序員就可以重定向渲染目標到其他的存儲空間,比如將渲染目標重定向到紋理空間,實現渲染到紋理功能(Render to Texture)。
上文提到過幀緩沖區中包含的二維數組按用途劃分,可分為顏色緩沖區(color buffer),深度緩沖區(depth buffer),模版緩沖區(stencil buffer)和累加緩沖區(accumulation buffer)。FBO中也提供了與顏色緩沖區、深度緩沖區和模版緩沖區相對應的功能(注意,FBO沒有提供與累加緩沖區對應的功能)。但是,這並不意味著FBO會直接為這些緩沖區分配空間。FBO只是為這些緩沖區提供一個或多個掛接點。我們需要分別為各個緩沖區創建對象,申請空間,然後掛接到相應的掛接點上。FBO提供的掛接點如下圖所示。
可以看出,FBO提供了多個顏色緩沖區掛接點GL_COLOR_ATTACHMENT0_EXT ... GL_COLOR_ATTACHMENTn_EXT、一個深度緩沖區掛接點GL_DEPTH_ATTACHMENT_EXT和一個模板緩沖區掛接點GL_STENCIL_ATTACHMENT_EXT。顏色緩沖區掛接點的個數是因不同廠商和不同型號的顯卡而異的。你可以通過GL_MAX_COLOR_ATTACHMENTS_EXT查詢當前顯卡所支持的顏色緩沖區掛接點的最大個數。FBO提供多個顏色緩沖區掛接點的用意是,允許程序員進行多目標渲染(使用GL_ARB_draw_buffers擴展功能)。
能夠與FBO掛接的對象有兩種,一種是紋理對象(texture object),另一種是渲染緩沖區對象(renderbuffer object)。紋理對象,就是我們平日為模型設置紋理貼圖時使用的對象;而渲染緩沖區對象可以用作不具有紋理格式的緩沖區,如深度緩沖區和模板緩沖區。當然,渲染緩沖區對象也可以用來渲染場景。
前面提到過,窗體系統創建的幀緩沖區也有對應的FBO的實例。但是,這個FBO與其他通過手工創建的FBO相比有許多不同。第一,通過手工創建的FBO不能用於將渲染結果直接顯示到窗口輸出區,通過手工創建的FBO只能用於屏外渲染(Off-screen Rendering);第二,窗體系統生成的FBO在創建的時候就擁有顏色緩沖區,深度緩沖區,模版緩沖區,且創建後就為這些緩沖區分配了空間。而手工創建的FBO需要手動為其添加各個緩沖區,並為其申請空間。窗體系統創建的FBO中的各個緩沖區對象不能與手動創建的FBO的掛接點掛接,反之亦然。
創建、綁定和刪除一個FBO
我們可以使用glGenFramebuffersEXT()來向OpenGL申請一個或者多個閒置的FBO的ID。注意,就算成功地申請到了閒置的ID,OpenGL也不會馬上為其創建實例。只用當調用glBindFramebufferEXT ()綁定FBO的時候OpenGL才會真正的創建一個FBO實例(這和其他glBind*函數極為相似)。在FBO被綁定之後,這個FBO就會被OpenGL當作當前的操作對象,後續的操作都被視為對被綁定的FBO進行的操作。窗體系統創建的FBO的ID默認為0。我們可以通過調用glDeleteFramebuffersEXT()函數來釋放FBO的實例,如果要刪除的FBO實例正在被使用,則OpenGL會自動綁定窗口系統創建的FBO(ID為0)。
渲染緩沖區對象
FBO創建完成後,還不能對其進行什麼實質性的操作。因為,FBO的各個掛接點上還沒有掛接實際的存儲對象。我們需要手動創建這些對象,並將其與既存的FBO對象進行掛接。上文提到過,能夠與FBO掛接的對象有兩種,一種是紋理對象(texture object),另一種是渲染緩沖區對象(renderbuffer object)。讀者可能對紋理對象比較熟悉,因為在為模型進行紋理貼圖的時候,經常要使用這種對象。然而,對於渲染緩沖對象,讀者可能會不太熟悉,因為這是OpenGL擴展中新引入的功能。下文將著重介紹渲染緩沖區對象的內容。
渲染緩沖區對象主要是為了實現屏外渲染(Off-screen Rendering)而設計的。渲染緩沖區對象主要用做FBO的深度緩沖區和模板緩沖區。可以使用glGenRenderbuffersEXT()函數來申請一個或多個閒置的渲染緩沖區對象ID(非負整數)。ID 0被OpenGL所保留。注意,申請了閒置ID之後,OpenGL並沒有創建實際的對象,需要調用glBindRenderbufferEXT()函數來綁定並創建實際的對象。如果綁定ID 0,OpenGL會解除先前設定的渲染緩沖區對象。
上文提到過,渲染緩沖區對象實際上是某種二維數組的抽象。在綁定了一個渲染緩沖區對象之後,需要使用glRenderbufferStorage()函數為其分配二維數組存儲空間。注意,同一個FBO中的各個二維數組空間的行數(或列數)應該相同。
同FBO類似,可以使用glDeleteRenderbuffersEXT()函數來刪除一個渲染緩沖區對象。
掛接
可以使用glFramebufferRenderbufferEXT()函數將渲染緩沖區對象掛接到FBO上;使用glFramebufferTexture2D()。如果掛接的ID為0,則OpenGL將解除先前的綁定。當被綁定紋理對象或渲染緩沖區對象被刪除,則他們會被自動從當前正在使用的FBO上解除掛接。如果紋理對象或渲染緩沖區對象被掛接到多個FBO上,他們被刪除的時候,只會從當前被綁定的FBO上解除掛接,而不會從未被綁定的FBO上解除綁定。
FBO的完整性
在向FBO輸出渲染結果之前,需要測試FBO的完整性。如果FBO不完整,任何渲染操作都會失敗。我們可以使用glCheckFramebufferStatusEXT()函數來測試FBO的完整性(此函數不能在glBegin()和glEnd()函數之間調用)。FBO完整性的判別法則如下:
FBO的使用
當所有上述的准備工作都完成之後,就可以調用glBindFramebufferEXT()來綁定一個FBO。隨後,就可一像操作窗體系統提供的幀緩沖區一樣操作當前綁定的FBO了。日常的3D渲染操作這裡不再贅述。這裡主要強調像素操作的使用。OpenGL提供了glBlitFramebufferEXT()函數進行像素操作。
示例代碼
示例代碼使用FBO實現渲染到紋理(Render to texture)功能。編譯代碼需要鏈接GLEW和SDL兩個庫。
免費下載地址在 http://linux.linuxidc.com/
用戶名與密碼都是www.linuxidc.com
具體下載目錄在 /2014年資料/3月/25日/OpenGL中的FBO對象(附源碼)
下載方法見 http://www.linuxidc.com/Linux/2013-07/87684.htm