Three.js - Sprite / 文本标签性能

Oli*_*ver 1 performance textures sprite three.js

有一个带有一些 3D 对象和 200 - 300 个小文本标签的 Three.js 场景(< 10% 在一个角度对相机可见)。添加文本精灵将 FPS 从 60 降低到 30 - 40 并且它也非常消耗内存。

有没有办法让精灵更快?我读过缓存材料,但标签都是独一无二的 - 所以这是不可能的。

测试:https : //jsfiddle.net/h9sub275/4/(您可以更改 SPRITE_COUNT 以查看您机器上的 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();};
Run Code Online (Sandbox Code Playgroud)

Lee*_*eft 5

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

这里的一个挑战是 Three.js 设计选择在 ; 上有一组 UV 坐标Texture;即使您将标签精灵组合在一个纹理贴图中,并且您.clone()无需付出额外的努力就可以为每个材质创建纹理,它仍然会Texture在不共享内存的情况下将每个精灵发送到 GPU。简而言之,它目前没有记录在案的方法来告诉它这些纹理是相同的,并且您不能将每个指向Material相同,Texture因为它不是Material保持 UV 的那个。https://github.com/mrdoob/three.js/issues/5821讨论了这个问题。

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

我已经在一个单独的库中提取了我的代码,它可以在这里找到:https : //github.com/Leeft/three-sprite-texture-atlas-manager带有一个活生生的例子(它还没有使用精灵,但这应该很容易添加)在http://jsfiddle.net/Shiari/sbda72k9/

幸运的是,虽然这还没有记录在案,但我也发现在最近的版本中(至少是 r73,也许 r72)通过确保纹理都具有相同的.uuid值来强制纹理共享 GPU 内存是很容易的。我的库确保利用这一点,并且在我迄今为止的测试中(使用 2048x2048 精灵图;我只需要其中两个与我渲染的大小相同的),这使 GPU 内存在不共享时从 ~2.6GB 降低共享时增加到 ~300-600MB。(2048px 太大了,当你只在那里放置一个标签时,当地图没有共享时,减小纹理大小有很大帮助)。

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