在Three.js中优化数百条动态行

时间:2018-10-03 22:17:31

标签: javascript performance three.js 3d

我是Three.js的新手,我只是在玩它。我试图在页面上实现一个简单的动态全屏背景,您得到the example here

function createHexagon( vertices, color ) {
    var geometry = new THREE.BufferGeometry();
    geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
    var material = new THREE.LineBasicMaterial( { color: color, opacity: Math.min((Math.random() / 5), 0.1), transparent: true } );

    var hexagon = new THREE.Line( geometry, material );
    return hexagon;
}

function initMatrix() {
    var color = defaultColor.getHex();
    var vertices;

    var x = ( width / -2 ) - 90;
    var y = height / -2;
    var deltaX = 120;
    var deltaY = 60;
    var time = 5.0;
    while( y < height / 2 ) {
        while( x < width / 2 ) {

            vertices = new Float32Array([
                0,  30, 0,
               20,   0, 0
            ]);
            var hexagon = createHexagon( vertices, color );
            scene.add( hexagon );  
            hexagon.position.set( x, y, 0 );

            vertices = new Float32Array([
               20,   0, 0,
               60,   0, 0
            ]);
            var hexagon = createHexagon( vertices, color );
            scene.add( hexagon );  
            hexagon.position.set( x, y, 0 );

            vertices = new Float32Array([
               60,   0, 0,
               80,  30, 0
            ]);
            var hexagon = createHexagon( vertices, color );
            scene.add( hexagon );  
            hexagon.position.set( x, y, 0 );

            x += deltaX;

        }

        x = ( width / -2 ) - 90;
        y += deltaY;
    }

    x = ( width / -2 ) - 30;
    y = ( height / -2 ) - 30;
    deltaX = 120;
    deltaY = 60;
    while( y < height / 2 ) {
        while( x < width / 2 ) {

            vertices = new Float32Array([
                0,  30, 0,
               20,   0, 0
            ]);
            var hexagon = createHexagon( vertices, color );
            scene.add( hexagon );  
            hexagon.position.set( x, y, 0 );

            vertices = new Float32Array([
               20,   0, 0,
               60,   0, 0
            ]);
            var hexagon = createHexagon( vertices, color );
            scene.add( hexagon );  
            hexagon.position.set( x, y, 0 );

            vertices = new Float32Array([
               60,   0, 0,
               80,  30, 0
            ]);
            var hexagon = createHexagon( vertices, color );
            scene.add( hexagon );  
            hexagon.position.set( x, y, 0 );          

            x += deltaX;
        }

        x = ( width / -2 ) - 30;
        y += deltaY;
    }
}

这些是单个bufferGeometry行(如您在上面的函数中创建背景所看到的),它们随机旋转并使用raycaster和TweenLite更改鼠标悬停时的不透明度。它工作正常。您会注意到,CPU使用率几乎达到100%。

我知道,如果将线分组为相同的几何形状,则性能会更好,但是我无法使用raycaster设置单线的动画,尤其是不透明度。

我搜索了很多讨论,并尝试了很多事情。最好的结果是这种方式,分别渲染单行。您能提出一些建议吗?

2 个答案:

答案 0 :(得分:0)

(代表问题作者发布)

我找到了解决方案。我制作了一个整体几何图形,并为顶点和相应的颜色建立了索引。唯一的问题是我无法以这种方式管理不透明性,但是它可以正常工作,CPU大约为20%。

document.addEventListener( 'DOMContentLoaded', main );

var width = screen.width;
var height = screen.height;
var camera, scene, renderer, raycaster, mouse;
var rayColor = new THREE.Color( 0x0640C2 );
var rayColor2 = new THREE.Color( 0xCC311B );
var colors;
var linesPositions, originalPositions;
var linesMesh;
var geometry;
var intersects;

var stats;

