Search Unity

アニメーションインスタンシング – SkinnedMeshRenderer 向けのインスタンシング

, 4月 16, 2018

デベロッパーは常に CPU と GPU の両方のパフォーマンスを意識するものです。高いパフォーマンスの維持は、シーンが大きく複雑になるにつれて、より難しくなります。キャラクターが多数追加されている場合はなおさらです。私と上海の同僚はお客様のサポートを行う中でこの問題に頻繁に直面したため、キャラクターのインスタンシング時のパフォーマンスを向上させるためのプロジェクトに数週間掛けて取り組みました。その結果生み出された技術をアニメーションインスタンシングと名付けました。

屋外のシーンを GPU インスタンシングで実装するのは一般的です。例えば草や木などです。しかし SkinnedMeshRenderer(キャラクターなど)にはインスタンシングを使用できません。何故かと言うと、スキニングは CPU で計算され、ひとつずつ GPU へサブミットされるからです。基本的には、サブミットによって全てのキャラクターを描画することはできません。シーン内に SkinnedMeshRenderer が多数ある場合、ドローコールの数とアニメーション計算の量が多くなります。

私たちは、CPU の負荷を抑えて Unity での GPU インスタンシングをアニメーションインスタンシングで補完する方法を発見しました。GitHub からコードを入手してください。.これは試験的なカスタムのソリューションで、これまでは Unity Enterprise をお使いの一部のお客様へのサポートの一環として限定的に共有されていたものですが、現段階で、より広くフィードバックの受け入れ準備が整いました。是非プロジェクトのページに直接コメントをお寄せください

目標

現時点で、この試験的プロジェクトは以下の事を目標にしています。

  • SkinnedMeshRenderer のインスタンシング
  • できるだけ多くのアニメーション機能を実装する
  • LOD
  • モバイルプラットフォームへの対応
  • カリング

時間的な制約のため、一部の目標は未達成です。対応しているアニメーション機能は、ルートモーション、アタッチメント、およびアニメーションイベントです(遷移とアニメーションレイヤーには未対応です)。また、アニメーションインスタンシングは OpenGL ES 3.0 以降のモバイルプラットフォームでしか機能しません。

しかしながら、今回の実験的なプロジェクトは、このアプローチのもたらす面白い効果を実証する結果となりました。これがどういうことか、具体的に見て行きましょう。

アニメーションの生成

キャラクター向けのインスタンシングを使用するには、事前にアニメーションを生成する必要があります。ここでは、あるキャラクターのアニメーションをテクスチャ内に生成しました。このテクスチャはアニメーションテクスチャと呼ばれます。このテクスチャは GPU 上でスキニングに使用されます。

このジェネレーターが、処理の対象になっているゲームオブジェクトにアタッチされた Animation コンポーネントからアニメーションを取得します。このジェネレーターはアニメーションイベントも取得します。Mecanim システムからアニメーションインスタンシングへ転送すると便利です。キャラクターに何かをアタッチしたい場合、「Attachment setting(アタッチメントの設定)」でアタッチ先のボーンを指定する必要があります。

アニメーションテクスチャの生成が完了すると、アニメーションインスタンシングスクリプトが実行時にアニメーション情報を読み込みます。(注)アニメーション情報とはアニメーションクリップファイルの事ではありません。

インスタンシング

アニメーションインスタンシングは簡単に適用できます。生成されたゲームオブジェクトにアニメーションインスタンシングスクリプトを追加してみましょう。Bone Per Vertex パラメーターは頂点毎に計算されるボーンの数を制御します。ここで覚えておく必要があるのは、ボーンの数が少なくなると、パフォーマンスが向上する一方、精度が下がるということです。

次に、インスタンシングに対応するためにシェーダーを修正する必要があります。ここでは、シェーダーに以下の行を追加するだけです。これはシェーディングには影響しませんが、スキニングに頂点シェーダーを追加します。

パフォーマンスの解析

ここでは Mecanim Example Scenes に含まれるデモシーンの若干修正したバージョンを使用して、そのパフォーマンスを iPhone 6 でテストしました。元々の例とインスタンシング版の例の両方のプロファイラ―表示を詳しく確認していきましょう。

CPU

元々のプロジェクトは、およそ 15 FPS で 300 体のキャラクターを生成します。最低 30 FPS 維持できるようにするには、キャラクターの数を 150 体ほどに制限する必要があります。アニメーションインスタンシングを使用したバージョンでは、30 FPS を維持しながら 900 体のキャラクターを生成できます。

お分かりの通り、CPU での計算がプロジェクトの実行速度を遅くします。

インスタンシングのプロジェクトでは、CPU のアニメーション計算(骨格やスキニングなど)を大幅に削減しました。こうすることで、5~6 倍のキャラクターを生成することができます!

このテストシーンでは、環境の描画に約 80 回のドローコールが必要です。このキャラクターはマテリアルを 3 つ持っています。つまり、1 体のキャラクターのレンダリングに 3 回のドローコールが実行されます。

