天天看點

OpenGL學習之緩沖區對象

緩沖區對象是一個強大的概念,它允許應用程式迅速友善地将資料從一個渲染管線移動到另一個渲染管線,以及從一個對象綁定到另一個對象。

緩沖區:緩沖區有很多不同的用途,它們能夠儲存頂點資料、像素資料、紋理資料、着色器處理的輸入,或者不同階段着色器階段的輸出。

緩沖區儲存在GPU記憶體中,它們提供高速和高效的通路。在OpenGL有緩沖區對象之前,應用程式隻有有限的選擇可以在GPU中存儲資料。另外,在GPU中更新資料常常需要重新加載整個對象。在系統記憶體和GPU記憶體之前來回移動資料可能是一個緩慢的過程。

建立緩沖區

我們可以使用glGenBuffers來為我們所需的任何數量的新緩沖區建立名稱。實際緩沖區對象将在第一次使用時建立。

Gluint pixBufferObj[1];
glGenBuffers(1,pixBufferObjs);
           

當我們有了新的緩沖區名稱,就可以對這些名稱進行綁定來使用緩沖區。在OpenGL中有許多不同的綁定點,每個綁定點都允許我們為了不同的目的而使用某個緩沖區。我們可以将每個結合點或綁定點看作一個在同一時刻隻能結合一個對象的槽。

緩沖區對象綁定點

名稱 描述
GL_ARRAY_BUFFER 數組緩沖區存儲顔色、位置、紋理等頂點屬性,或者其他自定義屬性
GL_COPY_READ_BUFFER 緩沖區用作通過glCopyBufferSubData進行複制的資料源
GL_COPY_WRITE_BUFFER 緩沖區用作通過glCopyBufferSubData進行複制的目标
GL_ELEMENT_ARRAY_BUFFER 索引緩沖區用于儲存glDrawElements、glDrawRangeElements和glDrawElementsInstanced的索引
GL_PIXEL_PACK_BUFFER glReadPixels之類像素包裝操作的目标緩沖區
GL_PIXEL_UNPACK_BUFFER glTexImage1D、glTexImage2D、glTexImage3D、glTexSubImage1D、glTexSubImage2D和glTexSubImage3D之類紋理更新函數的緩沖區
GL_TEXTURE_BUFFER 着色器可以通過紋理單元拾取來通路的緩沖區
GL_TRANSFORM_FEEDBACK_BUFFER 變換回報頂點着色器(transform feedback vertex shader)寫入的緩沖區
GL_UNIFORM_BUFFER 着色器能夠通路的UNiform值

要綁定一個緩沖區以備用,我們可以以這個緩沖區名稱為參數,以上表列出的緩沖區為目标來調用glBindBuffer。我們将新的緩沖區綁定到像素包裝緩沖區綁定點之後,就可以使用glReadPixels将像素資料複制到緩沖區中了。

glBindBuffer(GL_PIXEL_PACK_BUFFER,pixBuffer[0]);
           

要從一個綁定中對一個緩沖解除綁定,可以再次調用以0為緩沖區名稱、目标與上述調用相同的glBuffer。我們還可以隻是将另外一個合法的緩沖區綁定到同一個目标上。

當我們使用完一個緩沖區之後,這個緩沖區需要進行清除,在進行删除之前,我們要確定緩沖區沒有被綁定到任何綁定點。

glDeleteBuffer(1,pixBufferObjs);
           

填充緩沖區

使用glBufferData函數來簡單地将資料直接上傳到任何類型的緩沖區中。

glBindBuffer(GL_PIXEL_PACK_BUFFER,pixBufferObjs[0]);
glBufferData(GL_PIXEL_PACK_BUFFER,0);
           

在調用glBufferData之前,我們必須将要使用的緩沖區進行綁定。對glBufferData使用的目标與我們為第一個參數綁定緩沖區時使用的目标相同。第二個參數是我們将要上傳的資料大小,以位元組(byte)為機關,而第三個參數則是将要被上傳的資料本身。

如果我們想要配置設定一個特定大小的緩沖區卻不需要立即對它進行填充,那麼這個指針也可能是NULL。glBufferData的第4個參數用來告訴OpenGL我們打算如何使用緩沖區。

緩沖區對象使用方式

