在OpenGL中,图像(image)类似于一个矩阵,代表一块内存,我们在着色器中可以对它进行读写操作。它有点像单一层级的纹理,但与纹理不同,它不支持滤波、深度比较等采样操作。
其核心函数是
void glBindImageTexture(
GLuint imageUnit, //图像单元
GLuint textureID, //纹理ID
Gint level, //纹理层级
GLboolean layered, //对于纹理数组,是绑定某一层还是单一的一层,若是整个数组,则不考虑后面的layer
GLint layer, //纹理数组的某一层
GLenum access, //GL_READ_ONLY/GL_WRITE_ONLY/GL_READ_WRITE
GLenum format //图像元素的格式//要与shader中的uniform变量匹配//对于内存中的格式,只要大小相同即可
);
注意:
1.内部格式不一定要和外部格式匹配
2.内部格式一定要和Shader中的uniform变量匹配
3.外部格式一定要和读到内存中的纹理数据格式匹配
Shader中选择 image1D、image2D、image3D。。。
GLuint tex;
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex);
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA32F, 512, 512);
glBindTexture(GL_TEXTURE_2D, 0);
glBindImageTexture(0, tex, 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA32F);
Shader中选择 imageBuffer
GLuint tex, buf;
glGenBuffers(1,&buf);
glBindBuffer(GL_TEXTURE_BUFFER, buf);
glBufferData(GL_TEXTURE_BUFFER, 4096, NULL, GL_DYNAMIC_COPY);
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_BUFFER, tex);
glTexBuffer(GL_TEXTURE_BUFFER, GL_R32F, buf);
glBindTexture(GL_TEXTURE_2D, 0);
glBindImageTexture(0, tex, 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA32F);
类似于texture绑定shader中的采样器,首相用glUniform1i()设置uniform对应的图像单元
也可以直接在着色其中指定uniform对应的图像单元
layout (binding = 0, rgba32f) uniform imageBuffer colors;
layout (binding = 1, rgba32f) uniform image2D output_buffer;
使用imageLoad和imageStore操作image
void main(void)
{
vec4 col = imageLoad(colors, gl_PrimitiveID & 255);
imageStore(output_buffer, ivec2(gl_FragCoord.xy) - ivec2(200, 0), vec4(100.0,100.0,100.0,100.0));
imageStore(output_buffer, ivec2(gl_FragCoord.xy) + ivec2(200, 0), col);
}
《OpenGL编程指南(第八版)》p428
注意:图像的存储次数是没有限制的,而帧缓存对象关联的附件数量受到严格限制
由于各类OpenGL对原子操作的支持程度不同,原子操作可能会带来非常严重的性能影响
#version 420 core
uniform (r32ui) uimageBuffer counter_buffer;
uniform sampler2D my_texture;
in vec2 tex_coord;
layout (location=0) out vec4 fragment_color;
void main (void)
{
vec4 texel_color = texture(my_texture, tex_coord);
if (texel_color.r>texel_color.g)
imageAtomicAdd(countre_buffer, 0, 1);
else
imageAtomicAdd(counter_buffer, 1, 1);
fragment_color = texel_color;
}
对于一些很常见的操作,如加,减法,GLSL有专门的工具来完成这一需求。原子计数器就是专用于计数的一种特殊对象。
GLuint buffer;
GLuint *counters;
glGenBuffers(1, &buffer);
glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, buffer);
glBufferData(GL_ATOMIC_COUNTER_BUFFER, 2*sizeof(GLuint), NULL, GL_DYNAMIC_COPY);
counters = (GLuint*)glMapBuffer(GL_ATOMIC_COUNTER_BUFFER, GL_MAP_WRITE_ONLY);
counters[0]=0;
counters[1]=0;
glUmmapBuffer(GL_ATOMIC_COUNTER_BUFFER);
glBindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 0, buffer);
改进shader
#version 420 core
layout (binding = 0, offset = 0) uniform atomic_uint red_texels;
layout (binding = 0, offset = 4) uniform atomic_uint green_texels;
uniform sampler2D my_texture;
in vec2 tex_coord;
layout (location=0) out vec4 fragment_color;
void main (void)
{
vec4 texel_color = texture(my_texture, tex_coord);
if (texel_color.r>texel_color.g)
atomicCounterIncrement(red_texels);
else
atomicCounterIncrement(green_texels);
fragment_color = texel_color;
}