我正试图从画布中获取像素RGBA数据,以供进一步处理。我认为画布实际上是一个团结的游戏,如果这有区别的话。
我试着用摇摇与飞碟游戏的画布来做这件事。我使用来自readPixels的上下文方法。
这就是我试过的:
var example = document.getElementById('#canvas');
var context = example.getContext('webgl2'); // Also doesn't work with: ', {preserveDrawingBuffer: true}'
var pixels = new Uint8Array(context.drawingBufferWidth * context.drawingBufferHeight * 4);
context.readPixels(0, 0, context.drawingBufferWidth, context.drawingBufferHeight, context.RGBA, context.UNSIGNED_BYTE, pixels);但所有像素显然都是黑色的(这显然不是真的)。
编辑:而且,我想要多次读取像素。谢谢大家的回答。https://stackoverflow.com/users/3702797/kaiido提供的答案对我来说非常有用:)
发布于 2019-01-05 10:18:00
您只能要求画布上下文一次。如果将相同的选项传递给null,以下所有请求都将返回getContext(),或者返回之前创建的相同上下文。
现在,您链接到的一个页面在创建上下文时没有传递preserveDrawingBuffer选项,这意味着要从其中获取像素信息,您必须连接到与游戏循环发生的事件循环相同的事件循环。
幸运的是,这个精确的游戏确实使用了一个简单的requestAnimationFrame循环,因此要连接到同一个事件循环,我们所需要做的就是将代码打包到一个requestAnimationFrame调用中。
因为回调是堆叠的,而且它们确实需要从一个这样的回调的下一个帧创建一个循环,所以我们可以确保我们的调用会在它们之后被堆叠。
我现在意识到这可能并不明显,所以我将进一步解释requestAnimationFrame的功能,以及我们如何确保在联合的回调之后会被调用。
requestAnimationFrame(fn)将fn回调推入一个回调堆栈中,这些回调将以先到先出的顺序同时被调用,就在浏览器执行屏幕操作之前。这偶尔发生一次(通常是每秒60次),在最近的事件循环结束时发生。
它可以理解为一种setTimeout(fn , time_remaining_until_next_paint),其主要区别是保证requestAnimationFrame回调执行器将在事件循环结束时被调用,从而在此事件循环执行其他js之后被调用。
因此,如果我们在调用回调的事件循环中调用requestAnimationFrame(fn),那么我们的假time_remaining_until_next_paint将是0,而fn将被推到堆栈的底部(最后一次进入,最后一次退出)。
当从回调执行器本身调用requestAnimationFrame(fn)时,time_remaining_until_next_paint将是16的一部分,而fn将在下一个帧的第一个调用中被调用。
因此,在请求动画框架的回调执行器之外进行的对requestAnimationFrame(fn)的任何调用都保证在与requestAnimationFrame驱动的循环相同的事件循环中调用,并在后面调用。
因此,我们需要获取这些像素,只需将对readPixels的调用包装在一个requestAnimationFrame调用中,并在统一的循环开始后将其称为。
var example = document.getElementById('#canvas');
var context = example.getContext('webgl2') || example.getContext('webgl');
var pixels = new Uint8Array(context.drawingBufferWidth * context.drawingBufferHeight * 4);
requestAnimationFrame(() => {
context.readPixels(0, 0, context.drawingBufferWidth, context.drawingBufferHeight, context.RGBA, context.UNSIGNED_BYTE, pixels);
// here `pixels` has the correct data
});发布于 2019-01-05 16:55:35
可能您需要在呈现的同一事件中读取像素,或者需要强制画布使用preserveDrawingBuffer: true,以便随时读取画布。
若要执行第二个覆盖getContext,请执行
HTMLCanvasElement.prototype.getContext = function(origFn) {
const typesWeCareAbout = {
"webgl": true,
"webgl2": true,
"experimental-webgl": true,
};
return function(type, attributes = {}) {
if (typesWeCareAbout[type]) {
attributes.preserveDrawingBuffer = true;
}
return origFn.call(this, type, attributes);
};
}(HTMLCanvasElement.prototype.getContext);在“团结”游戏之前将其放在文件的顶部,或者将其放在单独的脚本文件中,并在“团结”游戏之前将其包括在内。
现在,您应该能够在任何canvas Unity上获得上下文,并在任何时候调用gl.readPixels。
对于另一种方法,即获取同一事件中的像素,您可以将requestAnimationFrame包装起来,以便在联合使用requestAnimationFrame之后插入您的requestAnimationFrame
window.requestAnimationFrame = function(origFn) {
return function(callback) {
return origFn(this, function(time) {
callback(time);
gl.readPixels(...);
};
};
}(window.requestAnimationFrame);另一种解决方案是使用虚拟webgl上下文。此库展示了实现虚拟webgl上下文的示例。和shows 后处理单位输出的一个例子
请注意,在某个时候,联合可能会切换到使用OffscreenCanvas。到那时,它可能需要除上述解决方案之外的其他解决办法。
发布于 2021-05-08 06:43:31
或者,您可以将画布的内容流到一个视频元素中,将视频的内容绘制到另一个画布上,并读取那里的像素。
这应该独立于requestAnimationFrame绘制的框架,但是是异步的。
我们需要一个视频,另一个画布和一条溪流:
var example = document.getElementById('#canvas');
var stream=example.captureStream(0);//0 fps
var vid=document.createElement("video");
vid.width=example.width;
vid.height=example.height;
vid.style="display:none;";
document.body.appendChild(vid);
var canvas2=document.createElement("canvas");
canvas2.width=example.width;
canvas2.height=example.height;
canvas2.style="display:none;";
var width=example.width;
var height=example.height;
body.appendChild(canvas2);
var ctx = canvas2.getContext('2d');现在,您可以通过请求流中的帧、将其插入视频并将视频绘制到我们的画布上来读取游戏画布:
stream.requestFrame();
//wait for the game to draw a frame
vid.srcObject=stream;
//wait
ctx.drawImage(vid, 0, 0, width, height, 0, 0, width, height);
var pixels = new Uint8Array(context.drawingBufferWidth * context.drawingBufferHeight * 4);
ctx.readPixels(0, 0, ctx.drawingBufferWidth, ctx.drawingBufferHeight, ctx.RGBA, ctx.UNSIGNED_BYTE, pixels);https://stackoverflow.com/questions/54047609
复制相似问题