天天看點

OpenGL圖像(image)

在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

繼續閱讀