Performance Three.js-精灵/文本标签性能

Performance Three.js-精灵/文本标签性能,performance,three.js,textures,sprite,Performance,Three.js,Textures,Sprite,有一个包含一些3D对象和200-300个小文本标签的three.js场景(

有一个包含一些3D对象和200-300个小文本标签的three.js场景(<10%在一个透视图上对摄影机可见)。添加文本精灵可以将FPS从60降低到30-40,而且还非常消耗内存

有没有办法让精灵跑得更快? 我读过关于缓存材料的文章,但是标签都是唯一的,所以这是不可能的

测试: (您可以更改SPRITE_计数以查看机器上的FPS下降)

编辑1:将画布大小设置为文本边界将减少内存消耗,但不会提高FPS

var Test = {
    SPRITE_COUNT : 700,
    init : function() {

        this.renderer = new THREE.WebGLRenderer({antialias : true}); // false, a bit faster without antialias                   
        this.renderer.setPixelRatio(window.devicePixelRatio);
        this.renderer.setSize(window.innerWidth, window.innerHeight);
        this.container = document.getElementById('display');
        this.container.appendChild(this.renderer.domElement);
        this.camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);
        this.scene = new THREE.Scene();
        this.group = new THREE.Object3D();
        this.scene.add(this.group);
        for (var i = 0; i < this.SPRITE_COUNT; i++) {
            var sprite = this.makeTextSprite('label ' + i, 24);
            sprite.position.set(Math.random() * 20 - 10, Math.random() * 20 - 10, Math.random() * 20 - 10);
            this.group.add(sprite);
        }

        this.stats = new Stats();
        this.stats.domElement.style.position = 'absolute';
        this.stats.domElement.style.left = '0px';
        this.stats.domElement.style.top = '0px';
        document.body.appendChild(this.stats.domElement);

        this.render();
    },

    render : function() {

        var self = this;

        this.camera.rotation.x += 0.002;
        this.renderer.render(this.scene, this.camera);
        this.stats.update();

        requestAnimationFrame(function() {self.render();});
    },

    makeTextSprite : function(message, fontsize) {
        var ctx, texture, sprite, spriteMaterial, 
            canvas = document.createElement('canvas');
        ctx = canvas.getContext('2d');
        ctx.font = fontsize + "px Arial";

        // setting canvas width/height before ctx draw, else canvas is empty
        canvas.width = ctx.measureText(message).width;
        canvas.height = fontsize * 2; // fontsize * 1.5

        // after setting the canvas width/height we have to re-set font to apply!?! looks like ctx reset
        ctx.font = fontsize + "px Arial";        
        ctx.fillStyle = "rgba(255,0,0,1)";
        ctx.fillText(message, 0, fontsize);

        texture = new THREE.Texture(canvas);
        texture.minFilter = THREE.LinearFilter; // NearestFilter;
        texture.needsUpdate = true;

        spriteMaterial = new THREE.SpriteMaterial({map : texture});
        sprite = new THREE.Sprite(spriteMaterial);
        return sprite;   
    }
};

window.onload = function() {Test.init();};
var测试={
雪碧计数:700,
init:function(){
this.renderer=new THREE.WebGLRenderer({antialias:true});//false,在没有antialias的情况下会快一点
this.renderer.setPixelRatio(window.devicePixelRatio);
this.renderer.setSize(window.innerWidth、window.innerHeight);
this.container=document.getElementById('display');
this.container.appendChild(this.renderer.doElement);
this.camera=新的3.PerspectiveCamera(45,window.innerWidth/window.innerHeight,11000);
this.scene=新的三个.scene();
this.group=new THREE.Object3D();
this.scene.add(this.group);
对于(var i=0;i
你是对的,three.js确实导致GPU以这种方式使用大量纹理内存。请记住,发送到GPU的每个纹理都必须高到宽,因此为每个精灵制作一个画布将浪费大量内存

这里的一个挑战是three.js设计选择在
纹理上有一组UV坐标
;即使将标签精灵组合到单个纹理贴图中,并且无需额外努力即可为每个材质创建
.clone()
纹理,它仍会将每个
纹理
发送到GPU,而无需共享内存。简言之,它目前没有文档记录的方法来告诉它这些纹理是相同的,并且您不能将每个
材质
指向相同的
纹理
,因为保存UV的不是
材质
。讨论这个问题

我通过在一个或多个纹理贴图中组合精灵来解决这些问题。为此,我创建了一个“sprite纹理图集管理器”,它可以根据需要管理sprite纹理的分配,并且我将一个背包算法移植到JS,它可以帮助我(主要)用标签填充这些纹理贴图,这样就不会浪费太多内存

我已经在一个单独的库中提取了这方面的代码,在这里可以找到:有一个实例(它还没有使用sprite,但应该很容易添加)

幸运的是,虽然这还没有被记录下来,但我也发现在最近的版本中(至少r73,也许还有r72),通过确保纹理都具有相同的
.uuid
值来强制纹理共享GPU内存是相当容易的。我的库确保利用了这一点,在我目前的测试中(使用2048x2048 sprite贴图;我只需要两个与我渲染的大小相同的贴图),这将GPU内存从未共享时的~2.6GB降低到共享时的~300-600MB。(2048px在仅放置单个标签时太大,而当贴图不共享时,减小纹理大小有很大帮助)


最后,根据您自己的回答,drawcalls和culling在r73中也是一个性能问题。不过,我从来没有遇到过这个问题,因为我已经通过对所有内容进行分组来批处理我的draw调用。

你是对的,three.js确实导致GPU以这种方式使用大量纹理内存。请记住,发送到GPU的每个纹理都必须高到宽,因此为每个精灵制作一个画布将浪费大量内存

这里的一个挑战是three.js设计选择在
纹理上有一组UV坐标
;即使将标签精灵组合到单个纹理贴图中,并且无需额外努力即可为每个材质创建
.clone()
纹理,它仍会将每个
纹理
发送到GPU,而无需共享内存。简言之,它目前有
<script src="https://rawgit.com/mrdoob/three.js/dev/build/three.js"> </script>