我正在WebGL2中的大型4D输入数组上进行一些GPGPU处理。最初,我只是将输入数组展平并将其作为一个统一的int数组传递,在GLSL中使用自定义访问器函数将4D坐标转换为数组索引,如下所示:
const int SIZE = 5; // The largest dimension that works; if I can switch to textures, this will be passed in as a uniform value.
const int SIZE2 = SIZE*SIZE;
const int SIZE3 = SIZE*SIZE2;
const int SIZE4 = SIZE*SIZE3;
uniform int u_map[SIZE4];
int get_cell(vec4 m){
ivec4 i = ivec4(mod(m,float(SIZE)));
return u_map[i.x*SIZE3+i.y*SIZE2+i.z*SIZE+i.w];
}
在JavaScript方面,展平的数据传递如下:
const map_loc = gl.getUniformLocation(program, "u_map");
gl.uniform1iv(map_loc, data);
其中data
是一个包含8位值的Int32Array
(因为这是映射到GLSL整数所需的)。
这有效,但它严重限制了我可以使用的输入大小。如果只有1024个可用于此数据和其他控制输入,则尺寸大小为6会导致使用1296个统一插槽。
所以,我想切换到使用纹理来容纳更大量的数据。所以,我已经将JS代码更新为:
const tex = gl.createTexture();
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.R8, data.length, 1, 0, gl.RED, gl.UNSIGNED_BYTE, data);
其中data
被重新打包为Uint8Array
,应该用于在GLSL中的纹理采样器对象中提供单通道r
值。 GLSL代码更新如下:
uniform sampler2D u_map;
int get_cell(vec4 m){
ivec4 i = ivec4(mod(m,float(SIZE)));
float r = texelFetch(u_map, ivec2(i.x*SIZE3+i.y*SIZE2+i.z*SIZE+i.w, 0), 0).r;
return int(r);
}
它应该从底层缓冲区中获取单个有效通道值,就像我们使用数组时一样。
然而,在做这个替换后,我只是得到了垃圾。从get_cell
返回的值看起来都是1或0 - 并且它们都没有可靠地对应于原始数据缓冲区中实际的1或0值的存在。
我究竟做错了什么?
如果纹理的内部格式是gl.R8
,则纹理在texelFetch
中查找的值在[0.0,1.0]范围内。
如果要在[0,255]范围内下注值,则必须将值乘以255.0:
int get_cell(vec4 m){
ivec4 i = ivec4(mod(m,float(SIZE)));
float r = texelFetch(u_map, ivec2(i.x*SIZE3+i.y*SIZE2+i.z*SIZE+i.w, 0), 0).r;
return int(r * 255.0);
}
请注意,R8
,RGB8
和RGBA8
等格式是无符号规范化定点格式。假设颜色值是[0.0,1.0]范围内的浮点值,其中最小可能值0被转换为0.0并且最大可能值(2 ^ 8-1 == 255)被转换为1.0。
您使用的R8
格式是一种标准化浮点格式,其值为0.0到1.1。如果你想要一个int
格式然后考虑使用R32I
纹理内部格式,提供纹理数据格式gl.RED_INTEGER
,键入gl.INT
并使用Int32Array
,将您的采样器更改为isampler2D
并使用int r = textureFetch(....).r
读取数据。
如果你想要一个整数结果,你还需要为结果创建一个整数纹理,并将其附加到帧缓冲区以呈现整数结果。