1. 程式人生 > >OpenGL超級寶典筆記(n)PBO緩衝區

OpenGL超級寶典筆記(n)PBO緩衝區

前略好幾章都沒放上來,以後再補上吧。 之前一直使用gltools的GLBatch來填充資料傳給shader,現在直接用自己的buffer來傳資料了。

不外乎幾步:

  1. 弄一個buffer指標,其實弄一個buffer陣列也可以了,陣列可以記得個數,指標可不能,容易出事兒
  2. 就這麼著吧,算是上一步弄了個buffer陣列,那陣列名就是一個指標了,現在把指標傳給OpenGL來分配一個buffer
    unsigned int n = 1;
    GLuint buffers[n];
    glGenBuffer(n, buffers);
    
  3. 接下來就是針對某一個buffer來bind了,bind的時候要說明用處,比如
    glBindBuffer
    (GL_ARRAY_BUFFER, buffers[0]); // 當然想寫成*buffers也可以
    上面提及這種型別就是可以拿來放頂點啊,顏色啊,紋理座標啥的。 迄今為止見過的還有GL_TEXTURE_BUFFER, GL_UNIFORM_BUFFER
  4. 用完之後不想要了就要刪除,但是要保證buffer沒有被繫結為任何用途。
    glDeleteBuffers(1, buffers);
    

另外,怎麼傳輸資料是個問題。

glBufferData(GL_PIXEL_PACK_BUFFER, dataSize, data, GL_DYNAMIC_COPY);

第一個引數是繫結點(what?),第二個引數是用途。 不知道用啥的時候就用GL_DYNAMIC_DRAW

。 使用glBufferData之後原來緩衝區的所有東西會被刪除,重新進行填充。 如只想更新部分資料而不清空舊有,可以使用

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階段
  1. 從硬碟讀出bmp圖片紋理
  2. 申請6個texture,全都用bmp紋理初始化
  3. 申請一個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階段
  1. 從螢幕把畫素打包到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);
    
  2. 這個時候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);
    
  3. 最後一步是改寫shader。片段shader要去讀0~5號sampler,然後把結果疊加起來!