function initMatrix() {

    var AlinesPositions = [ ];

    var x = ( width / - 2 ) - 80;
    var y = ( height / - 2 ) - 60;
    var deltaX = 120;
    var deltaY = 60;

    while( y <= ( height / 2 ) ) {

        while( x <= ( width / 2 ) ) {

            AlinesPositions.push( x, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
            AlinesPositions.push( x + 80, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
            AlinesPositions.push( x + 80, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
            AlinesPositions.push( x + 80, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
            AlinesPositions.push( x + 80, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
            AlinesPositions.push( x, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
            AlinesPositions.push( x, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
            AlinesPositions.push( x, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );

            x += deltaX;

        }

        x = ( width / -2 ) - 80;
        y += deltaY;

    }

    x = ( width / - 2 );
    y = ( height / - 2 ) - 60;

    while( y <= ( height / 2 ) ) {

        while( x <= ( width / 2 ) ) {

            AlinesPositions.push( x, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
            AlinesPositions.push( x + 80, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
            AlinesPositions.push( x + 80, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
            AlinesPositions.push( x + 80, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
            AlinesPositions.push( x + 80, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
            AlinesPositions.push( x, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
            AlinesPositions.push( x, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
            AlinesPositions.push( x, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );

            x += deltaX;

        }

        x = ( width / -2 );
        y += deltaY;

    }

    linesPositions = new Float32Array( AlinesPositions );

    var Acolors = [ ];
    for( var i = 0; i < AlinesPositions.length; i++ ) {

        var fact = Math.random() * 20.0;
        var ran = Math.min( Math.max( ( Math.random() / fact ), 0.01 ), 0.5 );
        Acolors[i] = ran;
        Acolors[i+1] = ran;
        Acolors[i+2] = ran;

        i += 2;
    }

    colors = new Float32Array( Acolors );

    geometry = new THREE.BufferGeometry();

    geometry.addAttribute( 'position', new THREE.BufferAttribute( linesPositions, 3 ).setDynamic( true ) );
    geometry.addAttribute( 'color', new THREE.BufferAttribute( colors, 3 ).setDynamic( true ) );

    var material = new THREE.LineBasicMaterial( {
        vertexColors: THREE.VertexColors,
        blending: THREE.AdditiveBlending,
        transparent: true
    } );

    linesMesh = new THREE.LineSegments( geometry, material );
    scene.add( linesMesh );

    originalPositions = new THREE.BufferAttribute( linesPositions, 3 );
    originalPositions.copy( linesMesh.geometry.attributes.position );

}

function init() {
    scene = new THREE.Scene();
    //camera = new THREE.OrthographicCamera( width / - 2, width / 2, height / 2, height / - 2, 1, 100 );
    camera = new THREE.PerspectiveCamera( 120, width / height, 1, 1000 );
    camera.position.set( 0, 0, 200 );
    camera.lookAt( 0, 0, 0 );

    // Create matrix
    initMatrix();

    raycaster = new THREE.Raycaster();
    raycaster.linePrecision = 20;

    mouse = new THREE.Vector2();

    renderer = new THREE.WebGLRenderer({ antialias: true });
    renderer.setSize( width, height );
    document.body.appendChild( renderer.domElement );
}

function main() {

    init();
    animate();

}

function animate() {

    requestAnimationFrame( animate );
    render();

}

var intersected = [];

function removeX( i ) { intersected.splice( i, 1 ); }

function alterate( index ) {

    var randColor = ( Math.random() <= 0.49 ) ? rayColor : rayColor2;
    // Change color
    var currentC = new THREE.Color( linesMesh.geometry.attributes.color.getX( index ), linesMesh.geometry.attributes.color.getY( index ), linesMesh.geometry.attributes.color.getZ( index ));
    var tweenC = TweenLite.to( currentC, 1.0, {

        r: randColor.r,
        g: randColor.g,
        b: randColor.b,
        immediateRender: true,
        lazy: false,
        onUpdate: function() {

            linesMesh.geometry.attributes.color.setXYZ( index, currentC.r, currentC.g, currentC.b );
            linesMesh.geometry.attributes.color.needsUpdate = true;

        },
        onUpdateParams: [ index, currentC ]

    });

    // Change coordinates
    var currentXY = { x: originalPositions.getX( index ), y: originalPositions.getY( index ), z: originalPositions.getZ( index ) };
    var goal = [ ( currentXY.x + ( Math.random() * 50 ) * ( ( Math.random() < 0.5 ) ? 1 : -1 ) ), ( currentXY.y + ( Math.random() * 50 ) * ( ( Math.random() < 0.5 ) ? 1 : -1 ) ), ( currentXY.z + ( Math.random() * 50 ) * ( ( Math.random() < 0.5 ) ? 1 : -1 ) ) ];
    var tweenXY = TweenLite.to( currentXY, 1.0, {

        x: goal[ 0 ],
        y: goal[ 1 ],
        z: goal[ 2 ],
        immediateRender: true,
        lazy: false,
        onUpdate: function() {

            linesMesh.geometry.attributes.position.setXYZ( index, currentXY.x, currentXY.y, currentXY.z );    
            linesMesh.geometry.attributes.position.needsUpdate = true;

        },
        onUpdateParams: [ index, currentXY ]

    });

}

function render() {

    // update the picking ray with the camera and mouse position
    raycaster.setFromCamera( mouse, camera );

    intersects = raycaster.intersectObject( linesMesh );

    if( intersects.length ) {

        if( !intersected.includes( intersects[ 0 ].index ) ) {

            for( var x = 0; x < intersects.length; x++ ) {

                if( !intersected.includes( intersects[ x ].index ) ) {

                    var index = intersects[ x ].index;

                    // Save index
                    intersected.push( index );

                    alterate( index );

                }

            }

            var present = 0;

            for( var y = 0; y < intersected.length; y++ ) {

                for( var j = 0; j < intersects.length; j++ ) {

                    if( intersects[ j ].index == intersected[ y ] ) { present = 1; break; }

                }

                if( !present ) {

                    ( function( y ) { 

                        var randC = Math.min( Math.max( ( Math.random() / ( Math.random() * 20.0 ) ), 0.01 ), 0.5 );

                        // Current item old coordinates
                        let index = intersected[ y ];
                        var indexX = originalPositions.getX( index );
                        var indexY = originalPositions.getY( index );
                        var indexZ = originalPositions.getZ( index );

                        removeX( y );

                        // Reset color
                        var currentCreset = new THREE.Color( linesMesh.geometry.attributes.color.getX( index ), linesMesh.geometry.attributes.color.getY( index ), linesMesh.geometry.attributes.color.getZ( index ));
                        var tweenCreset = TweenLite.to( currentCreset, 1.5, {

                            r: randC,
                            g: randC,
                            b: randC,
                            immediateRender: true,
                            lazy: false,
                            onUpdate: function() {

                                linesMesh.geometry.attributes.color.setXYZ( index, currentCreset.r, currentCreset.g, currentCreset.b );
                                linesMesh.geometry.attributes.color.needsUpdate = true;

                            },
                            onUpdateParams: [ index, currentCreset ],
                            onComplete: function() { }

                        } );

                        // Reset coordinates
                        var currentXYreset = { x: linesMesh.geometry.attributes.position.getX( index ), y: linesMesh.geometry.attributes.position.getY( index ), z: linesMesh.geometry.attributes.position.getZ( index ) };
                        var tweenXYreset = TweenLite.to( currentXYreset, 1.5, {

                            x: indexX,
                            y: indexY,
                            z: indexZ,
                            immediateRender: true,
                            lazy: false,
                            onUpdate: function() {

                                linesMesh.geometry.attributes.position.setXYZ( index, currentXYreset.x, currentXYreset.y, currentXYreset.z );    
                                linesMesh.geometry.attributes.position.needsUpdate = true;

                            },
                            onUpdateParams: [ index, currentXYreset ]                       

                        });

                    }) ( y );

                }

            }

        }

    }

    renderer.render( scene, camera );

}

function onMouseMove( event ) {

    // calculate mouse position in normalized device coordinates
    // (-1 to +1) for both components
    event.preventDefault();

    mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
    mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;

    var normX = event.clientX - ( window.innerWidth / 2 );
    var normY = event.clientY - ( window.innerHeight / 2 );

    camera.position.set( 0 + ( normY / 50 ), 0 + ( normX / 50 ), 200 );
    camera.lookAt( 0 + ( normY / 50 ), 0 + ( normX / 50 ), 0 );

}

window.addEventListener( 'click', function() {

    if( intersected.length ) {

        for( var x = 0; x < intersected.length; x++ ) {

            var index = intersected[ x ];

            alterate( index );

        }

    }

});

window.addEventListener( 'mousemove', onMouseMove, false );
// Set new dimensions for scene when resize window
window.addEventListener( 'resize', function() { 
    var wWidth = window.innerWidth;
    var wHeight = window.innerHeight;

    /*camera.left = wWidth / -2;
    camera.right = wWidth / 2;
    camera.top = wHeight / 2;
    camera.bottom = wHeight / -2;*/
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();

    raycaster.setFromCamera( mouse, camera );
    renderer.setSize( window.innerWidth, window.innerHeight );
} );

这里是Codepen link

答案 1 :(得分:0)

我找到了解决方案。我制作了一个整体几何图形,并为顶点和相应的颜色建立了索引。唯一的问题是我无法以这种方式管理不透明性,但是它可以正常工作,CPU大约为20%。

document.addEventListener( 'DOMContentLoaded', main );

var width = screen.width;
var height = screen.height;
var camera, scene, renderer, raycaster, mouse;
var rayColor = new THREE.Color( 0x0640C2 );
var rayColor2 = new THREE.Color( 0xCC311B );
var colors;
var linesPositions, originalPositions;
var linesMesh;
var geometry;
var intersects;

var stats;

function initMatrix() {

    var AlinesPositions = [ ];

    var x = ( width / - 2 ) - 80;
    var y = ( height / - 2 ) - 60;
    var deltaX = 120;
    var deltaY = 60;

    while( y <= ( height / 2 ) ) {

        while( x <= ( width / 2 ) ) {

            AlinesPositions.push( x, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
            AlinesPositions.push( x + 80, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
            AlinesPositions.push( x + 80, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
            AlinesPositions.push( x + 80, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
            AlinesPositions.push( x + 80, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
            AlinesPositions.push( x, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
            AlinesPositions.push( x, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
            AlinesPositions.push( x, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );

            x += deltaX;

        }

        x = ( width / -2 ) - 80;
        y += deltaY;

    }

    x = ( width / - 2 );
    y = ( height / - 2 ) - 60;

    while( y <= ( height / 2 ) ) {

        while( x <= ( width / 2 ) ) {

            AlinesPositions.push( x, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
            AlinesPositions.push( x + 80, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
            AlinesPositions.push( x + 80, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
            AlinesPositions.push( x + 80, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
            AlinesPositions.push( x + 80, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
            AlinesPositions.push( x, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
            AlinesPositions.push( x, y + 60, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );
            AlinesPositions.push( x, y, 0 /*Math.abs( ( 100 / ( width / 2 ) * x ) + ( 100 / ( height / 2 ) * y ) )*/ );

            x += deltaX;

        }

        x = ( width / -2 );
        y += deltaY;

    }

    linesPositions = new Float32Array( AlinesPositions );

    var Acolors = [ ];
    for( var i = 0; i < AlinesPositions.length; i++ ) {

        var fact = Math.random() * 20.0;
        var ran = Math.min( Math.max( ( Math.random() / fact ), 0.01 ), 0.5 );
        Acolors[i] = ran;
        Acolors[i+1] = ran;
        Acolors[i+2] = ran;

        i += 2;
    }

    colors = new Float32Array( Acolors );

    geometry = new THREE.BufferGeometry();

    geometry.addAttribute( 'position', new THREE.BufferAttribute( linesPositions, 3 ).setDynamic( true ) );
    geometry.addAttribute( 'color', new THREE.BufferAttribute( colors, 3 ).setDynamic( true ) );

    var material = new THREE.LineBasicMaterial( {
        vertexColors: THREE.VertexColors,
        blending: THREE.AdditiveBlending,
        transparent: true
    } );

    linesMesh = new THREE.LineSegments( geometry, material );
    scene.add( linesMesh );

    originalPositions = new THREE.BufferAttribute( linesPositions, 3 );
    originalPositions.copy( linesMesh.geometry.attributes.position );

}

function init() {
    scene = new THREE.Scene();
    //camera = new THREE.OrthographicCamera( width / - 2, width / 2, height / 2, height / - 2, 1, 100 );
    camera = new THREE.PerspectiveCamera( 120, width / height, 1, 1000 );
    camera.position.set( 0, 0, 200 );
    camera.lookAt( 0, 0, 0 );

    // Create matrix
    initMatrix();

    raycaster = new THREE.Raycaster();
    raycaster.linePrecision = 20;

    mouse = new THREE.Vector2();

    renderer = new THREE.WebGLRenderer({ antialias: true });
    renderer.setSize( width, height );
    document.body.appendChild( renderer.domElement );
}

function main() {

    init();
    animate();

}

function animate() {

    requestAnimationFrame( animate );
    render();

}

var intersected = [];

function removeX( i ) { intersected.splice( i, 1 ); }

function alterate( index ) {

    var randColor = ( Math.random() <= 0.49 ) ? rayColor : rayColor2;
    // Change color
    var currentC = new THREE.Color( linesMesh.geometry.attributes.color.getX( index ), linesMesh.geometry.attributes.color.getY( index ), linesMesh.geometry.attributes.color.getZ( index ));
    var tweenC = TweenLite.to( currentC, 1.0, {

        r: randColor.r,
        g: randColor.g,
        b: randColor.b,
        immediateRender: true,
        lazy: false,
        onUpdate: function() {

            linesMesh.geometry.attributes.color.setXYZ( index, currentC.r, currentC.g, currentC.b );
            linesMesh.geometry.attributes.color.needsUpdate = true;

        },
        onUpdateParams: [ index, currentC ]

    });

    // Change coordinates
    var currentXY = { x: originalPositions.getX( index ), y: originalPositions.getY( index ), z: originalPositions.getZ( index ) };
    var goal = [ ( currentXY.x + ( Math.random() * 50 ) * ( ( Math.random() < 0.5 ) ? 1 : -1 ) ), ( currentXY.y + ( Math.random() * 50 ) * ( ( Math.random() < 0.5 ) ? 1 : -1 ) ), ( currentXY.z + ( Math.random() * 50 ) * ( ( Math.random() < 0.5 ) ? 1 : -1 ) ) ];
    var tweenXY = TweenLite.to( currentXY, 1.0, {

        x: goal[ 0 ],
        y: goal[ 1 ],
        z: goal[ 2 ],
        immediateRender: true,
        lazy: false,
        onUpdate: function() {

            linesMesh.geometry.attributes.position.setXYZ( index, currentXY.x, currentXY.y, currentXY.z );    
            linesMesh.geometry.attributes.position.needsUpdate = true;

        },
        onUpdateParams: [ index, currentXY ]

    });

}

function render() {

    // update the picking ray with the camera and mouse position
    raycaster.setFromCamera( mouse, camera );

    intersects = raycaster.intersectObject( linesMesh );

    if( intersects.length ) {

        if( !intersected.includes( intersects[ 0 ].index ) ) {

            for( var x = 0; x < intersects.length; x++ ) {

                if( !intersected.includes( intersects[ x ].index ) ) {

                    var index = intersects[ x ].index;

                    // Save index
                    intersected.push( index );

                    alterate( index );

                }

            }

            var present = 0;

            for( var y = 0; y < intersected.length; y++ ) {

                for( var j = 0; j < intersects.length; j++ ) {

                    if( intersects[ j ].index == intersected[ y ] ) { present = 1; break; }

                }

                if( !present ) {

                    ( function( y ) { 

                        var randC = Math.min( Math.max( ( Math.random() / ( Math.random() * 20.0 ) ), 0.01 ), 0.5 );

                        // Current item old coordinates
                        let index = intersected[ y ];
                        var indexX = originalPositions.getX( index );
                        var indexY = originalPositions.getY( index );
                        var indexZ = originalPositions.getZ( index );

                        removeX( y );

                        // Reset color
                        var currentCreset = new THREE.Color( linesMesh.geometry.attributes.color.getX( index ), linesMesh.geometry.attributes.color.getY( index ), linesMesh.geometry.attributes.color.getZ( index ));
                        var tweenCreset = TweenLite.to( currentCreset, 1.5, {

                            r: randC,
                            g: randC,
                            b: randC,
                            immediateRender: true,
                            lazy: false,
                            onUpdate: function() {

                                linesMesh.geometry.attributes.color.setXYZ( index, currentCreset.r, currentCreset.g, currentCreset.b );
                                linesMesh.geometry.attributes.color.needsUpdate = true;

                            },
                            onUpdateParams: [ index, currentCreset ],
                            onComplete: function() { }

                        } );

                        // Reset coordinates
                        var currentXYreset = { x: linesMesh.geometry.attributes.position.getX( index ), y: linesMesh.geometry.attributes.position.getY( index ), z: linesMesh.geometry.attributes.position.getZ( index ) };
                        var tweenXYreset = TweenLite.to( currentXYreset, 1.5, {

                            x: indexX,
                            y: indexY,
                            z: indexZ,
                            immediateRender: true,
                            lazy: false,
                            onUpdate: function() {

                                linesMesh.geometry.attributes.position.setXYZ( index, currentXYreset.x, currentXYreset.y, currentXYreset.z );    
                                linesMesh.geometry.attributes.position.needsUpdate = true;

                            },
                            onUpdateParams: [ index, currentXYreset ]                       

                        });

                    }) ( y );

                }

            }

        }

    }

    renderer.render( scene, camera );

}

function onMouseMove( event ) {

    // calculate mouse position in normalized device coordinates
    // (-1 to +1) for both components
    event.preventDefault();

    mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
    mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;

    var normX = event.clientX - ( window.innerWidth / 2 );
    var normY = event.clientY - ( window.innerHeight / 2 );

    camera.position.set( 0 + ( normY / 50 ), 0 + ( normX / 50 ), 200 );
    camera.lookAt( 0 + ( normY / 50 ), 0 + ( normX / 50 ), 0 );

}

window.addEventListener( 'click', function() {

    if( intersected.length ) {

        for( var x = 0; x < intersected.length; x++ ) {

            var index = intersected[ x ];

            alterate( index );

        }

    }

});

window.addEventListener( 'mousemove', onMouseMove, false );
// Set new dimensions for scene when resize window
window.addEventListener( 'resize', function() { 
    var wWidth = window.innerWidth;
    var wHeight = window.innerHeight;

    /*camera.left = wWidth / -2;
    camera.right = wWidth / 2;
    camera.top = wHeight / 2;
    camera.bottom = wHeight / -2;*/
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();

    raycaster.setFromCamera( mouse, camera );
    renderer.setSize( window.innerWidth, window.innerHeight );
} );

这是一个Codepen链接:Example