将网络摄像头流作为 WebGL 纹理进行操作?

问题描述 投票:0回答:2

将网络摄像头流直接获取到 WebGL 作为纹理的最有效方法是什么?

标准流程(如 Three.js 中所述)是使用 video 标签,然后在 canvas 标签中操作它,然后从 canvas 到 WebGL。

这会增加 CPU 开销,并且 Chrome 会启动工作线程将图像数据从相机发送到 GPU(例如每秒 30 次)。

有什么方法可以更有效地做到这一点并减少 CPU 上的渲染/处理?

canvas video html5-video webrtc webgl
2个回答
1
投票

最简单的方法是简单地将流放入视频 HTML 元素中,然后将其转换为纹理。这种方式会导致 fps 较低。幸运的是,有一个库可以快速完成此操作。我不明白到底是怎么回事(可能从流本身中提取纹理,或者优化第一种方法)。

https://p5js.org/examples/dom-video-capture.html https://p5js.org/examples/3d-shader-using-webcam.html#

您可以检查源代码以了解他们是如何做到的。 https://github.com/processing/p5.js/

当我比较这两种方法时,第二种方法的速度为 60fps,而第一种方法的着色器速度为 20fps。


0
投票

用于更新纹理数据的 WebGL 函数

gl.texImage2D()
将直接接受
<video>...<video/>
页面元素作为其最后一个参数的缓冲区源。

因此,您需要做的就是设置 WebGL 管道,请求用户允许捕获其网络摄像头,然后启动一个动画循环,反复调用

gl.texImage2D()
将数据从相机移动到纹理,然后再渲染您的场景。

我测试了这段代码 Mozilla Firefox

122.0.1
。为了简洁起见,我故意省略了错误检查和浏览器兼容性代码:

<!DOCTYPE html>
<html><head><script>
function main() {
    // Capture webcam input using invisible `video` element
    // Adapted from p5js.org/examples/3d-shader-using-webcam.html
    let camera = document.getElementById("camera");
    
    // Ask user permission to record their camera
    navigator.mediaDevices.getUserMedia({video:1,audio:0}).then(
        (stream)=>{ try {
            if ('srcObject' in camera) camera.srcObject = stream;
            else camera.src = window.URL.createObjectURL(stream);
        } catch (err) {camera.src = stream;}},console.log);
    camera.play();
    
    // 512×512 Canvas with WebGL context
    var canvas = document.getElementById("maincanvas");
    var gl = canvas.getContext("webgl");
    canvas.width = canvas.height = 512;
    gl.viewport(0, 0, canvas.width, canvas.height);

    // Vertex shader: Identity map
    var vshader = gl.createShader(gl.VERTEX_SHADER);
    gl.shaderSource(vshader,
        "attribute vec2 p;"+
        "void main(){"+
        "    gl_Position = vec4(p,0,1);"+
        "}");
    gl.compileShader(vshader); 
    
    // Fragment shader: sample video texture, change colors
    var fshader = gl.createShader(gl.FRAGMENT_SHADER);
    gl.shaderSource(fshader,
        "uniform sampler2D data; "+
        "void main() {"+
        "    gl_FragColor=texture2D(data,gl_FragCoord.xy/vec2(512,512)).zxyw;"+
        "}");
    gl.compileShader(fshader); 
    
    // Create and link program
    var program  = gl.createProgram();
    gl.attachShader(program,vshader);
    gl.attachShader(program,fshader);
    gl.linkProgram(program);   
    gl.useProgram(program);

    // Vertices: A screen-filling quad made from two triangles
    gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
    gl.bufferData(gl.ARRAY_BUFFER,new Float32Array([-1,-1,1,-1,-1,1,-1,1,1,-1,1,1]),gl.STATIC_DRAW);
    gl.enableVertexAttribArray(0);
    gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
    
    // Texture to contain the video data
    var texture = gl.createTexture();
    gl.bindTexture(gl.TEXTURE_2D, texture);
    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); 
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
    
    // Bind texture to the "data" argument to the fragment shader
    var param = gl.getActiveUniform(program,0); // data bind point
    gl.uniform1i(gl.getUniformLocation(program,"data"),0);
    gl.activeTexture(gl.TEXTURE0);
    gl.bindTexture(gl.TEXTURE_2D,texture);
    
    // Repeatedly pull camera data and render
    function animate(){
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, camera);
        gl.drawArrays(gl.TRIANGLES, 0, 6);
        requestAnimationFrame(animate);
    }
    animate();
}
</script></head>
<body onload="javascript:main()">
<canvas id='maincanvas' style="width:512px;height:512px;"></canvas>
<video id='camera' visible="False" style="width: 512px; height: 512px; display:none;" controls="true" playsinline="" crossorigin="anonymous"></video>
</body>
</html>
© www.soinside.com 2019 - 2024. All rights reserved.