threejs getImageData 视频表现

threejs getImageData video performance

编辑;

working codepen(需要提供视频文件以避免跨域政策)

https://codepen.io/bw1984/pen/pezOXm


我正在尝试在此处修改优秀的 rutt etra 示例 https://airtightinteractive.com/demos/js/ruttetra/ 以用于视频(仍在使用 threejs)并且我遇到了奇怪的性能问题。

我的代码目前按预期工作,实际上在我的 macbook pro 上 chrome 上运行相当顺利,但似乎会导致某种缓慢的内存泄漏,我认为这与所有繁重的工作有关必须由 getImageData 完成。奇怪的是,它只有在我尝试刷新选项卡时才会被注意到,所以看起来它可能与 chrome 中的垃圾收集有关?无论如何将繁重的工作分流到 GPU 而不是杀死 CPU?

我只是想知道我是否在代码优化方面遗漏了任何明显的东西,或者考虑到我正在尝试做的事情的性质,我所面临的性能问题是否在意料之中。

我只对 WebGL / chrome 功能感兴趣,所以真的不需要担心任何类型的浏览器兼容性。

<script>

var container, camera, scene, renderer, controls;

// PI
var PI = Math.PI;
var TWO_PI = PI*2;

// size

SCREEN_WIDTH = window.innerWidth;
SCREEN_HEIGHT = window.innerHeight;
SCREEN_PIXEL_RATIO = window.devicePixelRatio;

// camera

var VIEW_ANGLE = 45;
var ASPECT = SCREEN_WIDTH / SCREEN_HEIGHT;
var NEAR = 0.1;
var FAR = 20000000;



// video raster

var video;
var videoImage;
var videoImageContext;

var _imageHeight;
var _imageWidth;


// lines

var _lineGroup;


// gui

var _guiOptions = {
    stageSize:      1,
    scale:          1.0,
    scanStep:       5,
    lineThickness:  10.0,
    opacity:        1.0,
    depth:          50,
    autoRotate:     false
};


// triggered from audio.php getMediaStream

function runme()
{
    console.log('runme running');

    init();
    animate();
}

runme();


function init() 
{
    container = document.createElement('div');
    document.body.appendChild(container);

    //----------
    // scene
    //----------

        scene = new THREE.Scene();


    //----------
    // camera
    //----------

        camera = new THREE.PerspectiveCamera(VIEW_ANGLE, ASPECT, NEAR, FAR);

        //camera.position.set(0,0,450);

        camera.position.set(0,150,300);


    //----------
    // objects
    //----------

        // create the video element
        video = document.createElement('video');
        // video.id = 'video';
        // video.type = ' video/ogg; codecs="theora, vorbis" ';
        video.src = 'data/sintel.ogv';
        //video.src = 'data/az.mp4';

        video.load(); // must call after setting/changing source
        video.play();

        videoImage = document.createElement('canvas');
        //videoImage.width = 480;
        //videoImage.height = 204;

        videoImageContext = videoImage.getContext('2d');

        _imageWidth = videoImage.width;
        _imageHeight = videoImage.height;

        //videoImageContext.fillStyle = '#ffffff';
        //videoImageContext.fillRect(0, 0, videoImage.width, videoImage.height);



    //----------
    // controls
    //----------

        controls = new THREE.OrbitControls(camera);


    //----------
    // events
    //----------

        window.addEventListener('resize', onWindowResize, false);


    //----------
    // render
    //----------

        var args = {
            //antialias: true // too slow
        }

        renderer = new THREE.WebGLRenderer(args);

        renderer.setClearColor(0x000000, 1);
        renderer.setPixelRatio(SCREEN_PIXEL_RATIO); //Set pixel aspect ratio
        renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);

        // attach to dom
        container.appendChild(renderer.domElement);

        //render();
}


