普通Texture無法在一個pass中完成讀和寫,且無法操作一個給定位置的數據,所以只能使用ImageTexture,使用時遇到了兩個問題:
1、快速清空
ImageTexture 使用的是一張普通的 texture 紋理,texture 被包裝為ImageTexture,故可以直接將texture清空即可。
texture的清空方法:
a、傳值給texture(CPU-->GPU)、
void Transfer2Texture(float* data) { glBindTexture(GL_TEXTURE_2D, this->textureID); if (channels == 1) glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_R, GL_FLOAT, data); if (channels == 2) glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RG, GL_FLOAT, data); if (channels == 3) glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGB, GL_FLOAT, data); if (channels == 4) glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_FLOAT, data); glBindTexture(GL_TEXTURE_2D, 0); }
b、綁定到FBO
初始化時,調用glClearColor()
、glClearDepth()
及glClearStencil()
分別設置清空后顏色緩存、深度緩存和模板緩存中的默認值。
在每一幀渲染前,調用glClear()
並傳入GL_COLOR_BUFFER_BIT
、GL_DEPTH_BUFFER_BIT
、GL_STENCIL_BUFFER_BIT
或它們的位組合進行對應緩存的清空操作。
或者:在OpenGL3.0中支持了 void glClearBufferfv(GLenum buffer, GLint drawbuffer, const GLfloat* value)。
buffer
可以傳入GL_COLOR
、GL_DEPTH
或GL_STENCIL
,以指明我們要清空哪種緩存;
value
指明了清空后的默認值;
drawbuffer
用於多輸出緩存的情況。
以上方法這里都不適用:a方法需要CPU-->GPU傳輸數據很慢;b方法,texture已經被綁定封裝給ImageTexture,可能不能再綁定到FBO
后來查詢到可以直接對ImageTexture清空數據!
c、直接清空ImageTexture
OpenGL需要版本4.4以上
void WKS::ImageTexture::InitData() { if (this->channels == 1) glClearTexImage(this->textureID, 0, GL_R, GL_FLOAT, NULL); if (this->channels == 2) glClearTexImage(this->textureID, 0, GL_RG, GL_FLOAT, NULL); if (this->channels == 3) glClearTexImage(this->textureID, 0, GL_RGB, GL_FLOAT, NULL); if (this->channels == 4) glClearTexImage(this->textureID, 0, GL_RGBA, GL_FLOAT, NULL); }
2、讀寫錯誤
我需要計算場景的一張occlusion mask,一個像素對應多個物體(大於等於2個物體)則置 1, 否則置為 0
故需要使用一張ImageTexture,當出現兩個不同的物體ID則發現重疊了,故需要一個pass中的讀和寫同時存的操作。打印這張mask后發現,遮擋區域(1的位置)基本符合,但是會出現一些零星的 0 值,而且相機不移動時,mask也會變動(零星的0值會隨機出現在其它位置)。我感覺是出現並行讀寫錯誤,但是OpenGL渲染場景是一個一個mesh繪制的,每渲染一個mesh是需要CPU向GPU發送一個Draw的命令的,故mesh之間應該本身就是有順序關系的,不會同時執行出現錯誤。但我后來還是嘗試着在每個mesh draw后面加了一個glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT) ——針對image 讀寫的內存屏障,結果問題解決了,太意外了!由此我猜想mesh Draw之間不是完全順序執行的,會同時執行,導致出現並行讀寫錯誤。