WebGL2でIntegerテクスチャに書き込みと読み込みを行う方法です。
サンプルをGithubに置いておいたので、この記事では要点だけを解説します。
aadebdeb/Sample_WebGL2_IntegerTexture: Sample of writing & reading integer texture in WebGL2
まずはIntegerテクスチャの作成を行います。
texImage2Dでフォーマットを指定します。このメソッドの引数であるinternalformat
とformat
, type
は適切な組み合わせを選択する必要があります。今回はinternalformatをRGBA32I
、formatをRGBA_INTEGER
, typeをINT
にして4つの値を格納できるようにします。組み合わせはこの記事が参考になります。
格納する値が1つでいい場合はinternalformat
とformat
をそれぞれR32I
とRED_INTEGER
に、2つの場合はRG32I
とRG_INTEGER
を選択するといいでしょう。また小さい値しか格納しないのであればRGBA8I
やRGB16I
も使用できます。
また、Integerテクスチャを使用する場合はフィルタリングの方法をNEAREST
にするよう必要があります。
function createTexture(gl, sizeX, sizeY) {
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA32I, sizeX, sizeY, 0, gl.RGBA_INTEGER, gl.INT, null);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.bindTexture(gl.TEXTURE_2D, null);
return texture;
}
次に作成したIntegerテクスチャに値を書き込む方法です。
通常のオフスクリーンレンダリングと同じように作成したテクスチャを紐づけたフレームバッファに以下のようなフラグメントシェーダーでレンダリングを行います。out
修飾子がついた変数の型がテクスチャの型と同じになるようにivec4
にしています。
#version 300 es
precision highp float;
out ivec4 value;
void main(void) {
ivec2 coord = ivec2(gl_FragCoord.xy);
value = ivec4((coord.x + coord.y + ivec3(0, 2, 4)) % 8, 0);
}
最後にIntegerテクスチャの読み込みです。Integerテクスチャにはsampler2D
ではなくisampler2D
を利用します。またprecision highp isampler2D;
のようにisampler2D
の精度を指定する必要もあります。
#version 300 es
precision highp float;
precision highp isampler2D;
out vec4 color;
uniform isampler2D tex;
uniform vec2 resolution;
void main(void) {
vec2 uv = gl_FragCoord.xy / resolution;
ivec3 v = texture(tex, uv).rgb;
color = vec4(vec3(v) / 8.0, 1.0);
}
符号なし整数のテクスチャへの書き込み&読み込みも同様に行います。
テクスチャ作成時にはinternalformat
にはRGBA32UI
のようにUが付き、type
がUNSIGNED_INT
になります。
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA32UI, sizeX, sizeY, 0, gl.RGBA_INTEGER, gl.UNSIGNED_INT, null);
テクスチャ書き込み時には出力先の変数の型がuvec4
になります。
#version 300 es
precision highp float;
out uvec4 value;
void main(void) {
ivec2 coord = ivec2(gl_FragCoord.xy);
value = uvec4((coord.x + coord.y + ivec3(0, 2, 4)) % 8, 0);
}
テクスチャ読み込み時にはisampler2D
ではなくusampler2D
を利用します。
#version 300 es
precision highp float;
precision highp usampler2D;
out vec4 color;
uniform usampler2D tex;
uniform vec2 resolution;
void main(void) {
vec2 uv = gl_FragCoord.xy / resolution;
uvec3 v = texture(tex, uv).rgb;
color = vec4(vec3(v) / 8.0, 1.0);
}
最後に注意事項について書いておきます。
RGB32I
のように3変数の値を格納するテクスチャへの書き込みはできません。書き込もうとすると以下のような警告が出ます。
[.WebGL-00000219F401A920]GL ERROR :GL_INVALID_FRAMEBUFFER_OPERATION : glDrawElements: framebuffer incomplete
また、Integerテクスチャの読み込み時にisampler2D
やsampler2D
ではなくsampler2D
を使うと、エラーにはなりませんがサンプルした値が0になります。