function render()
{

    if(video.readyState === video.HAVE_ENOUGH_DATA && !video.paused && !video.ended) // and video.currentTime > 0 
    {
        //_imageWidth = videoImage.width;
        //_imageHeight = videoImage.height;

        videoImageContext.drawImage(video,0,0,_imageWidth,_imageHeight);


        // Grab the pixel data from the backing canvas
        var _data = videoImageContext.getImageData(0,0,videoImage.width,videoImage.height).data;

        //log(data);

        //_pixels = data;

        var x = 0, y = 0;

        if(_lineGroup)
        {
            scene.remove(_lineGroup);
            //_lineGroup = null;
        }

        _lineGroup = new THREE.Object3D();


        var _material = new THREE.LineBasicMaterial({
            color: 0xffffff,
            linewidth: _guiOptions.lineThickness
        });


        // loop through the image pixels

        for(y = 0; y < _imageHeight; y+= _guiOptions.scanStep) 
        {

            var _geometry = new THREE.Geometry();

            for(x=0; x<_imageWidth; x+=_guiOptions.scanStep) 
            {
                var color = new THREE.Color(getColor(x, y, _data));

                var brightness = getBrightness(color);

                var posn = new THREE.Vector3(x -_imageWidth/2,y - _imageHeight/2, -brightness * _guiOptions.depth + _guiOptions.depth/2);

                //_geometry.vertices.push(new THREE.Vertex(posn));
                _geometry.vertices.push(posn);

                _geometry.colors.push(color);

                _color = null;
                _brightness = null;
                _posn = null;
            }

            // add a line
            var _line = new THREE.Line(_geometry, _material);

            //log(line);

            _lineGroup.add(_line);

            // gc
            _geometry = null;
        }

        scene.add(_lineGroup);

        _data = null;
        _line = null;

    }

    renderer.render(scene,camera);
}


function animate(){

    requestAnimationFrame(animate);

    stats.update();

    render();
}


function onWindowResize(){

    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
    render();
}



// Returns a hexadecimal color for a given pixel in the pixel array.

function getColor(x, y, _pixels)
{
    var base = (Math.floor(y) * _imageWidth + Math.floor(x)) * 4;

    var c = {
        r: _pixels[base + 0],
        g: _pixels[base + 1],
        b: _pixels[base + 2],
        a: _pixels[base + 3]
    };
    return (c.r << 16) + (c.g << 8) + c.b;
}



// return pixel brightness between 0 and 1 based on human perceptual bias

function getBrightness(c) 
{
    return ( 0.34 * c.r + 0.5 * c.g + 0.16 * c.b );
}

</script>

任何人都可以提供的任何帮助将不胜感激,即使它只是为我指明了正确的方向,因为我才刚刚开始尝试这些东西并且几乎给自己一个动脉瘤试图包裹我的小头脑它。

缓慢的内存泄漏很可能是由于:

        // add a line
        var _line = new THREE.Line(_geometry, _material);

        //log(line);

        _lineGroup.add(_line);

THREE.Line 是一个对象,包含其他对象和大量数据。每次实例化它时,它都会创建 .matrix.matrixWorld.modelViewMatrix.normalMatrix,这些都是带有一堆数字的数组。 .position.quaternion.scale.rotation 可能还有 .up 是向量、quats 等,它们稍微小一些,但也是具有特殊构造函数的数组。

每 16 毫秒分配一次所有这些,仅在下一帧释放,这可能是您 "leak" 的原因。

您应该创建一个包含 THREE.Line 个对象的池,然后在每一帧绘制它。您可以使用 .visible 控制并改变其变换属性的绘制对象的数量。

@pailhead 我听取了你关于提前预渲染线条和线条组的建议,然后更新每个动画帧上的顶点,现在它像小猫一样发出咕噜咕噜的声音。还需要插入以下行以确保拾取更新的坐标;

e.geometry.verticesNeedUpdate = true;

我无法弄清楚如何让托管视频在 codepen(违反跨源策略问题)上工作,但我已经发布了一个版本来显示工作代码。

https://codepen.io/bw1984/pen/pezOXm

我会尽快安装一个自托管(工作)版本

我一直在徒劳地尝试让颜色发挥作用,但这必须改天再练习。