如何减少浏览器中的Three.js CPU / GPU使用率

时间:2018-07-11 09:27:03

标签: performance typescript three.js webgl gpu

此刻,我有一个旋转的动画地球仪,地球仪上的点随机改变颜色。它可以正常工作,但如果留在后台,则会大大减慢我的笔记本电脑的速度。我是否可以进行任何更改以减少正在使用的内存量?

在Chrome的任务管理器中,该选项卡处于活动状态时,我看到它正在使用12%的CPU和128MB的GPU内存。 Three.js正常吗?还是需要更改代码?

    ngAfterViewInit() {
    if(this.enabled) {
        this.controls = new OrbitControls(this.camera, this.renderer.domElement);
        this.controls.rotateSpeed = 0.5;
        this.controls.enableDamping = true;
        this.controls.dampingFactor = 0.5;
        this.controls.rotationSpeed = 0.3;
        this.controls.enableZoom = false;
        this.controls.autoRotate = true;
        this.controls.autoRotateSpeed = -1;

        this.renderer.setSize(window.innerWidth, window.innerHeight);
        this.rendererContainer.nativeElement.appendChild(this.renderer.domElement);
        this.animate();
        const timerId = setInterval(() => this.updateColor(), 650);
    }
}

private get enabled(): boolean {
    if(this._enabled!==undefined) {
        return this._enabled;
    }
    const canvas = document.createElement("canvas");
    const gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
    this._enabled = gl && gl instanceof WebGLRenderingContext;
    return this._enabled;
}

private initGlobe(): void {
    this.scene = new THREE.Scene();
    this.camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 1000);
    this.camera.position.set(0, 5, 15);
    this.camera.lookAt(this.scene.position);

    this.renderer = new THREE.WebGLRenderer({
        antialias: true
    });
    this.renderer.setClearColor('rgb(55, 44, 80)');

    this.geom = new THREE.SphereBufferGeometry(6, 350, 90);
    this.colors = [];

    this.color = new THREE.Color();
    this.colorList = ['rgb(123, 120, 194)'];
    for (let i = 0; i < this.geom.attributes.position.count; i++) {
        this.color.set(this.colorList[THREE.Math.randInt(0, this.colorList.length - 1)]);
        this.color.toArray(this.colors, i * 3);
    }
    this.geom.addAttribute('color', new THREE.BufferAttribute(new Float32Array(this.colors), 3));
    this.geom.addAttribute('colorRestore', new THREE.BufferAttribute(new Float32Array(this.colors), 3));

    this.loader = new THREE.TextureLoader();
    this.loader.setCrossOrigin('');
    this.texture = this.loader.load('/assets/globe-dot.jpg');
    this.texture.wrapS = THREE.RepeatWrapping;
    this.texture.wrapT = THREE.RepeatWrapping;
    this.texture.repeat.set(1, 1);
    const oval = this.loader.load('/assets/circle.png');

    this.points = new THREE.Points(this.geom, new THREE.ShaderMaterial({
        vertexColors: THREE.VertexColors,
        uniforms: {
            visibility: {
                value: this.texture
            },
            shift: {
                value: 0
            },
            shape: {
                value: oval
            },
            size: {
                value: 0.4
            },
            scale: {
                value: 300
            }
        },
        vertexShader: `
              uniform float scale;
              uniform float size;

              varying vec2 vUv;
              varying vec3 vColor;

              void main() {

                vUv = uv;
                vColor = color;
                vec4 mvPosition = modelViewMatrix * vec4( position, 0.99 );
                gl_PointSize = size * ( scale / length( mvPosition.xyz )) * (0.3 + sin(uv.y * 3.1415926) * 0.35 );
                gl_Position = projectionMatrix * mvPosition;

              }
        //   `,
        fragmentShader: `
              uniform sampler2D visibility;
              uniform float shift;
              uniform sampler2D shape;

              varying vec2 vUv;
              varying vec3 vColor;

              void main() {

                vec2 uv = vUv;
                uv.x += shift;
                vec4 v = texture2D(visibility, uv);
                if (length(v.rgb) > 1.0) discard;

                gl_FragColor = vec4( vColor, 0.9 );
                vec4 shapeData = texture2D( shape, gl_PointCoord );
                if (shapeData.a < 0.0625) discard;
                gl_FragColor = gl_FragColor * shapeData;
              }
          `,
        transparent: false
    }));

    this.points.sizeAttenuation = false;
    this.scene.add(this.points);

    this.globe = new THREE.Mesh(this.geom, new THREE.MeshBasicMaterial({
        color: 'rgb(65, 54, 88)', transparent: true, opacity: 0.5
    }));
    this.globe.scale.setScalar(0.99);
    this.points.add(this.globe);
    this.scene.add(this.globe);
}

