今回はスキニングを使って同じものをたくさん出す処理について書きます。シーンにSkinnedMeshRendererが一つしかなく、DrawCallも基本(影やZプリパスは別)一回です。GameObjectもシーンに6つしかありません。数が増えてもGameObjectを増やさずに済みます。ソースコードはサンプルの形でgithubに置いてあります。破片を散らす、草を生やす、等々、ほとんど同じものを違う場所にたくさん出したくなることはよくあります。方法は一つではありません。MAYAやBlender上で、そもそもたくさん複製して配置まで済ませておく何の制御もいらないので楽だが、メモリを食い、一個一個を独立に動かすことができない。MeshRendererをつけたGameObjectを単純にたくさん置く何の工夫も必要ないので楽。batchingが有効であればUnityがうまくまとめてくれるかもしれないが、CPU負荷がかかる。GameObjectやTransformの処理負荷がCPU側にかかる。GPUインスタンシング例えば100頂点の石をたくさん置く場合、頂点バッファを100頂点ごとにループさせつつ、ループの度に違う頂点データを与える、ということができる機能。メモリが小さく、頂点単位の計算はGPUで完結するので速い。GLES3以上が必須。シェーダの対応が必要。標準の対応済みシェーダであればマテリアルのスイッチをいじるだけ。Unity的にお手軽に利用する場合、GameObjectやTransformの処理負荷は残る。1ボーンスキニングをインスタンシング代わりにする頂点を複製するのでメモリを食い、初期化にも時間を食う。GPUインスタンシングよりも頂点シェーダの負荷が大きい。Unityの場合シェーダの対応が不要。GameObjectやTransformを使わないで書けるのでCPU負荷を削れる。普通に考えて、新し目の機械だけ相手にするならGPUインスタンシングが最強でしょう。メモリ効率、CPU負荷、GPU負荷、の全てで最強ですし、物ごとに色やUVを変えられるなど、より大きな自由度があります。広い範囲のお客さんに遊んでもらいたいのであれば、切り捨てるのは惜しい印象です。自作シェーダをインスタンシングに対応させるにはそれなりに知識と手間も必要でしょう。単純にGameObjectをたくさん置けば何の工夫もいりません。プレハブをInstantiateして作るでしょう。初期化による処理スパイクは気になります。使える局面もあるかもしれません。用意するもの用意するものは以下です。SkinnedMeshRendererたくさん描きたいメッシュサンプルでは標準のキューブを見えない所に置いて、実行時にMeshFilter.sharedMeshで抜いて使っている好きなマテリアルSkinnedMeshRendererは描画の本体です。単純に「頂点ごとに別の行列で動かす」という用途に使うことができます。入ったメッシュの生成Meshクラスをnewして、元のメッシュの頂点を複製しながら詰めます。2400頂点のメッシュを作ることになります。位置、法線、UV、BoneWeightの配列を作ります。データで、例えば「0番を20%、2番を40%、5番を40%」のようなことを書くデータです。「24番を100%使う」というような簡単な指定になります。最後のposesは行列の配列で、これは物の数だけ存在します。インデクスは元のインデクスをズラしながら書き込みます。いうことであれば、物の番号(i)が進む度に100づつ値を足せばいいわけです。最後にBoneWeightに詰めます。物の番号であるiを入れ、weight0は1にしておきます。ここの処理は頂点数に比例するので、それなりに重い処理です。なければ画面が黒いうちにやっておくのが良いでしょう。いう手もあります(ファイル容量が増えるので、おすすめはしません)。Meshに諸々を設定します。これでMeshの初期設定は終わりです。vertices,normals,uv,triangles,boneWeightsはもう触りません。そのため、物が増えてもCPU負荷はさして増えないわけです。次にSkinnedMeshRendererの設定をします。transforms;sharedMeshにさっき作ったMeshを指定します。今回は「全部同じTransformが入った配列」を渡します。Transformを渡すのですが、今回は全部根本のTransformをつっこんでおきます。bindposesの方を自力で差し換えて動かせばTransformは全部一緒でもやりたいことはできるわけです。これによって、物の数だけGameObjectとTransformを用意する必要がなくなります。毎フレームやることあとは、Mesh.bindposesにつっこむ行列を毎フレーム差し換えて動かします。若干GC的に気にはなりますね。毎フレーム128kBのGCAllocが発生することになります。これが問題になるようであれば、Transformを物の数だけ用意する方が良いのかもしれません。いう感じのインターフェイスにしています。行列を生成できるので、行列演算の詳細を知る必要はありません。サンプルでは物理シミュレーションをしてクォータニオンを積分しながらクルクル回しています。2018(13インチ)のエディタ実行にて測定を行いました。順に、単にGameObjectたくさん、その上でGPUインスタンシング有効、そして今回のスキニング、です。雑に見て、4.5ms、3ms、2msといったところでしょうか。思ったほどには遅くもない気がします。GameObjectやTransformがたくさんあっても、それだけならそんなに遅くはないのでしょう。それでも全く使い物にならないというほどでもありません。下層がMetalだからでしょう。下がGLだったらもっと悲惨なことになる気はします。3桁を大きく超える数を出すなら、多少の工夫はしておいた方が無難かなと思えます。instancingを有効にしたものです。DrawCallが20回以下にまで激減し、描画処理スレッドの負荷は大きく減ります。予想していたほどではありません。まとめる処理は必要なはずで、それなりの負荷は残ってしまうのでしょう。決めておければ最速になるはずです。最後が今回の手法で、一応この条件では最速となりました。何をまとめて描画するかは私が手動で決めているので、余計な負荷がないのです。一応他のスレッドも含めて見てみましょうか。1つ目がGameObjectをただ置いただけのもの、2つ目が今回のスキニングです。2つ目は字が入らないくらいの幅(1ms以下)しかありません。大半の時間はWaitForTargetFPSで寝ています。スキニングについてUnityはスキニングを描画の前に済ませてしまう方法を採用しています。事前にスキニング計算だけを済ませてしまった方が合計計算量が減ります。GLES2では、CPUでスキニング計算をしてから描画を行います。今回の技法は頂点数に比例したCPU負荷が発生するはずです。フレームあたり数万頂点までは楽に耐えられるでしょう。1ボーンなので計算量はかなり小さくなります。いうフローになっているものと思われます。Feedbackと呼びます。コンピュートシェーダを用いてスキニングを行っているようです。ProでFrameDebuggerを見てみると、ComputeShaderが動いているように見えます。「GPUでやるので十分に速い」という程度に思っておいて良いでしょう。GPUでやる場合の制約さてここで問題になるのは、「物はいくつまで出せるのか?」です。ボーンの数、つまり行列の数の限界はいくつか、ということが問題になります。見たように、PCでは2048個の物を出しても問題なく動きます。2048個以上の行列をGPUに送れる、ということです。64バイト(ケチれば48バイト)を2048個送れば、128kBにもなります。そんな大きなものをどうやってGPUに送っているんでしょう?きっとテクスチャでしょうね。ないことがありました。時代が進んでも70個か80個が限界の時代がありました(Vector4が256個)。頂点シェーダでのテクスチャ読み出しは遅くなりがちです。なんてことがあると安心して使えないわけですが、そのへんはどうなっているんでしょうね。テクスチャなのかなとは思うのですが。行列数以外に、頂点数にも制約があります。インデクスが16ビットである場合、頂点番号の最大値は65535です。しれない、ということです。実際本サンプルでは2700個くらいが限界です。掛けると64800頂点となって限界付近であることがわかります。これを超えると途中までしか絵が出なくなります。キューブならともかく、標準の球は500頂点もあるので、128個ですらあふれてしまいます。私はやったことはありません。頂点数の制約はありません。行列数の制約も違ってきます。終わりに物をたくさん出したいというのは、ゲーム作りで頻繁に出てくる欲求です。思ったほどには性能が出なかったりもします。使うやり方を紹介してみました。「行列だけ送ればいいので速い」という話になりません。いうことにもなります。スキニングなりインスタンシングなりが向いている気がします。2019の2日目の記事です...2019-09-13メッシュを.objにしてUnityから出す画面写真をクリックするとサンプルのWebGLビルドに飛びます。画像をクリックするとWebGLビルドに飛びます。(1)月別アーカイブ検索スターをつけました引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。