プラベのVRChatブログ

VRChatで楽しく過ごすためのTipsをまとめてます。

【Unity/VRChat】Unityでワールド(シーン)を軽量化する

この記事は、特にVRChatワールド作成者向けに、Unityで作成するワールド(シーン)を容量・処理ともに軽量化する方法をまとめたものです。

50MB以下という厳しい制限のあるQuest対応ワールドを作成するなら当然として、PCクライアントから見ても、ワールドが重くて良いことは何もありません。ワールド作りに慣れてきたら、軽量化についても妥協せず取り組んでみましょう。
ここでまとめている方法を使えば、演出や見栄えをほとんど妥協することなく、ワールドを簡単に、大幅に軽くすることができます。

概要

ワールドはざっくり以下の手順で軽量化できます。
検証Unityバージョン:2019.4.31f

  1. テクスチャのインポート設定をする
  2. モデルのインポート設定をする
  3. サウンドのインポート設定をする/外部から呼ぶ
  4. Prefabを活用する
  5. とりあえず Static にする
  6. ライトベイクする
  7. オクルージョンベイクする
  8. 【任意】シェーダーをVRChatのものにする
  9. 【任意】メッシュをまとめてベイクする
  10. 【任意】DRY原則を守る

テクスチャの圧縮設定をする

テクスチャ画像データそのものを圧縮しなくとも、Unityはインポート設定に応じて圧縮してからビルドしています。この設定をうまく活用することで、特にワールド容量を大幅に削減できます。
ワールドに登場する全てのテクスチャを以下のように設定してください。

テクスチャのインポート設定
テクスチャのインポート設定

解像度が低すぎるようでしたら、大事なテクスチャだけ解像度 (Max Size) を大きくしましょう。
この設定を一括でやってくれる大変便利なツールがあります。ありがたく活用させて頂きましょう。
一括圧縮ツール・AssetsCompressor1.3 - 神城工業 / Kamishiro Industry - BOOTH

おすすめの設定は以下です。

一括圧縮ツールの設定1
一括圧縮ツールの設定1
一括圧縮ツールの設定2
一括圧縮ツールの設定2

モデルのインポート設定をする

FBXってやつを使いますよね?それのインポート設定もワールド容量や処理に大きく関わってきます。
とりあえず、FBXを片っ端から以下のように設定してしまいましょう。

モデルの設定1
モデルの設定1
モデルの設定2
モデルの設定2

Import BlendShapes
FBXのBlendShapesを入れるかどうか。必要ならチェックを入れてください。
たとえば、アバターであれば必ず必要なのでチェックしましょう。

Import Cameras
FBXに元々入っているカメラを入れるかどうか。絶対にいらない。チェックを外しましょう。

Import Lights
FBXに元々入っているライトを入れるかどうか。いらん。チェックを外しましょう。

Read/Write Enabled
読み書きの可否。チェックを入れていないとどのみちVRCSDKがエラー吐きます。チェックを入れましょう。

Normals Mode
よくわからん。あるほうがキレイに見える。とりあえず「Area And Angle Weighted」で。

Smoothing Angle
よくわからん。60にしておこう。

Generate Lightmap UVs
Static(後述)にする場合はチェック必須。

Import Animation
FBXに元々入っているアニメーションを入れるかどうか。いらなければチェックを外しましょう。

モノによりますが、これをやっただけでワールド容量が15MBほど削減できたことがあります。
これらの設定をある程度一括でやるにも、神城先生のツールが大変便利です。活用させて頂きましょう。
一括圧縮ツール・AssetsCompressor1.3 - 神城工業 / Kamishiro Industry - BOOTH

サウンドのインポート設定をする/外部から呼ぶ

効果音SEやBGMなどに使うサウンドファイルの容量はばかになりません。追加前後の容量を比較してみると、数~二十MB程度になることがわかります。
サウンドもテクスチャと同様、データ自体を変更しなくとも、Unity側のインポート設定に応じてビルド時に圧縮されます。登場するサウンドファイルの設定をすべて以下のように設定してください。

