当前位置: 首页 > 工具软件 > image2D > 使用案例 >

OpenGL图像(image)

茅高卓
2023-12-01

在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.外部格式一定要和读到内存中的纹理数据格式匹配

1.用纹理texture作为image的存储空间

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);

2.用缓存buffer作为image的存储空间

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);

3.在Shader中操作图像

类似于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

注意:图像的存储次数是没有限制的,而帧缓存对象关联的附件数量受到严格限制

4.原子操作

由于各类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;
}

5.原子计数器

对于一些很常见的操作,如加,减法,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;
}

6.例子:OIT

 类似资料: