VRChat東雲めぐ夏祭りワールド技術メモ 負荷対策編

概要

東雲めぐ夏祭りで使用した技術周りの情報をまとめようと思って始めたんですが、書きたいこと多かったのでいくつかに分けようかと思います。今回は負荷対策を中心に紹介します。

想定読者は、VRChatのワールド作成を始めてある程度Unityを使えるようになった人あたりです。細かい手順などは深く説明してませんが、私はこんな方法でワールド作成しましたって感じの記事なので、足りない部分は各自補完しながら読んでいただければと思います。

負荷対策

主な対策としては以下のようなことをしました。

  • 花火Particleの厳選
  • LightのBake
  • Occlusion Culling
  • TerrainのObject化
  • アセットの圧縮
  • MeshBaker

負荷の調べ方

どれも使い方によっては効果の高い負荷対策となります(particleの厳選は汎用性が低い)。
負荷調整時では基本的にGameビューのstatsを見ながらSetPass Callsを減らしていく作業をしてました。
80%
簡単にいうと、SetPass callsの数字が少ないほど描画負荷が軽いってことになります。
なぜ軽くなるのか、どうしたら軽くなるのか、辺りのことは以下の記事が参考になります。

また、より細かく、具体的にどんな処理がどのタイミングで負荷をかけているのかはProfilerを見ることで調べることができます。
Window > Profiler から表示
stats_sample.png
ProfilerではUnity内でのフレームごとの各処理にどれだけ時間やリソースを使っているかが確認できます。
profile_sample_1.png
画像は対策後のものですが、ワールド制作初期の段階ではCPU処理の負荷の大半がパーティクルの描画に使用されていました。
そのため、ワールド内で常時複数種類の花火を打ち上げていたのを、負荷の軽いものだけを限定して使用するように変更しました。
ここで最も効果が高かったのは、花火particle内のサブエミッターに大量にセットされていたオブジェクトをカットしたことでした。

花火周り

花火アセットが重かったので、調整して必要な部分だけ使いました。
起動すると一瞬ワールドが止まるレベルのスパイク負荷が発生していましたが、負荷の原因となっていた大量のサブエミッターを消すことで回避できました。
サブエミッターの代わりに、Unlit/Transparent Cutoutのシェーダーを割り当てたイラストを花火として打ち上げました。
Post Processing StackのBloom効果のおかげで、イラストも空に打ち上げると微妙に発光していいかんじになりました。
また、常時打ち上げる花火のために、別のアセットも併用しました。アセットストアで見つけた花火アセットがUnityバージョン2017を要求しており、VRChatで使えるUnityと不一致だったのでダウングレードして使いました。対応してるUnityバージョンで開いてUnityPackageでexportし、5.6.3p1のUnityで開くだけ。今回はそれだけで使用可能だったのでラッキーでした。

LightのBake

テラシュールブログさんの記事とUnityのリファレンスを読みながら進めました(テラシュールブログさんにはいつもお世話になってます)。

ベイク時間の短縮はこちらの記事を参考にしています。

基本的にリアルタイムライトの処理は負荷が高いので、directional lightを一つだけ残して全てBakedのライトにしています。
Light Probe Groupは自分もあまりよくわかってませんが、光源にLightProbeを設置するのと、光の影響の境目にLightProbeを設置するってことを意識して置いています。
提灯だけでは各屋台の中がうまく光らなかったので、ライトをbaked only設定にしておいています。

Occlusion Culling

Occlusion Cullingは、カメラに写っていないオブジェクトを描画しないようにできる機能です。
カメラに写っていない部分のレンダリングを省略できるので、負荷対策には非常に効果的です。
ただし、適切に設定しないと予期せぬカリングが発生して、消えてほしくないものが消えてしまったりします。
こちらの記事を参考にしています。

occluderとoccludeeの違いを意識することで、予期せぬカリングを避けることができます。
occluderとoccludeeの設定は、インスペクタービューの右上のStaticから設定します。
画像の状態だと、このオブジェクトはOcclusion Cullingに無視されます。
occluder_occludee.png
この二つとSmallest Holeの設定を調整することで、フォトスポットの穴からのぞいた時や、木の葉っぱ越しに見たときに周りを見たときに、意図しないカリングがされないように調整しています。
また、Terrainで作成した地面をBlenderで分割することで、地面ごとカリングで非表示になるように調整しました。
しかし、分割したことでライトマップのベイクの調整が難しくなってしまったので一長一短があるかと思います。
Occulusionタブからベイクの設定などを確認し、Bakeボタン押下で設定を反映することができます。