サウンドのインポート設定
サウンドのインポート設定

デフォルトではステレオに大抵なっていますが、VRChatワールドでサウンドをステレオにする意味はあまりありません。

この設定を一括でするにも神城大先生のツールが便利です。心の中で柏手を打ちながら活用しましょう。ついでにBoostという名のお賽銭を差し上げましょう。
一括圧縮ツール・AssetsCompressor1.3 - 神城工業 / Kamishiro Industry - BOOTH

また、特にBGMに関しては、サウンドファイルをワールドに組み込まず、URLで外部から呼び出しするのが非常にオススメです。
例えば、以下の商品を利用すると、あらかじめ入力しておいたYouTube URLリストの動画音声をBGMとして順番にループ再生してくれます。
booth.pm
美しいモデルもついてくるので、これはすごくオススメです。

Prefab を活用する

同じようなオブジェクトを何度も使うのであれば、必ず Prefab 化しましょう。ワールド容量にも処理にも関わってきます。
たとえば、明かりをのせたテーブルセットであるとか、樹木であるとか、沢山の草花であるとか。
オブジェクトを Prefab 化するには、シーンから Project ウィンドウにドラッグ&ドロップします。すると、青い箱のようなアイコンになります。以降、つくった Prefab を Project からシーンに放り込むだけで同じオブジェクトを並べることができます。
また、軽量化できるだけでなく、「なんか、テーブルに飾りを足したくなってきたな」「ちょっと高さを変えたいかもしれない」といったとき、テーブルセットを10個ならべていたとしても10回修正しないでよくなります。Prefab の大本(ルート)を修正してやれば、同じ Prefab を参照するオブジェクトすべてに反映されるのです。
Prefab を修正するには、Project にある Prefab をダブルクリックします。すると、その Prefab の内容だけが浮かんだ空間がシーン画面に開かれます。オートセーブだと編集しにくいので、右上の設定からオートセーブモードを外しましょう。Ctrl + S を押せばセーブできますので、ちょっと変えたら Ctrl + S のクセをつけてしまいましょう。ちなみに、このショートカットは大抵のアプリケーションで「セーブ」に対応しています。

とりあえず Static にする

動かなくて Udon も入ってないオブジェクトは全部 Static にしよう。Inspector の右上にチェック欄があるからそれにチェックをつけよう。
こまかいことは後でいい、とりあえず Static にしましょう。

ライトベイクする

Realtime と Baked ライト

ライトには大きく分けて2種類あります。ひとつは Realtime ライト、もうひとつは Baked ライトです。
Realtime ライトは、プレイ中に光の加減や影などがリアルタイムで計算される光源のことです。これが多ければ多いほど、ワールドの処理は重くなります。
Baked ライトは、止まっている( Static な)物体に対する光の加減や影などを先に計算しておき、プレイ中は決まった値を使う光源のことです。これは、いくら沢山置いても処理にほぼ影響しません。

ライトの数は、ワールドの見栄えに重大な影響を与えます。なので、ライトの数そのものを軽量化のために減らしてしまうのは得策ではありません。
代わりに、Baked ライトに差し替えて構わない光源(プレイヤーなどの動く物体の影が出なくても構わないもの)を、できるだけ Realtime から Baked ライトに変えることが大切です。
Realtime ライトは、太陽光などの動体影作りに重要な光源にだけ設定するか、あるいは全く設定しない方が良いでしょう。

Light Probe Group を配置する

Baked ライトをワールドに反映させるには、Light Probe Group を適宜ワールドに配置し、Light 設定から Bake する必要があります。
Light Probe Group とは、Baked ライトによる明るさの影響度を保存する Light Probe という点の集まりです。これがないと、プレイヤーなどの動く物体にとって Baked ライトは無いものと同じ扱いになってしまい、表示が真っ暗になってしまったりします。必ず配置しましょう。
Light Probe Group は、メニュー > GameObject > Light > Light Probe Group で配置することができます。
しかしながら、 Light Probe の配置はとても手動でやってられませんので、スクリプトを使って自動配置させましょう。以下のような便利な配置スクリプトがあります。
assetstore.unity.com
assetstore.unity.com

