【问题标题】:How to properly check webgl output如何正确检查 webgl 输出
【发布时间】:2017-12-14 08:29:44
【问题描述】:

您可以在下面找到有关 LUMINANCE_ALPHA 的原始问题,但我意识到我的问题是错误的。 真正的问题应该是:

我们如何有效地检查使用 webgl 绘制的画布上的输出值?
使用 webgl 画布作为图像在 2D 画布中绘制它并使用 getImageData() 获取值是个好主意吗?

const webglCanvas = ...;

const offCanvas = document.createElement('canvas');
offCanvas.style.background = 'black';
offCanvas.width = canvas.width;
offCanvas.height = canvas.height;

const context = offCanvas.getContext('2d');
context.drawImage(webglCanvas, 0, 0);
console.log( context.getImageData(0, 0, canvas.width, canvas.height).data );

原问题:

我不明白 gl.LUMINANCE_ALPHA 是如何工作的,据我了解,它应该获取 2 乘 2 字节并将第一个值分配给 rgb,将第二个值分配给 alpha。 但是,当我使用 webgl 执行此操作时:

 gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE_ALPHA, 1, 1, 0, gl.LUMINANCE_ALPHA, gl.UNSIGNED_BYTE, new Uint8Array([1, 30]));

我得到(8, 8, 8, 30) 的颜色,而我期待(1, 1, 1, 30)

我从those specs 得到这个定义:

每个元素都是亮度/alpha 双精度值。 GL 将每个分量转换为浮点数,钳制在 [0,1] 范围内,并通过将亮度值放置在红色、绿色和蓝色通道中,将它们组合成一个 RGBA 元素。

不确定这如何应用于 webgl,因为没有双精度。也许我错过了converts each component to floating point 的含义或缺少一些打包/解包配置。

这是一个复制问题的 sn-p:

const vertShaderStr = `
	attribute vec2 a_position;

  void main() {
      gl_Position = vec4(a_position, 0, 1);
  }
`;
const fragShaderStr = `
	 precision mediump float;

   uniform sampler2D u_texture;

   void main() {
        gl_FragColor = texture2D(u_texture, vec2(0, 0));
    }
`

var canvas = document.getElementById('canvas');
canvas.width = 1;
canvas.height = 1;

const gl = canvas.getContext('webgl');

const program = gl.createProgram();

const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertShaderStr);
gl.compileShader(vertexShader);

if ( !gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS) )
  throw new Error('Vertex shader error', gl.getShaderInfoLog(vertexShader));

gl.attachShader(program, vertexShader);

const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragShaderStr);
gl.compileShader(fragmentShader);

if ( !gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS) )
  throw new Error('Fragment shader error', gl.getShaderInfoLog(fragmentShader));

gl.attachShader(program, fragmentShader);

gl.linkProgram(program);

if ( !gl.getProgramParameter(program, gl.LINK_STATUS) )
  throw new Error(gl.getProgramInfoLog(program));
  
gl.useProgram(program);

const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
  1, 1,
  -1, 1,
  1, -1,
  -1, -1
]), gl.STATIC_DRAW);

const positionLocation = gl.getAttribLocation(program, 'a_position');
gl.enableVertexAttribArray(positionLocation);
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);

const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);

/*** Interresting part here ***/
gl.pixelStorei(gl.UNPACK_ALIGNMENT, 2);
gl.pixelStorei(gl.PACK_ALIGNMENT, 2);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE_ALPHA, 1, 1, 0, gl.LUMINANCE_ALPHA, gl.UNSIGNED_BYTE, 
	new Uint8Array([1, 30]));


gl.activeTexture(gl.TEXTURE0);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);

const offCanvas = document.createElement('canvas');
offCanvas.style.background = 'black';
offCanvas.width = canvas.width;
offCanvas.height = canvas.height;

const context = offCanvas.getContext('2d');
context.drawImage(canvas, 0, 0);
console.log( context.getImageData(0, 0, canvas.width, canvas.height).data );
<canvas id="canvas"></canvas>

更新

刚刚发现alpha值(30)会影响生成的rgb。但是我不知道到底在做什么,是使用 alpha 来计算 rgb 还是从缓冲区中读取了错误的字节。

【问题讨论】:

  • 不鼓励使用外部服务进行代码提琴,以便随着时间的推移保持问题的完整性,请在未来使用 SO 内置的代码提琴选项。

标签: webgl


【解决方案1】:

