普通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之间不是完全顺序执行的,会同时执行,导致出现并行读写错误。