Simple Light Probe Placer は指定した範囲に指定した密度で等間隔に、Magic Light Probe は光の変化が大きいところほど高密度になるように Light Probe を配置してくれるスクリプトです。
絶対に後者の方が綺麗にライトを表現できるはずですが、今のところ私にはあまり違いがわかりません。きっと良いものなんだと思います。
なお、Simple Light Probe Placer については、VRChatワールドビルド時にプロジェクトに入っているとエラーになります。使い終わったらフォルダごと消してしまい、必要なときだけインポートすると良いでしょう。

Reflaction Probe を配置する

次に、Reflection Probe を適宜配置します。Reflection Probe とは、光沢や鏡面反射のある物体に投影するための「周囲の光景」をあらかじめ360°ぶん保存するものです。この光景も Static な物体のみ保存されます。VRC Mirror のような鏡をきちんと描写すると、非常に重い処理負荷がかかるので、本当に「鏡」を表現するとき以外はこうした事前保存の景色を反射に使います。
Reflection Probe を置くには、メニュー > GameObject > Light > Reflection Probe を選択します。
Reflection Probe を置いておくと、反射のあるワールドオブジェクトだけでなく、光沢反射のあるプレイヤーアバターにも反映され、多くの場合はより綺麗に見えます。

ライトを焼き込む(ライトベイク)

準備ができたら、Lighting (メニュー > Window > Rendering > Lighting Settings) 画面で Generate Lighting を押して、ライトマップを焼き込みします。このとき、Light Probe と Reflection Probe も共に焼き込みされます。
Static な物体を移動させたときは、影や光の加減も変化します。その都度このライトベイクを実施するようにしましょう。

NVIDIAGPUの一部製品を組み込んだPCをお持ちの方は、以下の有償ツールを使うことで、より美しく、かつ高速にライトベイクをすることができます。
assetstore.unity.com

Unity付属のライトベイクはCPUを使うのに対し、Bakery はGPUを活用して高速処理します。

注意事項

ごくまれに、どうしてもライトマップが生成できない、Bakery などにエラーを出させてライトベイクを失敗させてしまうメッシュ/オブジェクトがあります。
こうしたものに当たった場合は「Contribute GI」のチェックを外し、ライトベイクの対象から外してしまいましょう。そんな時もあります。諦めも肝心です。
「Contribute GI」だけを外すには、Inspector の右上にある「Static」右の下三角を押して、「Contribute GI」のチェックを外します。

ライトベイクでエラーになる例
ライトベイクでエラーになる例

上記は筆者作のワールドにある螺旋階段。Dynamic Spiral Stairs というツールで生成した特殊なオブジェクトであるせいか、Bakery でライトベイクしようとするとエラーになって停止してしまう。そのため、Realtime ライトで陰を作っている。

オクルージョンベイクする

オクルージョンカリング (Occlusion Culling) とは、「見えないはずのものを描写しない」機能です。
画面に映るオブジェクトが多ければ多いほど処理は増え、動作が重くなります。オブジェクトは少ない方が軽量になる。しかし、だからといってワールドに置きたいものを置かなかったら面白くありません。
存在するオブジェクトの数はそのままに、壁などの障害物に隠れて見えないオブジェクトの描画処理を割愛する、それがオクルージョンカリングという機能です。
オクルージョンカリングを有効にするには、動かないオブジェクトに Static をきちんと設定したあと、オクルージョンウィンドウを開き、各種パラメータを設定してから、Bake タブを選択して Bake ボタンを押します。
Static の物体を動かした場合、オクルージョンカリングの Bake もやり直さなくてはいけません。その都度、ライトベイクと合わせて忘れずに Bake しましょう。

オクルージョンカリング
オクルージョンカリング

上記は筆者がよく使う設定。1m平方以上の障害物を Occluder (他のオブジェクトを隠すもの)として処理する。