在将 webgl 画布绘制到另一个 2d 画布转换时,正在应用过滤和混合操作,这可能会导致结果偏斜。虽然您可以通过将 2d 上下文中的 globalCompositeOperation 设置为 copy 来禁用混合,但您仍在运行一个非标准化的转换和过滤过程,并且不能保证提供精确的结果。

使用readPixels 会返回正确的结果,并且是从当前颜色帧缓冲区​​获得有保证的准确读数的唯一方法。如果您需要该数据可用于 2D 上下文,您可以将 ImageDataputImageData 结合使用。

const vertShaderStr = `
	attribute vec2 a_position;

  void main() {
      gl_Position = vec4(a_position, 0, 1);
  }
`;
const fragShaderStr = `
	 precision mediump float;

   uniform sampler2D u_texture;

   void main() {
        gl_FragColor = texture2D(u_texture, vec2(0, 0));
    }
`

var canvas = document.getElementById('canvas');
canvas.width = 1;
canvas.height = 1;

const gl = canvas.getContext('webgl');

const program = gl.createProgram();

const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertShaderStr);
gl.compileShader(vertexShader);

if ( !gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS) )
  throw new Error('Vertex shader error', gl.getShaderInfoLog(vertexShader));

gl.attachShader(program, vertexShader);

const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragShaderStr);
gl.compileShader(fragmentShader);

if ( !gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS) )
  throw new Error('Fragment shader error', gl.getShaderInfoLog(fragmentShader));

gl.attachShader(program, fragmentShader);

gl.linkProgram(program);

if ( !gl.getProgramParameter(program, gl.LINK_STATUS) )
  throw new Error(gl.getProgramInfoLog(program));
  
gl.useProgram(program);

const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
  1, 1,
  -1, 1,
  1, -1,
  -1, -1
]), gl.STATIC_DRAW);

const positionLocation = gl.getAttribLocation(program, 'a_position');
gl.enableVertexAttribArray(positionLocation);
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);

const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);

/*** Interresting part here ***/
gl.pixelStorei(gl.UNPACK_ALIGNMENT, 2);
gl.pixelStorei(gl.PACK_ALIGNMENT, 2);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE_ALPHA, 1, 1, 0, gl.LUMINANCE_ALPHA, gl.UNSIGNED_BYTE, 
	new Uint8Array([1, 30]));


gl.activeTexture(gl.TEXTURE0);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
var readback = new Uint8Array(4);
gl.readPixels(0,0,1,1,gl.RGBA,gl.UNSIGNED_BYTE,readback);

const offCanvas = document.createElement('canvas');
offCanvas.style.background = 'black';
offCanvas.width = canvas.width;
offCanvas.height = canvas.height;

const context = offCanvas.getContext('2d');
context.globalCompositeOperation = 'copy';
context.drawImage(canvas, 0, 0,1,1);
console.log("Canvas",context.getImageData(0, 0, canvas.width, canvas.height).data);
console.log("readPixels", readback );
<canvas id="canvas"></canvas>

【讨论】:

  • 转换是可预测的。回读时,像素只是不相乘。 1/(30/256)) ~= 8。此外,无论您做什么,使用一些 alpha 绘制像素都是有损操作。
  • @pleup 那么设置webgl上下文属性premultipliedAlphafalse时canvas结果怎么变成[0,0,0,30]呢?也许我错过了一些东西,但恕我直言,这根本不应该影响回读(因为据说该参数仅影响合成),所以我的判断是它不是真正可预测/可靠的,而且在任何情况下都不能保证。
  • 如果 gl 画布被认为没有预乘,则在 2D 画布上合成期间颜色会乘以(SRC_ALPHA,ONE_MINUS_SRC_ALPHA)。 ( 1*(30/256) ) 舍入为零 ( > 有损操作)。在乘法/非乘法(天花板或地板值?)期间舍入可能存在平台不一致,这就是您的意思?
  • 对...但不应该将 2d 上下文的 globalCompositeOperation 设置为 copy 禁用它?
  • "copy" 忽略 dest 值,但合成器始终确保源是预乘绘制的。例如dxr.mozilla.org/mozilla-central/source/gfx/layers/opengl/…
猜你喜欢
  • 2013-06-12
  • 1970-01-01
  • 1970-01-01
  • 2020-12-03
  • 2012-08-05
  • 2016-04-26
  • 2017-03-10
  • 2012-05-13
  • 2021-07-30
相关资源
最近更新 更多