我想从 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/