WebGL2 texImage2D 16位然后readPixels 16位问题
WebGL2 texImage2D 16-bit then readPixels 16-bit problem
是否可以创建 16 位 2d 纹理图像并使用 readPixels 检索原始 16 位值?
我制作了一个 jsfiddle,我在其中输入并绘制了 16 位像素的绿色渐变。
然后我使用 readPixels 其中 returns 一堆 [0,0,0,65535] 然后我得到的下一个唯一值是 [0,257,0,65535].
我认为这在完整的 16 位中工作正常,直到我更仔细地测试:(是否有可能使用 getPixels 获得准确的原始 16 位数据(未四舍五入为 8 位)?
https://jsfiddle.net/mortac8/b23cnrxt/5/
// image data
var w = 64;
var h = 64;
var size = w * h * 4;
var img = new Uint16Array(size); // need Uint16Array
for (var i = 0; i < img.length; i += 4) {
img[i + 0] = 0; // r
img[i + 1] = i; // g
img[i + 2] = 0; // b
img[i + 3] = 65535; // a
}
// draw
gl.texImage2D(
gl.TEXTURE_2D, // target
0, // mip level
gl.RGBA16UI, // internal format -> gl.RGBA16UI
w, h, // width and height
0, // border
gl.RGBA_INTEGER, //format -> gm.RGBA_INTEGER
gl.UNSIGNED_SHORT, // type -> gl.UNSIGNED_SHORT
img // texture data
);
这给了我绿色值,例如一堆 0,然后一堆 257,然后一堆 514...
let pix = new Uint16Array(w*h*4);
gl.readPixels(0, 0, w, h, gl.RGBA, gl.UNSIGNED_SHORT, pix);
这给了我全 0
let pix = new Uint16Array(w*h*4);
gl.readPixels(0, 0, w, h, gl.RGBA_INTEGER, gl.UNSIGNED_SHORT, pix);
// image data
var w = 64;
var h = 64;
var size = w * h * 4;
var img = new Uint16Array(size); // need Uint16Array
for (var i = 0; i < img.length; i += 4) {
img[i + 0] = 0; // r
img[i + 1] = i; // g
img[i + 2] = 0; // b
img[i + 3] = 65535; // a
}
// program
var canvas = document.getElementById('cv');
var gl = canvas.getContext('webgl2');
var program = gl.createProgram();
//var color_buffer_float_16ui = gl.getExtension('EXT_color_buffer_float'); // add for 16-bit
// texture
var tex = gl.createTexture(); // create empty texture
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texImage2D(
gl.TEXTURE_2D, // target
0, // mip level
gl.RGBA16UI, // internal format -> gl.RGBA16UI
w, h, // width and height
0, // border
gl.RGBA_INTEGER, //format -> gm.RGBA_INTEGER
gl.UNSIGNED_SHORT, // type -> gl.UNSIGNED_SHORT
img // texture data
);
console.log("ORIGINAL IMAGE:");
console.log(img);
// buffer
var buffer = gl.createBuffer();
var bufferData = new Float32Array([
-1, -1,
1, -1,
1, 1,
1, 1,
-1, 1,
-1, -1
]);
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, bufferData, gl.STATIC_DRAW);
// shaders
program.vs = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(program.vs, `#version 300 es
in vec4 vertex; // incoming pixel input?
out vec2 pixelCoordinate; // variable used to pass position to fragment shader
void main(){
gl_Position = vertex; // set pixel output position to incoming position (pass through)
pixelCoordinate = vertex.xy*0.5+0.5; // set coordinate for fragment shader
}
`);
program.fs = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(program.fs, `#version 300 es
precision highp float; // ?
uniform highp usampler2D tex; // ?
in vec2 pixelCoordinate; // receive pixel position from vertex shader
out vec4 fooColor;
void main() {
uvec4 unsignedIntValues = texture(tex, pixelCoordinate);
vec4 floatValues0To65535 = vec4(unsignedIntValues);
vec4 colorValues0To1 = floatValues0To65535 / 65535.0;
fooColor = colorValues0To1;
}
`);
gl.compileShader(program.vs);
checkCompileError(program.vs);
gl.compileShader(program.fs);
checkCompileError(program.fs);
function checkCompileError(s) {
if (!gl.getShaderParameter(s, gl.COMPILE_STATUS)) {
console.error(gl.getShaderInfoLog(s));
}
}
gl.attachShader(program,program.vs);
gl.attachShader(program,program.fs);
gl.deleteShader(program.vs);
gl.deleteShader(program.fs);
// program
gl.bindAttribLocation(program, 0, "vertex");
gl.linkProgram(program);
gl.useProgram(program);
gl.enableVertexAttribArray(0);
gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES, 0, 6); // execute program
let pix = new Uint16Array(size);
gl.readPixels(0, 0, w, h, gl.RGBA, gl.UNSIGNED_SHORT, pix);
console.log("READPIXELS:");
console.log(pix);
<canvas id="cv" width=100 height=100></canvas>
在那个 jsfiddle 中,您正在绘制到默认的帧缓冲区并从中读取。默认的帧缓冲区是 RGBA8,这就是你被截断的原因。
我有点惊讶你的系统允许 gl.readPixels(0, 0, w, h, gl.RGBA, gl.UNSIGNED_SHORT, pix);
!通常它只会是 RGBA+UNSIGNED_BYTE.
如果将 RGBA16UI 纹理附加到帧缓冲区,并直接从中读取像素,则可以取回未截断的原始数据:https://jsfiddle.net/3u2tvqe6/
值得注意的是,WebGL 2 不保证您可以将 RGBA+UNSIGNED_SHORT 与 RGBA16UI 一起使用。对于 unsigned-int-type 纹理,唯一保证的 readPixels 格式是 RGBA+UNSIGNED_INT!这在 GLES 规范的第 191 和 193 页上指定:https://www.khronos.org/registry/OpenGL/specs/es/3.0/es_spec_3.0.pdf
是否可以创建 16 位 2d 纹理图像并使用 readPixels 检索原始 16 位值?
我制作了一个 jsfiddle,我在其中输入并绘制了 16 位像素的绿色渐变。 然后我使用 readPixels 其中 returns 一堆 [0,0,0,65535] 然后我得到的下一个唯一值是 [0,257,0,65535].
我认为这在完整的 16 位中工作正常,直到我更仔细地测试:(是否有可能使用 getPixels 获得准确的原始 16 位数据(未四舍五入为 8 位)?
https://jsfiddle.net/mortac8/b23cnrxt/5/
// image data
var w = 64;
var h = 64;
var size = w * h * 4;
var img = new Uint16Array(size); // need Uint16Array
for (var i = 0; i < img.length; i += 4) {
img[i + 0] = 0; // r
img[i + 1] = i; // g
img[i + 2] = 0; // b
img[i + 3] = 65535; // a
}
// draw
gl.texImage2D(
gl.TEXTURE_2D, // target
0, // mip level
gl.RGBA16UI, // internal format -> gl.RGBA16UI
w, h, // width and height
0, // border
gl.RGBA_INTEGER, //format -> gm.RGBA_INTEGER
gl.UNSIGNED_SHORT, // type -> gl.UNSIGNED_SHORT
img // texture data
);
这给了我绿色值,例如一堆 0,然后一堆 257,然后一堆 514...
let pix = new Uint16Array(w*h*4);
gl.readPixels(0, 0, w, h, gl.RGBA, gl.UNSIGNED_SHORT, pix);
这给了我全 0
let pix = new Uint16Array(w*h*4);
gl.readPixels(0, 0, w, h, gl.RGBA_INTEGER, gl.UNSIGNED_SHORT, pix);
// image data
var w = 64;
var h = 64;
var size = w * h * 4;
var img = new Uint16Array(size); // need Uint16Array
for (var i = 0; i < img.length; i += 4) {
img[i + 0] = 0; // r
img[i + 1] = i; // g
img[i + 2] = 0; // b
img[i + 3] = 65535; // a
}
// program
var canvas = document.getElementById('cv');
var gl = canvas.getContext('webgl2');
var program = gl.createProgram();
//var color_buffer_float_16ui = gl.getExtension('EXT_color_buffer_float'); // add for 16-bit
// texture
var tex = gl.createTexture(); // create empty texture
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texImage2D(
gl.TEXTURE_2D, // target
0, // mip level
gl.RGBA16UI, // internal format -> gl.RGBA16UI
w, h, // width and height
0, // border
gl.RGBA_INTEGER, //format -> gm.RGBA_INTEGER
gl.UNSIGNED_SHORT, // type -> gl.UNSIGNED_SHORT
img // texture data
);
console.log("ORIGINAL IMAGE:");
console.log(img);
// buffer
var buffer = gl.createBuffer();
var bufferData = new Float32Array([
-1, -1,
1, -1,
1, 1,
1, 1,
-1, 1,
-1, -1
]);
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, bufferData, gl.STATIC_DRAW);
// shaders
program.vs = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(program.vs, `#version 300 es
in vec4 vertex; // incoming pixel input?
out vec2 pixelCoordinate; // variable used to pass position to fragment shader
void main(){
gl_Position = vertex; // set pixel output position to incoming position (pass through)
pixelCoordinate = vertex.xy*0.5+0.5; // set coordinate for fragment shader
}
`);
program.fs = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(program.fs, `#version 300 es
precision highp float; // ?
uniform highp usampler2D tex; // ?
in vec2 pixelCoordinate; // receive pixel position from vertex shader
out vec4 fooColor;
void main() {
uvec4 unsignedIntValues = texture(tex, pixelCoordinate);
vec4 floatValues0To65535 = vec4(unsignedIntValues);
vec4 colorValues0To1 = floatValues0To65535 / 65535.0;
fooColor = colorValues0To1;
}
`);
gl.compileShader(program.vs);
checkCompileError(program.vs);
gl.compileShader(program.fs);
checkCompileError(program.fs);
function checkCompileError(s) {
if (!gl.getShaderParameter(s, gl.COMPILE_STATUS)) {
console.error(gl.getShaderInfoLog(s));
}
}
gl.attachShader(program,program.vs);
gl.attachShader(program,program.fs);
gl.deleteShader(program.vs);
gl.deleteShader(program.fs);
// program
gl.bindAttribLocation(program, 0, "vertex");
gl.linkProgram(program);
gl.useProgram(program);
gl.enableVertexAttribArray(0);
gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES, 0, 6); // execute program
let pix = new Uint16Array(size);
gl.readPixels(0, 0, w, h, gl.RGBA, gl.UNSIGNED_SHORT, pix);
console.log("READPIXELS:");
console.log(pix);
<canvas id="cv" width=100 height=100></canvas>
在那个 jsfiddle 中,您正在绘制到默认的帧缓冲区并从中读取。默认的帧缓冲区是 RGBA8,这就是你被截断的原因。
我有点惊讶你的系统允许 gl.readPixels(0, 0, w, h, gl.RGBA, gl.UNSIGNED_SHORT, pix);
!通常它只会是 RGBA+UNSIGNED_BYTE.
如果将 RGBA16UI 纹理附加到帧缓冲区,并直接从中读取像素,则可以取回未截断的原始数据:https://jsfiddle.net/3u2tvqe6/
值得注意的是,WebGL 2 不保证您可以将 RGBA+UNSIGNED_SHORT 与 RGBA16UI 一起使用。对于 unsigned-int-type 纹理,唯一保证的 readPixels 格式是 RGBA+UNSIGNED_INT!这在 GLES 规范的第 191 和 193 页上指定:https://www.khronos.org/registry/OpenGL/specs/es/3.0/es_spec_3.0.pdf