I'm using GLSL shaders with 3D texture data in WebGL2 code via TypeScript. My texture data contains single-channel samples, with different data sources using samples with different bit widths (u8, u16, u32, f32). Unfortunately, I cannot get texture formats other than R8
to work (64 bit Chrome v104+ on Windows 10).
I see no GLSL shader/program compilation errors, and no WebGL runtime errors on the console or via return values from WebGL calls.
When I upload texture data from a Uint8Array
as R8
format, everything works fine. However, when I switch from R8
to R8UI
format (ostensibly identical data, but usampler
in the shader vs sampler
to return raw unsigned values rather than normalized float
s) I get ... nothing.
All the values returned by the sampler are zero, everywhere in the 3D texture data
I checked this by modifying the shader to simply output a gray pixel wherever the sampled texture data is non-zero - no gray pixels are created.
I also tried R16UI
and R32F
texture formats (source data passed via e.g., Uint8Array
or Float32Array
); these formats also result in textures full of zero values when the shader runs. It seems that only R8
produces anything other than textures full of 0
.
I could perhaps try to smear 16-bit values across 2 x 8-bit values via some sort of RG8
internal texture format, but the "correct" data types are apparently available by default in WebGL2 - I just can't seem to get them to work.
Any ideas?
Code snippets follow, with no error checking for concision:
R8
example)// R8 - this seems to work
const data = new Uint8Array(W*H*N)
internal_format = sys.gl.R8
< ... setup data array ... >
setDataTexture(W,H,N, data, internal_format)
R8UI
example)// R8UI - this doesn't seem to work, despite being ostensibly
// identical to the R8 source data
const data = new Uint8Array(W*H*N)
internal_format = sys.gl.R8UI
< ... setup data array ... >
setDataTexture(W,H,N, data, internal_format)
setDataTexture(X: number, Y: number, Z: number, data: any, internal_format: GLenum) {
const gl = this.gl
const params: Record<GLenum, any> = {}
params[gl.R8] = ["R8", gl.RED, gl.UNSIGNED_BYTE]
params[gl.R8UI] = ["R8UI", gl.RED_INTEGER, gl.UNSIGNED_BYTE]
params[gl.R16UI] = ["R16UI", gl.RED_INTEGER, gl.UNSIGNED_SHORT]
params[gl.R16I] = ["R16I", gl.RED_INTEGER, gl.SHORT]
params[gl.R32F] = ["R32F", gl.RED, gl.FLOAT]
gl.activeTexture(gl.TEXTURE0) // bind data to texture 0
if (this.dataTex !== null) {
gl.deleteTexture(this.dataTex)
this.dataTex = null
}
if (!params[internal_format]) {
console.log(`Unknown internal format ${internal_format}`)
return
}
const [str, fmt, typ] = params[internal_format]
this.dataTex = gl.createTexture()
gl.bindTexture(gl.TEXTURE_3D, this.dataTex)
// UNPACK_ALIGNMENT : https://stackoverflow.com/questions/51582282/error-when-creating-textures-in-webgl-with-the-rgb-format
gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1)
gl.texStorage3D(gl.TEXTURE_3D, 1, internal_format, X,Y,Z)
// LINEAR filtering doesn't work for some data types, default to NEAREST for testing
gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_WRAP_R, gl.CLAMP_TO_EDGE)
gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
gl.texSubImage3D(gl.TEXTURE_3D, 0, 0,0,0, X,Y,Z, fmt, typ, data)
}
R8
)#version 300 es
precision highp int;
precision highp float;
uniform highp sampler3D volume;
< ... etc, then loop calculating position "pos" ... >
// Assume only using red channel in texture data
float val = texture(volume, pos).r;
// ... now do something with "val"
R8UI
)#version 300 es
precision highp int;
precision highp float;
uniform highp usampler3D volume;
< ... etc, then loop calculating position "pos" ... >
// Assume only using red channel in texture data
uint val_ = texture(volume, pos).r;
if (val_ > 0u) {
// write gray pixel data
}
Your texture is incomplete. R8UI
is a format that is not texture filterable because integer values cannot be interpolated. Therefore, both the minification filter and the magnification filter must be "nearest" value filters. You have set the minification filter
gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
however you have forgotten the magnification filter
gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)