webgl - 使用 gl.readPixels 的正确方法是什么?

标签 webgl drawimage

我想从 Three.js 演示中获取像素数据。 据我所知,有两种方法可以继续:

1) 在 2D Canvas 内绘制 webGl Canvas 并使用 Context2D.getImageData ,如下所示:

var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
ctx.drawImage(renderer.domElement,0,0);
var data = ctx.getImageData(0,0,w,h).data;

2) 直接使用 context3D 和 readPixels,如下所示:

var ctx = renderer.domElement.getContext("webgl");
var data = new UInt8Array(w*h*4);
ctx.readPixels(0, 0, w,h, ctx.RGBA, ctx.UNSIGNED_BYTE, data);

这两种方法可以工作并给出相同的结果,但第二种方法几乎比使用 context2d.getImageData 的方法慢两倍。

我听起来很奇怪。将 3D 内容绘制到 2D Canvas 中如何比直接使用 context3D 更快?我不明白,我几乎确定我没有正确使用 gl.readPixels 。

然后我的问题是:如何使用 gl.readPixels 才能比 context2d.drawImage + context2d.getImageData 更快?

我尝试使用这样的 Float32Array

var ctx = renderer.domElement.getContext("webgl");
var data = new Float32Array(w*h*4);
ctx.readPixels(0, 0, w,h, ctx.RGBA, ctx.FLOAT, data);

我认为它应该更快,因为没有从 Float 到 UInt8 的转换,但看起来它不起作用,因为我的“数据”数组在调用 ctx.readPixels 后保持为空

感谢您的帮助!

(如果我的英语不完美,请原谅,这不是我的母语)

最佳答案

在我的机器上,我的 readPixels 速度比 drawImage/getImageData 快 2 到 20 倍。在 MacOS Chrome、Firefox 以及 Windows 10 Chrome 和 Firefox 上进行了测试。 Safari 的 readPixels 速度较慢。听起来像是 Safari 中的一个错误,事实上检查 Safari 技术预览版本 46,正如预期的那样,readPixels 比 drawImage/getImageData 快 3 到 1.2 倍

const gl = document.createElement("canvas").getContext("webgl");
const ctx = document.createElement("canvas").getContext("2d");

const w = 512;
const h = 512;

gl.canvas.width = w;
gl.canvas.height = h;
ctx.canvas.width = w;
ctx.canvas.height = h;

const readPixelBuffer = new Uint8Array(w * h * 4);

const tests = [
 { fn: withReadPixelsPreAlloc, msg: "readPixelsPreAlloc", },
 { fn: withReadPixels, msg: "readPixels", },
 { fn: withDrawImageGetImageData, msg: "drawImageGetPixels", },
];

let ndx = 0;
runNextTest();

function runNextTest() {
  if (ndx >= tests.length) {
    return;
  }
  const test = tests[ndx++];
  
  // use setTimeout to give the browser a change to 
  // do something between tests
  setTimeout(function() {
    log(test.msg, "iterations in 5 seconds:", runTest(test.fn));
    runNextTest();
  }, 0);
}

function runTest(fn) {
  const start = performance.now();
  let count = 0;
  for (;;) {
    const elapsed = performance.now() - start;
    if (elapsed > 5000) {
      break;
    }
    fn();
    ++count;
  }
  return count;
}

function withReadPixelsPreAlloc() {
  gl.readPixels(0, 0, w, h, gl.RGBA, gl.UNSIGNED_BYTE, readPixelBuffer);
}

function withReadPixels() {
  const readPixelBuffer = new Uint8Array(w * h * 4);
  gl.readPixels(0, 0, w, h, gl.RGBA, gl.UNSIGNED_BYTE, readPixelBuffer);
}

function withDrawImageGetImageData() {
  ctx.drawImage(gl.canvas, 0, 0);
  ctx.getImageData(0, 0, w, h);
}

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

至于转换为 float , Canvas 本身以字节为单位存储。没有转换为 float ,您可能会遇到总帐错误

const gl = document.createElement("canvas").getContext("webgl");
const buf = new Float32Array(4);
gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.FLOAT, buf);
log("ERROR:", glEnumToString(gl, gl.getError()));

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

function glEnumToString(gl, val) {
  if (val === 0) { return 'NONE'; }
  for (key in gl) {
    if (gl[key] === val) { 
      return key;
    }
  }
  return `0x${val.toString(16)}`;
}

检查控制台,我发现错误是

WebGL: INVALID_ENUM: readPixels: invalid type

关于webgl - 使用 gl.readPixels 的正确方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48139391/

相关文章:

javascript - 为特定几何禁用 WebGL 中的抗锯齿

webgl - 是否可以在同一页面上有两个 WebGL 上下文?

javascript - Webgl 未捕获引用错误 : createShaderFromScriptElement is not defined

javascript - 无法绘制大于 250px 正方形的图像

Java:gc.drawimage 和透明度

c# - WinForms 上的 DrawImage() 函数无法正常工作

javascript - onload 未触发并且图像无法绘制

javascript - 将纹理从 GPU 复制到 CPU

java - 我应该为 Graphics.drawImage() 函数的 ImageObserver 参数使用 null 吗?

webgl - 绘制可变数量的纹理