インスタンシング無しの場合、250 体のキャラクターの生成に約 1100 回のドローコールが必要です(3 回 × 250 体のキャラクター + そのシャドウ)。

アニメーションインスタンシングを使用した場合、800 体のキャラクターの生成後、ドローコールの数は約 50 回しか増加しません。インスタンシングの行を確認すると、バッチ化されたドローコールが 4800、バッチが 48(3 × 8 体のキャラクター + 3 × 8 つのシャドウ)あります。これは、バッチ毎に 100 体のキャラクターをサブミットしているからです。

GPU

この方法は GPU の負荷が若干高くなります。スキニングを GPU で実行するためです。キャラクターにシャドウがある場合、シャドウパス中にキャラクターのスキニングを再度行う必要があります。ただし CPU の負荷が削減されるため、全体的なフレームレートは改善されます。CPU の負荷は一般的にゲームの群衆シミュレーションにおける最大の課題です。

メモリ

アニメーションテクスチャを保存するために、追加でメモリが使用されます。アニメーションテクスチャはスキンのマトリックスを保持します。使用するテクスチャ形式は RGBAHalf です。仮に、ボーンを N 個持つ、各ボーン(各マトリックス)が 4 ピクセルのキャラクターがあって、1 つのアニメーションを M 個のキーフレームで生成するとしましょう。この場合、1 つのアニメーションのコストは N × 4 × M × 2 = 8NM バイトになります(訳注:テクスチャ形式に 1 ピクセルに 2 バイトを使う RGBAHalf を想定しているため、テクスチャに使うバイト数を計算する場合はキーフレーム分のピクセル数に 2 を掛けた値が求める値となります)。
ボーンが 50 あるキャラクターがあって 30 のキーフレームを生成する場合、1 つのアニメーションのコストは 50 × 4 × 30 = 6000 ピクセルになります。つまり、1 つの 1024 × 1024 のテクスチャが最大で 174 個のアニメーションを保存できることになります。

まとめ

SkinnedMeshRenderer が多数ある場合の CPU の負荷は、アニメーションインスタンシングによって大幅に削減されることが分かりました。ゾンビなど、類似した敵の群衆に役立つはずです。

この実験的なプロジェクトが、皆様のプロジェクトにおけるパフォーマンス問題の解決の手掛かりとなり、より精巧なシーンのビルドのために役立つことができれば嬉しく思います。今後も遷移やアニメーションレイヤーへの対応をはじめ、更なる改良を模索・検討しています。

是非 GitHub のコードをご確認のうえ、プロジェクトに直接コメントをお寄せください!

45 コメント

コメントの配信登録

