How to properly check webgl output

浪子不回头ぞ 提交于 2019-12-12 04:37:40

问题


You can find the original question below about LUMINANCE_ALPHA but I realized I was wrong about my problem. The real question should have been :

How can we efficiently check the output value done on a canvas drawn using webgl ?
Is using the webgl canvas as an image to draw it in a 2D canvas and get the values using getImageData() a good idea ?

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 );

Original Question :

I don't understand how gl.LUMINANCE_ALPHA works, from my understand it's supposed to get bytes 2 by 2 and assign the first value to rgb and the second value to alpha. However when I do that with webgl :

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

I'm getting a color of (8, 8, 8, 30) while I'm expecting (1, 1, 1, 30).

I got that definition from those specs :

Each element is an luminance/alpha double. The GL converts each component to floating point, clamps to the range [0,1], and assembles them into an RGBA element by placing the luminance value in the red, green and blue channels.

Not sure how this apply to webgl since there is no double. Maybe I'm missing what converts each component to floating point means or missing some pack/unpack configuration.

Here's a snippet replicating the issue:

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>

update

Just found out that the alpha value (30) will affect the resulting rgb. But I can't find out what's doing exactly, if it's using alpha to compute rgb or if it's reading the wrong bytes from the buffer.


回答1:


When drawing a webgl canvas to another 2d canvas conversion, filtering and blending operations are being applied which may lead to a skewed result. While you can disable blending by setting the globalCompositeOperation on the 2d context to copy you're still running through a conversion and filtering process which is not standardized and is not guaranteed to provide a precise result.

Using readPixels returns correct results and is the only way to get guaranteed accurate readings from the current color framebuffer. If you need that data to be available to a 2D context you may use ImageData in conjunction with putImageData.

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>


来源:https://stackoverflow.com/questions/45014858/how-to-properly-check-webgl-output

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!