occluder_tab.png
occlusion.png

TerrainのObject化と分割

以前に製作したゴルフワールドのときにも使用した負荷対策です。
Terrainをそのまま使うと重いので、3DObjectに変換して使用することにします。
この変換作業に必要となるのが以下のアセットです。

TerrainObjExporter

TerrainObjExporterはTerrainをObjファイルに変換してくれるアセットです。
リンク先からスクリプトをコピーして、プロジェクト内の適当なディレクトリに設置します。
すると画像のようにエディタが拡張され、シーン内に有効なTerrainが存在する場合はExportが可能になります。
設定は複雑な地形でなければデフォルトで特に問題ないです。
obj形式でTerrainがエクスポートできるので、これをblenderなどにimportして加工したりします。
この時、Terrainに設定していたテクスチャは剥がれてしまうので、後述のTerrainToMeshを使ってMaterialを作成します。
TerrainObjExporter_sample.png

TerrainToMesh

TerrainToMeshはTerrainに設定したテクスチャを出力するために使います。
Window > VacuumShaders > Terrain To Meshを選択します。
TerrainToMesh_tab.png

すると、下のようなウィンドウが表示されるので、使用するTerrainをHierarchyビューからドラッグアンドドロップで選択し、だいたいこんな感じの設定でGenerateします。
Sub-folder Nameの下のグレーの表記のディレクトリにマテリアルなどが生成されます。

TerrainToMesh_window.png
今回だとAssets > (Temporary) > test 配下に色々と生成されてます。
このNew Terrain_Materialを、先ほどTerrainObjExporterで作ったObjファイルに適用するといい感じにテクスチャ付きのTerrainモデルを作成することができます。
objファイルはBlenderなどに取り込んで修正したり、fbx形式で書き出したりしても問題ないです。
TerrainToMesh_material.png
余談ですが、TerrainでテクスチャをペイントしてnormalMapやsplatMapが生成された場合に、マテリアルがずれていることがあります。その場合は、マテリアルのTilingを-1にしたり画像を回転させて調整する必要があります。

アセットの圧縮

これは負荷対策からは若干逸れてしまいますが、各種テクスチャや音源ファイルは、必ず圧縮をして使います。
こうすることで、ビルド時にワールドのサイズを削減することができます。
テクスチャのサイズや圧縮方法は用途に応じて最適なものを選択するのが吉です。
圧縮率が高いと解像度が下がってぼやけてみえてしまうので、近距離で見るものは低圧縮、遠景は高圧縮のように使い分けましょう。

compression_crunch.png

compression_sound.png

Mesh Baker

負荷対策としてはメインといってもいいくらいの内容だったのですが、残念ながらここで力尽きました・・・
気が向いたらか、要望があれば続きを書こうかと思います。
簡単に書くと、Unityでの描画負荷の原因であるMaterialの増加を抑えてくれる有料Assetで、テクスチャアトラスを作成して複数のマテリアルを一つにまとめてくれます。
お面やうちわなど、細かいオブジェクトが多いとマテリアル数が増大し、負荷が大きくなってしまうのですが、子のアセットを使用することで大幅に負荷を軽減することができました。
使用時にちょくちょく気を付けておきたいことがあったりするので、そのあたりを書こうかと思ってたのですが…
次回作にご期待ください。

まとめ

ということで、今回は負荷対策についてでした。
箇条書きにすると以下の通りです。

  1. statsやProfilerを使って負荷をモニタリングする
  2. 使用するアセットが必要以上に重たくないか確認する
  3. RealtimeなLightはBakeする
  4. Occlusion Cullingを活用する
  5. TerrainはObjectにして使う
  6. テクスチャや音源は圧縮する
  7. MeshBakerに頼る

この他にも有効な負荷対策があればぜひコメントやTwitterで教えていただけると嬉しいです!
Twitterのフォローや感想をいただけると記事作成の励みになりますので、よろしければよろしくお願いします!

みんなで負荷の少ないワールド作っていきましょう!
最後まで見ていただきありがとうございました~!

この記事へのコメント