在three.js中提高动画精灵的性能

6

现在我正在制作一个在线游戏,为了性能考虑决定使用WebGL而不是HTML5的canvas。我使用three.js框架,我的意图是拥有移动的动画精灵。精灵本身放置在精灵表上,并使用UVOffset和UVScale来使用正确的艺术品,并在时间流逝时切换Sprite的艺术品。我想知道是否有一种方法可以改善这段代码的性能,因为现在它在同时存在约300个“玩家”时开始变慢。

问候

以下是我的代码中最重要的部分:

var image = THREE.ImageUtils.loadTexture( "img/idlew.png" );

  function addPlayer(){
    var mesh = new THREE.Sprite({map:image});//, affectedByDistance: false, useScreenCoordinates: false});
    images.push(mesh);
    mesh.position.set(Math.floor(Math.random() * 500), Math.floor(Math.random() * 500), 10);
    scene.add(mesh);
    mesh.uvScale.x = 96/1152;
    mesh.scale.x = 0.1;
    mesh.scale.y = 0.1;
  }


var imgBackground  = new THREE.MeshLambertMaterial({
      map:THREE.ImageUtils.loadTexture('img/grass.jpg')
  });


   var background = new THREE.Mesh(new THREE.PlaneGeometry(1000, 1000),imgBackground);


  scene.add(background);



  scene.add(camera);
camera.rotation.x = -(Math.PI/2);
scene.add(new THREE.AmbientLight(0xFFFFFF));

addPlayer();

  renderer.render(scene, camera);
  var moveUp = false;
  tick();
  var ticker = 0;
  var usedOne = 0;

  function tick(){
    ticker++;
    if(ticker%10==0){
      for (var i = 0; i < images.length; i++) {
        images[i].uvOffset.x = usedOne * 0.0835;
      };
        usedOne++;
        if(usedOne == 12) usedOne = 0;
        addPlayer();
        addPlayer();
        addPlayer();
        console.log(images.length);
    }
    requestAnimationFrame( tick );

      renderer.render(scene, camera);
  }

1
欢迎来到 Stack Overflow,很高兴您解决了问题!顺便说一句,如果您在下面的答案部分回答自己的问题,并(经过短暂等待后)将其标记为被接受的答案是完全可以接受的。这有助于未来可能遇到同样问题的其他人更快地找到解决方案。 - Toji
2个回答

9
我写了一个展示动画纹理的代码示例,实时演示页面如下:http://stemkoski.github.com/Three.js/Texture-Animation.html。同时,源代码也可以在以下链接中找到:http://github.com/stemkoski/stemkoski.github.com/blob/master/Three.js/Texture-Animation.html
其中有一个非常有用的函数,我编写了它来自动处理偏移量。这个函数(从上面的链接中提取)如下:
function TextureAnimator(texture, tilesHoriz, tilesVert, numTiles, tileDispDuration) 
{   
    // note: texture passed by reference, will be updated by the update function.

    this.tilesHorizontal = tilesHoriz;
    this.tilesVertical = tilesVert;

    // how many images does this spritesheet contain?
    //  usually equals tilesHoriz * tilesVert, but not necessarily,
    //  if there at blank tiles at the bottom of the spritesheet. 
    this.numberOfTiles = numTiles;
    texture.wrapS = texture.wrapT = THREE.RepeatWrapping; 
    texture.repeat.set( 1 / this.tilesHorizontal, 1 / this.tilesVertical );

    // how long should each image be displayed?
    this.tileDisplayDuration = tileDispDuration;

    // how long has the current image been displayed?
    this.currentDisplayTime = 0;

    // which image is currently being displayed?
    this.currentTile = 0;

    this.update = function( milliSec )
    {
        this.currentDisplayTime += milliSec;
        while (this.currentDisplayTime > this.tileDisplayDuration)
        {
            this.currentDisplayTime -= this.tileDisplayDuration;
            this.currentTile++;
            if (this.currentTile == this.numberOfTiles)
                this.currentTile = 0;
            var currentColumn = this.currentTile % this.tilesHorizontal;
            texture.offset.x = currentColumn / this.tilesHorizontal;
            var currentRow = Math.floor( this.currentTile / this.tilesHorizontal );
            texture.offset.y = currentRow / this.tilesVertical;
        }
    };
}       

您可以使用以下方式初始化材料(例如):
var runnerTexture = new THREE.ImageUtils.loadTexture( 'images/run.png' );
// a texture with 10 frames arranged horizontally, display each for 75 millisec
annie = new TextureAnimator( runnerTexture, 10, 1, 10, 75 ); 
var runnerMaterial = new THREE.MeshBasicMaterial( { map: runnerTexture } );

在每次渲染之前使用以下方法更新它:

var delta = clock.getDelta(); 
annie.update(1000 * delta);

希望这可以帮到您!

这绝对是一种不错的处理自动更改偏移量的方式;然而,我真的看不出这如何能使渲染更快。现在我已经达到了应用程序的上限为20fps(那是最佳点),精灵每帧都在移动(x和y值),动画每10次移动就会更改(偏移量更改)。每次帧更改时重新渲染。我现在遇到的问题是,当我的场景中有太多精灵时,渲染速度变得太慢了...当有100个精灵在移动并在移动相机时改变UV偏移量时,渲染速度变得极慢。 - Kristof
+1 我用精灵(476个)填充了我的视口,所有精灵都具有透明度和8帧动画。使用这种方法,我仍然可以获得30+ fps。谢谢! - Chad
嗨。我又写了一个相同想法的实现,并将其打包为模块。我也使用了UV偏移,但将所有计算移动到着色器中。你觉得怎么样?https://github.com/elephanter/AnimatedSprites - Elephant
使用ImageMagick的标准几何图形将帧合并为单个精灵表时,需要从左到右、从上到下阅读。为了使其兼容,我在更新方法中修改了Y位置:texture.offset.y = 1 - (currentRow / this.tilesVertical) - (1 / this.tilesVertical); - crowjonah
@crowjonah - 是的,我注意到了,你的代码很有用,谢谢。此外,李的爆炸jpg文件从右下角到左上角具有子图像序列,但他的代码期望序列从左下角到右上角运行。结果动画不够流畅。 - steveOw

2
我意识到通过将

标签移至开头,代码可以大幅改进。
renderer.render(scene, camera); 

将代码放在正确的位置,这样只有在需要时才会被调用


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接