緩沖區使用方式 描述
GL_STREAM_DRAW 緩沖區的内容将由應用程式進行一次設定,并且經常用于繪制
GL_STREAM_READ 緩沖區的内容将作為一條OpenGL指令的輸出來進行一次設定,并且經常用于繪制
GL_STREAM_COPY 緩沖區的内容将作為一條OpenGL指令的輸出來進行一次設定,并且不經常用于繪制或複制到其他圖像
GL_STATIC_DRAW 緩沖區的内容将有應用程式進行一次設定,并且經常用于繪制或複制到其他圖像
GL_STATIC_READ 緩沖區的内容将作為一條OpenGL指令的輸出來進行一次設定,并且由應用程式進行多次查詢
GL_STATIC_COPY 緩沖區的内容将作為一條OpenGL指令的輸出來進行一次設定,并且經常用于繪制或複制到其他圖像
GL_DYNAMIC_DRAW 緩沖區的内容将會經常由應用程式進行更新,并且經常用于繪制或複制到其他圖像
GL_DYNAMIC_READ 緩沖區的内容将作為OpenGL指令經常進行更新,并且由應用程式進行多次查詢
GL_DYNAMIC_COPY 緩沖區的内容将作為OpenGL指令的輸出來進行更新,并且經常用于繪制或複制到其他圖像

在我們不确定緩沖區的用途時,對于通常的緩沖區使用方式或條件來說,使用GL_DYNAMIC_DRAW是一個比較安全的值。我們總是可以再次調用glBufferData對緩沖區重新進行填充,還可以改變使用方式的提示。但是如果我們真的調用了lgBufferData,那麼緩沖區中原來的所有資料都将被删除。

可以使用glBufferSubData對已經存在的緩沖區中的一部分進行更新,而不會導緻緩沖區其他部分的内容變為無效。

void glBufferSubData(GLenum target,intptr offset,sizeiptr,const void *data);
           

glBufferSubData的大多愛護參數和glBufferData的相應參數相同。新的offset參數允許我們從除開頭部分以外的其他位置開始更新。我們也不能改變緩沖區的使用方式,因為記憶體已經被配置設定了。

像素緩沖區對象(PBO)

在存儲像素/紋理單元方面,像素緩沖區對象與紋理緩沖區對象非常相似,并且它們也都存儲在GPU記憶體中。可以使用與其他緩沖區對象類型一樣的方法通路和填充像素緩沖區對象。實際上,隻有在綁定到一個PBO緩沖區綁定點時,一個緩沖區才真正成為一個像素緩沖區。

第一個像素緩沖區對象綁定點時GL_PIXEL_PACK_BUFFER。當一個像素緩沖區對象被綁定到這個目标上是,任何讀取像素的OpenGL操作都會從像素緩沖區對象中獲得它們的資料,這些擦偶哦在包括glReadPixels、glGetTexImage和glGetCompressedTexImage。通常這些擦歐總會從一個幀緩沖區或紋理中抽取資料,并将它們都會用戶端記憶體中。當一個像素緩沖區對象被綁定到包裝緩沖區時,像素資料在GPU記憶體中的像素緩沖區對象中的任務就結束了,而不會下載下傳到用戶端。

第二個PBO綁定點時GL_PIXEL_UNPACK_BUFFER。當一個像素緩沖區對象被綁定到這個目标時。任何繪制像素的OpenGL操作都會向一個綁定的像素緩沖區對象中放入它們的資料。

對初學者來說,任何從PBO中讀取或寫入PBO中的調用或任何緩沖區對象都用管線進行處理。這就意味着GPU不需要完成所有其他工作,之喲啊對資料複制進行初始化,等待複制完成,然後繼續運作就可以了。

像素緩沖區對象是一個很好的容器,可以暫時存儲GPU本地像素資料,但是需要注意,在使用它們之前需要為它們配置設定存儲空間。和其他所有緩沖區對象一樣,要調用glBufferData為這個緩沖區配置設定記憶體空間并用資料填充。但是沒必要提供資料,為資料指針傳遞NULL可以簡單地配置設定記憶體而不進行填充,如果我們試圖填充存儲空間之前不對它進行配置設定,那麼OpenGL将抛出一個錯誤。

像素緩沖區經常用來存儲來自一個渲染目标的2D圖像、紋理或其他資料源。但是緩沖區對象本身是一維的,它們本質上沒有寬度或者高度。在為2D圖像配置設定存儲空間時,我們可以隻是将寬度與高度相乘,再與像素大小相乘。對于存儲像素資料沒有其他需要補充的;,但是緩沖區可以比戈丁的資料組所需的大小更大。

當我們打算為多種資料大小使相同的PBO,最好馬上關閉對資料大小上限的設定,而不是頻繁地對它進行重新設定。

