HTML Canvas 嗅探 Webgl 数据
HTML Canvas Sniff Webgl Data
这个问题可能很奇怪,但假设我们有一个 canvas,例如绘制一些像这样的 3D 内容 experiment。
不管使用ThreeJS、Babylon或任何其他库来实现相同的效果,是否可以设置一些间隔来复制每个体素的出生并稍后重复(重绘)。
只是我想记录 canvas 绘制过程并重播,而不使用 RTC、视频或图像序列。
做了什么?
我一直在尝试 WebGl Context
和Stream Capture,可惜没能达到预期的效果。
有人可以帮忙吗?
您可以包装 WebGL 上下文并捕获所有函数调用。包装 WebGL 上下文的示例类似于
const rawgl = document.querySelector("canvas").getContext("webgl");
const gl = wrapContext(rawgl);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.enable(gl.SCISSOR_TEST);
gl.scissor(40, 50, 200, 60);
gl.clearColor(0,1,1,1);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.scissor(60, 40, 70, 90);
gl.clearColor(1,0,1,1);
gl.clear(gl.COLOR_BUFFER_BIT);
function wrapContext(gl) {
const wrapper = {};
for (let name in gl) {
var prop = gl[name];
if (typeof(prop) === 'function') {
wrapper[name] = wrapFunction(gl, name, prop);
} else {
wrapProperty(wrapper, gl, name);
}
}
return wrapper;
}
function wrapFunction(gl, name, origFn) {
// return a function that logs the call and then calls the original func
return function(...args) {
log(`gl.${name}(${[...args].join(", ")});`);
origFn.apply(gl, arguments);
};
}
function wrapProperty(wrapper, gl, name) {
// make a getter because these values are dynamic
Object.defineProperty(wrapper, name, {
enumerable: true,
get: function() {
return gl[name];
},
});
}
function log(...args) {
const elem = document.createElement("pre");
elem.textContent = [...args].join(" ");
document.body.appendChild(elem);
}
canvas { border: 1px solid black; }
pre { margin: 0; }
<canvas></canvas>
在您的情况下,您无需记录调用,而是将它们添加到仅在您要捕获的帧上的某个调用数组中。
然后您需要以某种方式跟踪所有资源(缓冲区、纹理帧缓冲区、渲染缓冲区、着色器、程序)及其所有参数(如纹理上的过滤设置),您还需要跟踪统一设置等。
WebGL-Inspector does this and can playback frames so it might be a good example. There's also this webgl-capture 图书馆。
您需要为您的程序捕获什么取决于您的程序。例如,如果您知道您的缓冲区和纹理永远不会改变,并且当您想要播放时它们仍在内存中,那么您可能不需要尝试捕获上述两个示例都必须做的缓冲区和纹理的状态。
我不知道你是如何尝试使用 captureStream
方法的,但在你的示例页面上,这段代码确实有效。
let s = mycanvas.captureStream(),
r = new MediaRecorder(s),
chunks = [];
r.ondataavailable = e => chunks.push(e.data);
r.onstop = e => {
let videoURL = URL.createObjectURL(new Blob(chunks));
doSomethingWith(videoURL);
};
r.start();
setTimeout(_=>r.stop(), 3000); // records 3 seconds
现在您已获得指向您的 canvas' 记录的有效 blobURL,您可以在 <video>
元素中播放它,然后 draw it in your webgl context.
这个问题可能很奇怪,但假设我们有一个 canvas,例如绘制一些像这样的 3D 内容 experiment。
不管使用ThreeJS、Babylon或任何其他库来实现相同的效果,是否可以设置一些间隔来复制每个体素的出生并稍后重复(重绘)。
只是我想记录 canvas 绘制过程并重播,而不使用 RTC、视频或图像序列。
做了什么?
我一直在尝试 WebGl Context 和Stream Capture,可惜没能达到预期的效果。
有人可以帮忙吗?
您可以包装 WebGL 上下文并捕获所有函数调用。包装 WebGL 上下文的示例类似于
const rawgl = document.querySelector("canvas").getContext("webgl");
const gl = wrapContext(rawgl);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.enable(gl.SCISSOR_TEST);
gl.scissor(40, 50, 200, 60);
gl.clearColor(0,1,1,1);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.scissor(60, 40, 70, 90);
gl.clearColor(1,0,1,1);
gl.clear(gl.COLOR_BUFFER_BIT);
function wrapContext(gl) {
const wrapper = {};
for (let name in gl) {
var prop = gl[name];
if (typeof(prop) === 'function') {
wrapper[name] = wrapFunction(gl, name, prop);
} else {
wrapProperty(wrapper, gl, name);
}
}
return wrapper;
}
function wrapFunction(gl, name, origFn) {
// return a function that logs the call and then calls the original func
return function(...args) {
log(`gl.${name}(${[...args].join(", ")});`);
origFn.apply(gl, arguments);
};
}
function wrapProperty(wrapper, gl, name) {
// make a getter because these values are dynamic
Object.defineProperty(wrapper, name, {
enumerable: true,
get: function() {
return gl[name];
},
});
}
function log(...args) {
const elem = document.createElement("pre");
elem.textContent = [...args].join(" ");
document.body.appendChild(elem);
}
canvas { border: 1px solid black; }
pre { margin: 0; }
<canvas></canvas>
在您的情况下,您无需记录调用,而是将它们添加到仅在您要捕获的帧上的某个调用数组中。
然后您需要以某种方式跟踪所有资源(缓冲区、纹理帧缓冲区、渲染缓冲区、着色器、程序)及其所有参数(如纹理上的过滤设置),您还需要跟踪统一设置等。
WebGL-Inspector does this and can playback frames so it might be a good example. There's also this webgl-capture 图书馆。
您需要为您的程序捕获什么取决于您的程序。例如,如果您知道您的缓冲区和纹理永远不会改变,并且当您想要播放时它们仍在内存中,那么您可能不需要尝试捕获上述两个示例都必须做的缓冲区和纹理的状态。
我不知道你是如何尝试使用 captureStream
方法的,但在你的示例页面上,这段代码确实有效。
let s = mycanvas.captureStream(),
r = new MediaRecorder(s),
chunks = [];
r.ondataavailable = e => chunks.push(e.data);
r.onstop = e => {
let videoURL = URL.createObjectURL(new Blob(chunks));
doSomethingWith(videoURL);
};
r.start();
setTimeout(_=>r.stop(), 3000); // records 3 seconds
现在您已获得指向您的 canvas' 记录的有效 blobURL,您可以在 <video>
元素中播放它,然后 draw it in your webgl context.