ここで注意しなくてはいけないことは、「オクルージョンカリングはかなりざっくりしている」ということです。障害物がアーチ状の細道だったり、窓のあいた壁だったり、あるいは背板がなくて見通しのいいシェルフの場合でさえ、向こう側が見える範囲などお構いなしに背景のオブジェクトを消してしまいます。
こうしたオブジェクトについては、「Occluder Static」のチェックを外してしまいましょう。
Occluder とは「他の物体を隠すオブジェクト」、Occludee とは「他の物体から隠されるオブジェクト」のことです*1。これらだけを外すには、Inspector の右上にある「Static」右の下三角をクリックし、Occluder Static, Occludee Static のチェックを外します。

オクルージョンカリングを有効にした後は、かならずテストビルドをして、予期しない非表示オブジェクトがないか目視で確認するようにしましょう。

【任意】シェーダーをVRChatのものにする

VRCSDKに含まれるシェーダーがいくつかありますね?可能であれば、それらのどれかをできるだけマテリアルに適用することで、ワールドの容量・処理ともに軽くできます。
ただし、残念ながら見映えがあまりよくありません。なので、見比べてみて「これならまあ・・・」という場合だけ変更しておきましょう。

【任意】メッシュをまとめてベイクする

この項目で解説する内容は Mesh Baker を購入した方にしかできません。無い人は買ってきてください(任意)。
assetstore.unity.com

メッシュというのは、ざっくり言うと3Dモデルの「形」です。
メッシュが沢山あればあるほど、「このメッシュは今プレイヤーに見えているのか?」「障害物より手前にあるのか?」といった計算処理が発生します。しかし例えば、『お皿が沢山のった食器棚』などで皿ひとつひとつに対してそんな処理をされても無駄ですよね?どうせ一緒に視界に入るに決まっているのだから、棚もろとも一塊に「こいつは今みえていますよ」と処理をしてほしい。そんなときはメッシュを固めてしまいます(メッシュベイクする)。

メッシュベイク2
メッシュベイク1
メッシュベイク2
メッシュベイク2

上記は筆者作のワールドにあるショーケース。両開きの扉はアニメーションで開閉し、中段にあるティーセットはPickupで動かすことができる。これらの「動かせるオブジェクト」を除いた部分はメッシュベイクして固めてある。酒瓶やワイングラスなどがあるが、これらは元々別々のメッシュだった。
メッシュベイクされたオブジェクトは分離できなくなるので、こうなったら「ワイングラスをひとつだけ取り出す」「酒瓶を一本だけ動かす」といったことが不可能となることに留意すること。

InspactorのTag
InspactorのTag

ちなみに、Inspactorの上部に「Tag」というものがあり、これを「EditorOnly」に変更することで「データは残しておくけれどVRChatにはアップロードしないオブジェクト」とすることができる。メッシュベイクする前のオブジェクトをとっておいて、後から配置や品揃えを手軽に変更するためのバックアップとして取っておく際に便利なので、必要であれば活用しましょう。なお、これはアバター改変でも使用できます。

メッシュの固め方は余所様の記事をご参照ください。アバター改変にも非常に便利ですのでオススメです。
Mesh Renderer部門
bironist.hatenablog.com

Skinned Mesh Renderer部門(とくにアバター
www.asset-sale.net

ちなみに、マテリアルをまとめると高確率でバグるので、私個人としては Texture Baker の利用をオススメしません。 Mesh Baker だけ使って、マテリアルは分かれたまんまにするほうが便利です。

【任意】DRY原則を守る

この項目は、コード(スクリプト)を自分で書く人向けの話です。
DRYとは Don't Repeat Yourself の略称で、「おんなじようなコードを何度も繰り返し書かず、メソッドにしたりとかしてまとめようね」という原則です。
これはUnityでの制作にも非常に役立つ考え方で、やるかやらないかでワールド容量に差が出てきます。プログラミングの原則を守って楽しくコーディングしましょう。

*1:動詞の後ろに「er」がつくと「~する人/モノ」、「ee」がつくと「~される人/モノ」の意になります。