animate() {
    this.controls.update();
    this.renderer.render(this.scene, this.camera);
    this.animationQueue.push(this.animate);
    window.requestAnimationFrame(_ => this.nextAnimation());

}

nextAnimation() {
    try {
        const animation = this.animationQueue.shift();
        if (animation instanceof Function) {
            animation.bind(this)();
        }
    } catch (e) {
        console.error(e);
    }
}

updateColor() {
    for (let i = 0; i < this.usedIndices.length; i++) {
        let idx = this.usedIndices[i];
        this.geom.attributes.color.copyAt(idx, this.geom.attributes.colorRestore, idx);
    }

    for (let i = 0; i < this.pointsUsed; i++) {
        let idx = THREE.Math.randInt(0, this.geom.attributes.color.count - 1);
            if (idx%5 == 0 && idx%1 == 0)  {
                this.geom.attributes.color.setXYZ(idx, 0.9, 0.3, 0);
            }
            else {
                this.geom.attributes.color.setXYZ(idx, 1, 1, 1);
            }
        this.usedIndices[i] = idx;
    }

    this.geom.attributes.color.needsUpdate = true;

我查看了其他建议合并网格的问题,但不确定在这里是否可行。谢谢!

1 个答案:

答案 0 :(得分:1)

这取决于您所说的“背景”

如果用“背景”表示“不是最前面的选项卡”,那么,如果您使用的是requestAnimationFrame,那么您的页面不是浏览器的最前面的选项卡,或者如果您将页面最小化,浏览器窗口浏览器将停止向您发送动画帧事件,并且您的页面应完全停止。

如果通过“背景”来表示前选项卡,但是它是未最小化且也不是前窗口的窗口,则可以使用blurfocus事件来完全停止页面。

示例:注意:模糊事件在iframe中似乎不起作用,因此在下面的代码段中将不起作用,但是如果将其复制到文件中,它将起作用

let requestId;

function start() {
  if (!requestId) {
    requestId = requestAnimationFrame(animate);
  }
}

function stop() {
console.log('stop');
  if (requestId) {
    cancelAnimationFrame(requestId);
    requestId = undefined;
  }
}

const ctx = document.querySelector("canvas").getContext('2d');

function animate(time) { 
  requestId = undefined;
  
  ctx.save();
  ctx.translate(
    150 + 150 * Math.cos(time * 0.001), 
     75 +  75 * Math.sin(time * 0.003),
  );
  ctx.scale(
     Math.cos(time * 0.005), 
     Math.cos(time * 0.007),
  );
  ctx.fillStyle = `hsl(${time % 360},100%,50%)`;
  ctx.fillRect(-50, 50, 100, 100);
  ctx.restore();
  
  start();
}

start();

window.addEventListener('blur', stop);
window.addEventListener('focus', start);
body { margin: 0; }
canvas { width: 100vw; height: 100vh; display: block; }
<canvas></canvas>

当然,除了完全停止blur之外,您还可以控制自己的应用。仅每第5帧渲染一次或渲染更少的东西,等等...