OpenGL超級寶典筆記(n)PBO緩衝區
前略好幾章都沒放上來,以後再補上吧。 之前一直使用gltools的GLBatch來填充資料傳給shader,現在直接用自己的buffer來傳資料了。
不外乎幾步:
- 弄一個buffer指標,其實弄一個buffer陣列也可以了,陣列可以記得個數,指標可不能,容易出事兒
- 就這麼著吧,算是上一步弄了個buffer陣列,那陣列名就是一個指標了,現在把指標傳給OpenGL來分配一個buffer
unsigned int n = 1; GLuint buffers[n]; glGenBuffer(n, buffers);
- 接下來就是針對某一個buffer來bind了,bind的時候要說明用處,比如
上面提及這種型別就是可以拿來放頂點啊,顏色啊,紋理座標啥的。 迄今為止見過的還有GL_TEXTURE_BUFFER, GL_UNIFORM_BUFFERglBindBuffer
- 用完之後不想要了就要刪除,但是要保證buffer沒有被繫結為任何用途。
glDeleteBuffers(1, buffers);
另外,怎麼傳輸資料是個問題。
glBufferData(GL_PIXEL_PACK_BUFFER, dataSize, data, GL_DYNAMIC_COPY);
第一個引數是繫結點(what?),第二個引數是用途。 不知道用啥的時候就用GL_DYNAMIC_DRAW
void glBufferSubData(GLenum target, intptr offset, sizeiptr size, const void data*);
why
為啥使用緩衝區。 在使用緩衝區的時代之前,要向OpenGL傳遞紋理需要通過CPU先把內容複製到ram,再在需要的時候,命令CPU把內容從RAM傳給OpenGL。壞處有二:
RAM速度沒有視訊記憶體速度快用的還是記憶體,並不是視訊記憶體- 同步操作,佔用CPU週期,浪費CPU時間
使用緩衝區之後,這一過程變成了:CPU把內容交給PBO,而PBO是OpenGL管理的(也即是GPU管理),在需要的時候OpenGL直接通過DMA(直接存取)來讀取PBO,不需要經過CPU。這就是非同步過程,不影響CPU週期。
圖如下:
pack
關於pack和unpack。 在GL_PIXEL_PACK_BUFFER模式下時,使用glReadPixels可以從幀快取讀取畫素寫到pbo。 在GL_PIXEL_UNPACK_BUFFER模式下,使用glDrawPixels把畫素從pbo寫入幀快取。
修改記憶體
使用void* glMapBuffer(target, access)
可以獲得地址,如果找不到會返回nullptr。
target可能的值有:
- GL_PIXEL_PACK_BUFFER
- GL_PIXEL_UNPACK_BUFFER
access可能的值:
- GL_READ_ONLY
- GL_WRITE_ONLY
- GL_READ_WRITE
如果GPU正在操作這個buffer,glMapBuffer就會一直等待直到GPU同步完buffer資訊。為了避免這個無用的等待,最好就是使用空指標來呼叫glBufferData,這會讓這塊空記憶體上啥都沒有,然後馬上呼叫glMapBuffer。這時,OpenGL會丟棄老的buffer,然後重新分配一塊新的記憶體給它。
注意: 在使用完pbo之後,必須呼叫glUnmapBuffer(),如果成功了,會返回一個GL_TRUE。
分配
在分配的時候沒啥要注意的,如果是為紋理分配,那分配的空間就是長*寬*畫素大小。
READ FRAMEBUFFER
從當前幀獲取畫素資料,使用glReadPixels。
不使用pbo:
void *data = (void*)malloc(pixelDataSize);
glReadBuffer(GL_BACK_LEFT);
glReadPixels(0, 0, GetWidth(), GetHeight(), GL_RGB, GL_UNSIGNED_BTYE, pixelData);
使用pbo:
glReadBuffer(GL_BACK_LEFT);
glBindBuffer(GL_PIXEL_PACK_BUFFER, pixBuffObjs[0]);
glReadPixels(0, 0, GetWidth(), GetHeight(), GL_RGB, GL_UNSIGNED_BYTE, NULL);
這就把資料讀入了pbo。
使用pbo來做模糊效果。思路是儲存前面五幀來和當前幀做混合。 說一下主要套路:
setup階段
- 從硬碟讀出bmp圖片紋理
- 申請6個texture,全都用bmp紋理初始化
- 申請一個buffer並初始化,初始化的內容可以是空指標,但大小一定要指定
// 這個時候啥功能都可以,pack或者unpack都行 glBindBuffer(GL_PIXEL_PACK_BUFFER, g_bufferObjs[0]); glBufferData(GL_PIXEL_PACK_BUFFER, size, dataPtr, GL_DYNAMIC_DRAW); // 解綁 glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
render階段
- 從螢幕把畫素打包到buffer
glBindBuffer(GL_PIXEL_PACK_BUFFER, g_bufferObjs[0]); glReadPixels(0, 0, screenWidth, screenHeight, GL_RGB, GL_UNSIGNED_BYTE, nullptr); glBindBuffer(GL_PIXEL_PACK_BUFFER, *g_bufferObjs);
- 這個時候buffer裡面已經是慢慢的螢幕資訊啦!接下來要從buffer裡面把資訊寫入到texture。先把buffer先切到解包模式,才能寫入到texture或者螢幕,然後切換到目標texture,寫入,最後解綁buffer。
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, *g_bufferObjs); glActiveTexture(GL_TEXTURE0 + GetBlurTarget0()); // 當資料是nullptr的時候,就讀緩衝區~ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, screenWidth, screenHeight, 0, GL_UNSIGNED_BYTE, NULL);
- 最後一步是改寫shader。片段shader要去讀0~5號sampler,然後把結果疊加起來!