【问题标题】:WebGL2 texImage2D 16-bit then readPixels 16-bit problemWebGL2 texImage2D 16-bit then readPixels 16-bit问题
【发布时间】:2020-12-26 00:36:45
【问题描述】:

是否可以创建 16 位 2d 纹理图像并使用 readPixels 检索原始 16 位值?

我制作了一个 jsfiddle,在其中输入并绘制 16 位像素的绿色渐变。 然后我使用 readPixels 返回一堆 [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);
&lt;canvas id="cv" width=100 height=100&gt;&lt;/canvas&gt;

【问题讨论】:

  • 如果您将代码放在snippet 中,我会更加有用。另外,你有没有费心检查 JavaScript 控制台?您发布的代码会出现 WebGL 错误。
  • 我添加了一个sn-p。谢谢你的提示。我仍然不熟悉所有的 stackoverflow 功能。我希望默认帧缓冲区可以返回 16 位值。我正在开发的主要应用程序是根据我的一些有缺陷的假设设计的。
  • 为了回应您下面的 cmets,使用纹理绘图已覆盖 hereherehere 都涵盖了带有平移和缩放的 2D 绘图。您需要通过usampler2D 传递您的 16 位纹理,将过滤设置为 NEAREST,因为无法过滤整数纹理,然后进行一些数学运算以使 16 位无符号整数值从 0 变为1 在输出着色器中。
  • 是的,我看过你的教程。他们很棒!当我尝试缩放/平移(缩放/平移)我的图像时,我的问题就出现了。我需要能够单独缩放/平移和对比度增强每个像素。我希望使用 webgl2 画布来镜像我的 2d 画布的比例并进行转换,然后从中获取原始的 16 位数据来进行像素修改,这些修改被绘制到我的 2d 画布上。最初我写了我称之为“pixelMapper”的图像(其中 rgb 值映射到图像上的像素位置)来查找 orig 16 位数组中的值。我目前倾向于只使用它(全是“2d”)。

标签: javascript image-processing opengl-es webgl webgl2


【解决方案1】:

在那个 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

【讨论】:

  • 感谢您的帮助!是否可以缩放和翻译这个“非默认”帧缓冲区?我一直在我的着色器中进行这种缩放和翻译。我的最终目标是在 webgl2 画布上以 16 位表示我的数据,该画布与 2d 画布上的 8 位图像同步平移/缩放。 16 位数据用于计算(例如:对比度增强),从而为 2d 画布生成 8 位像素。
  • 换句话说,我如何将其绘制到屏幕上……或者在其上进行缩放或平移?
  • 要将其绘制到屏幕上,您需要使用着色器从 RGBA16UI 纹理对象的数据中(纹理)采样,并选择如何将其输出到默认帧缓冲区 null 以绘制到 RGBA8通过从片段着色器输出 [0.0, 1.0] 来画布后缓冲区。
猜你喜欢
  • 1970-01-01
  • 2022-08-19
  • 2010-11-24
  • 2015-02-26
  • 1970-01-01
  • 2014-03-01
  • 2011-08-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多