2015-10-16 28 views
1

有一个three.js所场面与一些3D对象和200 - 300小的文本标签(< 10%是相机可见的一个观点)。添加文本精灵将FPS从60降低到30 - 40,并且它也非常耗费内存。three.js所 - 雪碧/文本标签性能

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

测试: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();}; 
+1

如果我不得不冒险猜测,我会说three.js是做每个精灵标签1 drawcall。从性能角度来看,最好将所有精灵标签一起批量打包成一个批次并用一次调用进行绘制。 –

回答

2

这在three.js所WebGLRenderer的错误(缺少精灵视锥检查在< = R73)。它已经在开发分支中得到了修复。所以你可以期望它在r74中可用。
问题和详细信息https://github.com/mrdoob/three.js/issues/7371

固定的版本与最新发布的开发建设:https://jsfiddle.net/h9sub275/9/
性能测试与R73:https://jsfiddle.net/h9sub275/7/
(点击查看之间手动删除看不见的精灵,而不是删除它们的性能差异)

最新开发构建:

<script src="https://rawgit.com/mrdoob/three.js/dev/build/three.js"> </script> 
4

你是对的,three.js的确让GPU以这种方式使用了很多纹理内存。请记住,发送给GPU的每个纹理必须与宽度一样高,所以通过为每个sprite制作一个画布,您将浪费大量内存。

这里的挑战是three.js所设计的选择,有一组UV坐标上Texture;即使将单个纹理贴图中的标签精灵和每个材质的贴图组合在一起,也无需额外费力,它仍然会将每个Texture发送到GPU而不共享内存。简而言之,它目前没有任何记载的方式告诉它这些纹理是相同的,并且你不能指出每个Material在相同的Texture,因为它不是保持UV的Materialhttps://github.com/mrdoob/three.js/issues/5821讨论了这个问题。

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

我已经在单独的图书馆中提取了我的代码,它可以在这里找到:https://github.com/Leeft/three-sprite-texture-atlas-manager有一个生动的示例(它尚未使用精灵,但应该很容易添加)在http://jsfiddle.net/Shiari/sbda72k9/。幸运的是,虽然这还没有记录,但我还发现,在最近的版本(至少是r73,或许是r72),现在很容易强制纹理共享GPU内存,通过确​​保它们都具有价值相同.uuid。我的库肯定会利用这一点,并且在目前为止的测试中(使用2048x2048 sprite贴图;我只需要渲染大小的那些贴图中的两个),这会使GPU内存在不共享时从2.6GB降低在共享时约为300-600MB。 (当你仅仅放置一个标签时,2048px太大了,当地图不共享时,缩小纹理大小非常有帮助)。

最后,根据你自己的答案,拉格和剔除也是r73中的一个性能问题。虽然我从来没有遇到过这个问题,因为我已经通过将所有事情分组来完成我的平局。

+0

谢谢您的建议!如果我找到时间,我会尝试进一步改进。原来,在three.js WebGLRenderer中有一个错误(缺少视锥体检查锥体,请参见下一个答案)BTW:cool star map :) – Oliver

+0

当然可以。我相当进步,使我的代码一个单独的库,这将使建立一个纹理地图更容易为任何人,我会更新我的答案,当它准备好了。就我个人而言,我一直批量生产我的标签精灵,GPU内存一直是我的性能元凶(而且这会影响手机比什么都重要)。虽然也很好地了解扑杀问题。 – Leeft

+0

我已经更新了我的答案,因为我的图书馆现在可用,如果您想使用它。希望有所帮助! – Leeft