紋理緩沖區對象

一個紋理包含兩個主要組成成分:紋理采樣狀态和包含紋理值的資料緩沖區。紋理緩沖區也稱為texBO或TBO,允許我們完成一些傳統紋理不能完成的工作。首先,紋理緩沖區能夠直接填充來自其他渲染結果(例如變換回報、像素讀取操作或頂點資料)的資料。這樣就節省了不少時間,因為應用程式能夠直接從着色器以前的渲染調用中擷取像素資料。texBO的另一個特性是寬松的大小限制。紋理緩沖區與傳統的一維紋理相似,但要更大。TexBO向着色器提供了大量對多種不同風格和類型的資料通路,允許着色器以通常是預留給CPU的方式進行操作。紋理緩沖區能夠用來提供對片段着色器和頂點着色器中的頂點數組的通路。這在着色需要關于臨近幾何圖形的資訊以作出運作時決策和計算的情況下可能會非常有用。但是為了做到這一點,我們通常需要将texBO大小作為一個統一值傳遞到着色器中。

紋理緩沖區是作為普通的緩沖區來建立的,當它被綁定到一個紋理或者GL_TEXTURE_BUFFER綁定點時才會成為真正的紋理緩沖區。

glBindBuffer(gl_texture_buffer,texBo[0]);
glBufferData(GL_TEXTURE_BUFFER,xizeof(float)*count,fileData,GL_STATIC_DRAW);
           

但是texBO必須綁定到一個紋理單元上才能真正變得有用。要将一個texBO綁定到一個紋理上,可以調用glBuffer,但首先要確定要使用的紋理已經進行了綁定。

glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_BUFFER,texBOTexture);
glTexBuffer(GL_TEXTURE_BUFFER,GL_R32F,texBO[0]);
           

紋理緩沖區對象操作與普通紋理看似很像,但還有一些重要的不同點。紋理緩沖區不能在着色器中用普通的采樣器-也就是sampler1D和sampler2D進行通路。取而代之的是,我們必須用一個新的采樣器-samplerBuffer。由于采樣器類型不同,用來從紋理緩沖區中擷取值的采樣函數也不同。

幀緩沖對象

一個OpenGL視窗的表面長期以來一直被稱作“幀緩沖區”。但是現在OPenGL将繪制緩存區到一個對象所需要的狀态進行了封裝,稱為幀緩存區對象(FBO)。預設的幀緩沖區對象是與建立的OpenGL視窗相關聯的,并且在一個新的環境被綁定時自動進行綁定。可以建立多個幀緩沖區對象,也叫做FBO,并且直接渲染一個FBO而不是視窗。使用這種離屏渲染,應用程式就可以執行許多不同種類的渲染算法了,例如陰影貼圖、應用輻射着色,反射、後期處理和許多其他特效。另外,幀緩沖區對象并不受視窗大小的限制,它可以包含多個顔色緩沖區。甚至可以将紋理綁定到一個FBO上,這就意味着可以直接渲染到一個紋理中。

幀緩沖區根本不是緩沖區,并不存在與一個幀緩沖區對象相關聯的真正記憶體存儲空間。相反,幀緩沖區對象是一種容器,它可以儲存其他确實有記憶體存儲并且可以進行渲染的對象,例如紋理或渲染緩沖區。采用這種方式,幀緩沖區對象能夠在儲存OpenGL管線的輸出時将需要的狀态和表面綁定到一起。

使用FBO

先添加圖像,才能渲染到一個FBO。一旦一個FBO被建立、設定和綁定,大多數OpenGL操作就将像是在渲染到一個視窗一樣執行,但是輸出結果将存儲在綁定到FBO的圖像中。

要先生成FBO緩沖區名稱(可以同時生成任意數量的名稱),才建立FBO。再綁定一個新的FBO來修改和使用它

GLuint fboName;
glGenFramebuffers(1,&fboName);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER,fboName);
           

同一時間隻有一個FBO可以綁定用來進行繪制,并且同一時間隻有一個FBO可以綁定來進行讀取。在綁定一個幀緩沖區時,glBindFramebuffer的第一個參數既可以是GL_DRAW_FRAMEBUFFER,也可以是GL_READ_FRAMEBUFFER。這就意味着我們可以使用一個幀緩沖區進行讀取,而使用另一個不同幀緩沖區進行繪制。

使用完FBO或者在退出前進行清除時,要删除FBO

glDeleteFramebuffer(1,&fboName);
           

繼續閱讀