コメント受付を終了しました。

  1. Hello, I was wondering how will we know if you guys update this project? Will you make a new blog post, or will it be some other way? I would like to know if or when you are able to implement animation transitions with this technique. I keep checking back to this page, but I’m not sure if that’s how to keep updated with your development? Thank you.

    1. Hi, thank you for so much concern. I’m working on it now. I suppose I will commit it next month. Something always block me recently. I have to do in my free time. Anyway, I will finish ASAP.

  2. Graham Jones

    6月 5, 2018 11:12 am

    This looks great, Been using Mesh animator, but it’s broken in 2018 – whats the latest version of Unity I could use this in? I’ve put it into 2018, but doesnt seem to run, i get a couple of errors. and the shaders included arent rendering any info, but they do compile

  3. I download the project from github, try this, and its worked great on PC,
    but it didnt worked when I build the project on android, it return exception “InvalidOperationException: Instancing is not supported”

    ps: I already build the assetbundle, and it wordked on unity editor

  4. It says “you cannot bake on ios or android please switch to pc”. But that’s not an option for me, what should I do ?

    1. I’m sorry to confuse you. This is a warning because of history reason. I will remove it later.
      It doesn’t mean you can’t use it on iOS or android. You need to generate the animations on PC or Mac, then build onto iso and android.
      Anyway, I will remove the warning as soon as I can.

      1. Isaac Surfraz

        6月 1, 2018 4:29 pm

        Will you ever do the transitions? Its like the only thing stopping this from being useful + usable to many people

  5. It looks like “Mesh Animator” in assetstore.

    1. It’s different from “mesh animamtor” in technique. But they have the same goal.

  6. Really cool. I’m making an RTS game, and this is a VERY useful thing to have in terms of performance. I’m really looking forward to you guys getting the “transitions” figured out, if you can. Once this technique can do transitions, I will definitely be trying to implement it in my game. Thanks for sharing your great work with us!

  7. Hi! Very cool stuff!
    (-:
    Are lods supported?

    1. Yes, lods are supported.
      The generator will generate the lod group component automatically.

  8. I’ve got this error when opening project from git :
    Unable to open archive file: …Animation-Instancing-master/Assets/StreamingAssets/AssetBundle/animationtexture

    1. it’s ok now…if you got this error, just rebuild assetbundles and all is ok !!

  9. Does this allow full use of an animation graph? Can it blend animations for example?

    1. It can’t blend animations for now. We have not done because of time constraint. Transition and animation layers are our next work, then blend tree will be considered.
      So now I suggest using Mecanim system if you want to blend animations.

  10. Could this work on Animators done in Timeline?

    1. Sure. I think you can custom a playable to support.

  11. I use GPU Instancing in bone animation,but not play animation.
    What should I do?

    1. GPU Instancing can not be used directly for SkinnedMeshRenderer.

      1. OK,, thanks

  12. I honestly think Mecanim Animator should have a little checkbox like “Bake for GPU Vertex animation” and automate the whole process. No need to edit shaders or manage textures or anything. Just check “Bake for GPU Vertex animation” in the animator component, which should create a linked scriptable object based data and work after the baking process.

  13. Nordeus demo use three textures (not one) for animation and limitation (25 anim by one mesh type), mesh instances draw inside job system and use ComputeBuffer with nativearray
    similar technique was presented on GPUGems3
    https://developer.nvidia.com/gpugems/GPUGems3/gpugems3_ch02.html

  14. can i attach an ia to them and use nav mesh ?

    1. Of course you can.

  15. Would be interesting to see performance comparison on PC between built-in SkinnedMeshRenderer, this technique, and Unity Austin demo technique.

  16. Isn’t this just an animated impostors technique?
    What you do is rendering the animation into a sprite sheet, then use the sprite to do fewer draw calls, isn’t it?

    1. No. The characters are not sprites.
      In a few words, we generate the skeleton animations into textures in order to skinning on GPU.
      So we can draw many characters as a batch.

  17. I rolled my own solution like this using the GPU instancing API and a custom palette skinning shader. I guess in future it will be a lot less work for developers.
    In the meantime it was very interesting to learn about, and our game will be released very soon with around 1000 soldiers animated at once. They are also many different characters and soldiers mounted on horse back. I think these things do add to the CPU workload.

    https://youtu.be/-1d0HdstoHw

  18. I do something similar in my asset Mesh Animator, but achieve instancing by display mesh snapshots and can get 5000+ characters at 60fps, GPU skinning was my next step but looks like I don’t have to do it now! https://assetstore.unity.com/packages/tools/animation/mesh-animator-26009

  19. Why unity not support vertex you animation as usual?

  20. This’ll be great once you get Transitions working. Hope work continues on the project :)

  21. Doesn’t Unity already have a “use GPU skinning” checkbox? why not just use that?

    1. I’d like more explanation on that too.

      1. The checkbox “use GPU skinning” is GPU skinning. It puts the skinning calculation on GPU. It saves a little time on CPU. But the key of Animation Instancing is instancing. GPU skinning is a necessary component.
        Note: this gpu skinning is a little from the checkbox “use GPU skinning”.

  22. On 2017.3.1 I get some really bad framerates on a very capable gaming PC (5 fps for 500 characters). I did enable instancing with the in game button. The main culprits seem to be “PreLateUpdate.DirectorUpdateAnimationEnd/Begin”. Disabling rootMotion seems to improve things a bit, but it’s still far from the kind of performance we should expect from this (1000 characters at 20 fps)

    I think if other Unity projects like Ultimate Epic Battle Simulator and the Nordeus demo could have something like 100k units at 30 fps, we should be able to reach that ballpark with such a system

    1. Did you get any errors in console? Please open a issue on GitHub, I will have a look at it.

  23. Mecanim needs to improve.

  24. Alan Mattano

    4月 16, 2018 5:42 pm

    Is it compatible with 2018? Also, Is useful or compatible with Speedtree wind movement?

    1. Yes, it is supported on Unity5.4 or newer. I think it’s enough to Speedtree with gnu instancing.

  25. Sergey Klimenko

    4月 16, 2018 5:34 pm

    Awesome! How do I understand this is the technique that was shown on Unite Austin 2017 in the Spellsouls Universe massive battle demo?

    1. Since the Unite demo was 60000+ units at 60fps, and this here is 900 units at 30fps, I think it’s safe to assume this is not the same technique.

      In the Unite demo they used a different approach that would be completely inaccessible for most of the unity commnity. And it’s not an approach that’s easily “systemifiable” for more accessibility, because it has to be implemented very specifically for your specific use cases

      1. Yes, they are different. But the difference is not the units amount. The analysis in the blog is on Iphone 6, not PC. Of course you can run it on PC to support thousands of thousands units.

    2. I believe that was the ‘Job System’: https://create.unity3d.com/jobsystem

    3. The technique you mentioned on Unite Austin 2017 is C# job system. It’s different from Animation Instancing. But they’re not conflict. You can use both of them on Unity2018. C# Job system is a lower level technique. So we will implement Animation Instancing based on job system on Unity2018.