webgl readpixels总是返回0,0,0,0

alt*_*ter 13 javascript webgl

我正在尝试在WebGl中进行选择.我有两个渲染的形状,每个都映射了不同的纹理.我试图在某些坐标上抓取像素.这是一个例子.

var pixelValues = new Uint8Array(4);
gl.readPixels(10, 35, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixelValues);
console.log(pixelValues);
Run Code Online (Sandbox Code Playgroud)

但是pixelValues总是包含[0,0,0,0].我究竟做错了什么?我需要做一些与帧缓冲相关的事情吗?

fse*_*erb 13

根据WebGL最新规范,您现在需要调用getContext设置preserveDrawingBuffer标志,如:

var ctx = canvas.getContext("experimental-webgl", {preserveDrawingBuffer: true});
Run Code Online (Sandbox Code Playgroud)

这可以防止绘图缓冲区(颜色,深度,模板)在绘制到屏幕后被清除.请记住,这可能会导致性能下降.

  • 这是错误的:( (2认同)

gma*_*man 8

你不需要preserveDrawingBuffer: true打电话readPixels.您需要的是readPixels在退出当前事件之前调用.

规范说如果你调用影响画布的任何函数(gl.clear,gl.drawXXX),浏览器将在下一个复合操作后清除画布.当复合操作发生时,由浏览器决定.可能是在它处理了几个鼠标事件或键盘事件或点击事件之后.订单未定义.定义的是,在当前事件退出之前不会这样做

render
read
Run Code Online (Sandbox Code Playgroud)

const gl = document.querySelector("canvas").getContext("webgl");

render();
read();  // read in same event

function render() {
  gl.clearColor(.25, .5, .75, 1);
  gl.clear(gl.COLOR_BUFFER_BIT);
}

function read() {
  const pixel = new Uint8Array(4);
  gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel);
  log(pixel);
}

function log(...args) {
  const elem = document.createElement("pre");
  elem.textContent = [...args].join(' ');
  document.body.appendChild(elem);
}
Run Code Online (Sandbox Code Playgroud)
<canvas></canvas>
Run Code Online (Sandbox Code Playgroud)

适用于

render
setTimeout(read, 1000);  // some other event
Run Code Online (Sandbox Code Playgroud)

不起作用

const gl = document.querySelector("canvas").getContext("webgl");

render();
setTimeout(read, 1000);  // read in other event

function render() {
  gl.clearColor(.25, .5, .75, 1);
  gl.clear(gl.COLOR_BUFFER_BIT);
}

function read() {
  const pixel = new Uint8Array(4);
  gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel);
  log(pixel);
}

function log(...args) {
  const elem = document.createElement("pre");
  elem.textContent = [...args].join(' ');
  document.body.appendChild(elem);
}
Run Code Online (Sandbox Code Playgroud)
<canvas></canvas>
Run Code Online (Sandbox Code Playgroud)

请注意,因为它是复合操作(浏览器实际上在页面上绘制画布,其余的HTML)触发清除,如果画布不在页面上,那么它不会被合成,也不会被清除.

换句话说,上面没有工作的情况在这里起作用

// create an offscreen canvas. Because it's offscreen it won't be composited
// and therefore will not be cleared.
const gl = document.createElement("canvas").getContext("webgl");

render();
setTimeout(read, 1000);  // read in other event

function render() {
  gl.clearColor(.25, .5, .75, 1);
  gl.clear(gl.COLOR_BUFFER_BIT);
}

function read() {
  const pixel = new Uint8Array(4);
  gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel);
  log(pixel);
}

function log(...args) {
  const elem = document.createElement("pre");
  elem.textContent = [...args].join(' ');
  document.body.appendChild(elem);
}
Run Code Online (Sandbox Code Playgroud)

现在,如果您想要调用readPixels其他事件,例如当用户单击某个元素时,您至少有2个选项

  1. preserveDrawingBuffer: true

  2. 在您的活动中再次渲染

    screenshotElement.addEventListener('click', event => {
      render();  
      gl.readPixels(...);
    });
    
    Run Code Online (Sandbox Code Playgroud)