【VRChat】n番煎じで送るアバター最適化TIPS

# 注意
今回の記事は、VRChat公式が推奨する手段と、私が普段最適化を行う上で心がけている項目をまとめたものです。
いつものことですが間違いを含む可能性があります。その時は積極的に教えて頂けると助かります。
あくまで私の知識が及ぶ範囲での解説となりますので、ご了承ください。

追伸：今回の記事では、主にVRAM消費量の観点から最適化を行います。純粋な描画負荷の話はまた別の機会に詳しく触れさせて下さい。


# 挨拶
要求スペックがAAAタイトル並みと言われて久しいVRChatですが、皆様いかがお過ごしでしょうか。
身近な範囲のイベントでGoodアバターが求められたり、VRChat公式がアバターの容量制限を改めたり。
以前から最適化関連の記事は書こうと思っていたのですが、ようやくきっかけが見つかったということで、行動に移すことにしました。
拙くも私の持てる範囲での知識を盛り込んだ記事となっていますので、参考にして頂けますと幸いです。
ではでは早速本編へ～

# 第一章：前置き

## 1. 最適化ってそもそも何？
「最適化」「軽量化」などと呼び方にバラツキこそあるものの、この言葉を聞いたことが無い、という方はそう居ないでしょう。
人によってその定義が違ったりもする言葉ですが、私は主に**下記3つの行為を包含した**言葉だと考えています。

**1. ダウンロードサイズの削減**
**2. 描画負荷の削減**
**3. パフォーマンスランクの向上**

といった感じに分けてはみましたが、とりあえずは「**アバターの表示にかかる負荷を減らす行為**」だと思って頂ければそれで大丈夫です。

## 2. 最適化の必要性
「自分視点では重くないし、わざわざやる必要はない」
そう思う方も中にはいらっしゃるかもしれません。
実際のところ、自分自身が不自由していない場合、残念ながら最適化というのは無視されがちな行為になります。
しかしここで意識していただきたいのは
「**アバターを処理・表示するのは、あくまで各自のPCである**」 ということです。
それを踏まえると、アバターを見せるという行為は少し意地悪な言い方をすると
「**アバターの負荷を相手に押し付ける**」行為とも言えてしまうわけでして...

...もうお分かりですね？
とどのつまり、最適化というのは相手への配慮に他ならないのです。
何より「アバターが重すぎてあの人が居るだけで重い」といった事態は、誰も望んでいないはずです(そんなケースはごく稀ですが)

そんなこんなで、最適化の必要性についてご理解いただけましたら、次へ進んでいきましょう。



# 第二章：基礎知識

## 1. ダウンロードサイズとは
アバターの最適化を考えるうえで、まず真っ先に取り組むべきと考えられるのがこの項目です。
ダウンロードサイズとは、**アップロードしたアバターのデータサイズ**のことで、この分のデータを相手のPCがダウンロードすることでアバターを表示できる仕組みになっています。

![スクリーンショット 2024-05-31 183759.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2016616/87db58d5-c96b-524e-5724-6491072fc666.png)
参考までに、こちらが私の普段使っているアバターの数値です。

後ほど触れる例外もありますが、一般的にはこの項目は**VRAM消費量(テクスチャメモリー)に直結**します。
またアバターを**表示するのにかかる時間の短縮**にもなるため、まずもって削減しない手はないと言えるでしょう。

基本的にこの項目は、衣装や素体、その他オブジェクトなどに使用される**テクスチャ**、つまり**画像データが大半**を占めています。

VRAM使用量については、少し踏み込んだ内容をこっちに書いたので気になる方はどうぞ。

https://qiita.com/Hellcat_152/items/e643aa9920162f5380bb


## 2. 描画の仕組みと負荷について
ダウンロードサイズをひとしきり最適化した後、次にターゲットとなるのが純粋な描画負荷です。
あまり深く解説するとボロが出r(
...まあ私自身の理解が及ばない範囲にもなってくるので、簡単な解説に留めます。

UnityやVRChat上での描画負荷は「**Draw Calls**」という値を基準に判断することができます。
このDraw Callsというのは、ざっくり言うと「**CPUからGPUへの描画命令**」のことで、一般的にはこれが少ないほど描画負荷が低いものとみなすことができます。
この描画命令には毎度**それなりの時間がかかり**、またゲームのフレームはそれらがすべて完了してから表示されるため、この値は**FPSに直結**すると言えるでしょう。

してこの"Draw Calls"はさらに二つに分けられ、

・**Batches**：メッシュの描画命令
・**SetPass calls**：マテリアルの描画・更新命令

Unity上ではこの二つの項目を別々のものとして確認することになります。

どちらがよりパフォーマンスに影響するのか...については明確な回答が出来ないのですが、まあ両方とも少ないに越したことはないです。

![スクリーンショット 2024-05-31 211105.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2016616/ca48f039-883a-341a-ba01-e929a0713d3f.png)
参考までにこちらが私のアバター(最適化済)を単体で表示したときの数値です。
Batches：18
SetPass calls：15
ちなみにFPSの項目はハードによって異なるので、他者との比較では**全く参考になりません**。

## 3. パフォーマンスランクとは
恐らく上記の2つより圧倒的に知っている人が多いはずです。
VRChat公式が定め、アバター最適化のガイドライン的役割も果たしている、アバターの**総合的な負荷の目安**です。
**Excellent, Good, Medium, Poor, VeryPoor**と続き、左に行くほど負荷の軽いアバターという評価になります。

大人数が集まるイベントではMediumやGood以上のアバターが求められる場合もあるため、決して**無視のできない**項目であることはお分かりでしょう。
長らく更新されていないことから**実態との相違**は否めませんが、それでも今なお**最適化の参考にできる**基準です。

具体的な基準はこちらからご覧になれますので、興味のある方はどうぞ。

https://creators.vrchat.com/avatars/avatar-performance-ranking-system/#pc-limits

# 第三章：作業開始
ここまで、アバターの最適化を考えるうえで注目すべき3つのポイントについてお話しましたが、なんとなくご理解いただけましたでしょうか。
正直なところ、こういうのは実際に手を動かす方が手っ取り早いので、そろそろ本題に入っていきましょう。
**※作業開始前の数値を記録しておくと、あとからその効果を確認できるのでおすすめです。**

## 0. 必要なパッケージの導入
まずは先に必要なツールを導入してしまいましょう。

### ①lilAvatarUtils
アバターに使われているテクスチャを**一覧表示**できるツール
**lilToon**をVCC経由で導入するとついてくる

https://lilxyzw.github.io/lilToon/#/

### ②VRWorld Toolkit
シーンの**ビルド後の容量**と、その**内訳**を確認することができるツール
**VCCにデフォルトでついてくる**
アップロード時の**圧縮がかかった後の数値が表示される**ので、VRChat上での正確な容量が知りたい場合はこっちから確認する必要がある

### ③Avatar Optimizer(AAO)
アバターのルートオブジェクトに"**AAO Trace and Optimize**"というスクリプトをアタッチするだけで、**できる範囲の軽量化**を非破壊的に行ってくれる神ツール
手動設定も活用すれば比較的簡単にGoodを目指せるため、是非とも使いこなしたい一品

https://booth.pm/ja/items/4885109

### ④anatawa12's gists pack
アバター関連の便利なツールが詰め込まれたパッケージ
これまたVCCにデフォルトでついてくる

以上の導入が済んだら、次へ進みましょう



## 1. ダウンロードサイズの削減
ダウンロードサイズを削減するうえで、まずはいくつか原則があります。

### ダウンロードサイズ削減の原則
+ **1アバターにつき入れるのは1衣装までにすること**
+ **使わないオブジェクトは消すかEditorOnlyにすること**
+ **テクスチャ解像度は2K以下に留めること**
+ **テクスチャにはすべて圧縮をかけること**
+ **クランチ圧縮は出来るだけ避けること**

以上5点は、容量削減を行う上での**鉄則**です！
ではそれぞれについて解説していきましょう。

## 1. 単一のアバターに複数の衣装セットを実装しないこと
ダウンロードサイズがあまりにも大きい場合、まずはこれを疑ってみましょう。
VRChatでは、ExMenuとアニメーションを設定することで衣装や小物の表示を切り替える手法が一般的ですが、このうち**衣装セットごと**切り替えるような実装は、一般的に**強く非推奨とされています**。
具体的に言うならば、
・衣装セットA
・衣装セットB
・衣装セットC
がアバター配下に存在して、それらをアニメーションで"まとめて"切り替えるような実装の仕方を指します。

この方法がなぜダメなのか
答えは単純で、**全ての衣装セット分のデータが1アバターに詰め込まれるから**です。
ただでさえ重い衣装を複数放り込んだら...もう分かりますね？
そうです、クソ重アバターの爆誕です。
加えてUnity/VRChatの仕組み上、仮に**モデルが非表示であっても、そのテクスチャデータはVRAMを占有します**。
1度に着れるのは1衣装までなのに、**VRAM上は全衣装分の容量が確保される**ため、見る人のVRAMをあっという間に窒息させてしまうわけです。


この問題の解決方法は単純で、**衣装セットごとにアバターを分けること**になります。
このことを指してよく「1アバター1衣装」と言ったりします。

ちなみに「**セットごと**」切り替えるのが問題なのであって、1衣装内でのパーツ切り替えや、複数の衣装からパーツを持って来ることに関してはさほど問題ではありません。
ただし、パーツの元となる衣装が**1枚のテクスチャで構成されている**場合は、パーツのサイズ等を加味しつつ解像度を下げる必要が出てきます。
面倒であればとりあえず2Kにしておきましょう。


:::note
**1アバターにつき、入れる衣装は1セットまで！**
複数の衣装セットを使いたい場合は、アバターを分けて対応しましょう。
さもないと見る人のVRAMを窒息させてしまう可能性があります。
:::

1アバター1衣装の原則について、ご理解いただけましたら次へ進みましょう。


## 2. 使わないオブジェクトは、消すかEditorOnlyにすること
続いて二つ目、余分なデータを削り取る作業に入っていきます。
VRChatのアバターアップロードの仕組みについても軽く触れつつ進めていきましょう。

VRChatのSDKは、
1. アバターの配下にあるオブジェクトを取得
2. 依存関係を解析し、そのオブジェクトが要求するファイルやデータをまとめる
3. それら全てに独自の圧縮をかけ、VRChatのサーバーにアップロード

といった流れを踏んでアバターをアップロードします。
今回注目するのは主に2番目の部分で、ここに余計なデータが含まれないようにするのが目的です。

作業内容はいたってシンプルで、
使われていないオブジェクトに「**EditorOnly**」タグを付けるだけで完了します。

![スクリーンショット 2024-06-01 093721.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2016616/af7c70c2-3eb2-8f4e-1e52-f3740ed471b9.png)

このEditorOnlyタグというのは、文字通りエディタ、つまり**Unity上でだけ**表示される、という意味になります。
このタグを付けることで、**そのオブジェクトが使用する**メッシュやマテリアル、テクスチャなどのデータがビルド時に除外され、結果**余分なデータがアバターに含まれる**、といったことが無くなります。
ただし必要なものに誤って付けてしまうと、VRChat上で不具合の原因になるので気を付けましょう。

:::note
**不要なオブジェクトは「EditorOnly」タグをつけてビルド対象から除外する！**
そのオブジェクトに付随するデータがアップロードに含まれなくなります。
必要なオブジェクトに誤って付けないように注意！
:::


## 3. テクスチャ解像度は2K以下に留めること
3つ目にして最重要ポイント、おそらくこれが**最も効果的な**容量削減の手段になります。
これもまた、**画像のデータサイズと解像度の関係**について、先に少しだけお話ししてしまいましょう。

Unity上で指定できる画像の解像度のうち、以下の解像度をそれぞれ

・1024x1024：**1K**
・2048x2048：**2K**
・4096x4096：**4K**
・8192x8192：**8K**

と呼びます。
一般的にアバターのテクスチャに使用されるのは、おおよそ1K ~ 4Kの範囲です。

画像のデータサイズは基本的に**画像の解像度に依存**し、**1辺の解像度が2倍**になると、**データサイズは4倍**になります。
4K画像と1K画像では、実に**16倍もの**容量差が生じるわけです。

加えてUnityで利用可能な圧縮形式では、アルファチャンネル、つまり**透明度**を含む画像は、含まない画像に比べて、これまた**約2倍**の容量を持つことになります。
これには凹凸表現に役立つ「ノーマルマップ」や、表情のうち赤面などを含むテクスチャが該当します。

では以上のことを軽く頭に入れたうえで、作業に移っていきましょう。

**まずは現在のテクスチャ内訳を確認します。**
Unityのエディタ画面から、画面上部の"**Window ⇒ _lil ⇒ Avatar Utils**"と進んでください。

![スクリーンショット 2024-06-01 100123.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2016616/250730b3-6ad6-06de-e0cd-daf6b4095fa7.png)

以下のような画面になったら、左の"Hierarchy"タブから、アバターのルートオブジェクトをドラッグします。

![スクリーンショット 2024-06-01 100354.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2016616/3b0b11dd-2340-815f-6f41-70d0a3fcf09a.png)

するとこういった画面になります。

![スクリーンショット 2024-06-01 100820.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2016616/a8627b21-5aad-46c6-250e-4ca29e8825b8.png)

ここで主に注目すべき項目は

+ **Max Resolution**：Unity上で設定するテクスチャ解像度
+ **VRAM Size**：予想されるVRAM使用量の概算値

の主に二つになります。
ここで変更前のVRAM Sizeを記録しておくといいかもしれません。

まず"**Max Resolution**"の項目に、一つでも4096x4096と表示されている画像があれば、それらは**全て**2048x2048に落としてしまいましょう。
実のところ**2Kと4Kの違いを体感することはそう無く**、近距離で見ていても言われない限り気づかない場合がほとんどです。
また"**VRAM Size**"は基本的に解像度に大きく影響されるため、**テクスチャの解像度を落とせば、自ずとVRAM使用量も減る**仕組みになっています。
余裕のある方は、あまり目立たない細部のテクスチャ解像度を1Kに落とすなどしてみてもいいかもしれません。

Unity上の数値を参考にすると、同じテクスチャの"**Max Resolution**"を変更した場合のVRAM使用量は、
**4K：10.7MB**
**2K：2.7MB**
**1K：0.7MB**
といった風な差がつき、同じことを透明度情報を持つノーマルマップでも行うと
**4K：21.3MB**
**2K：5.3MB**
**1K：1.3MB**
といった様により顕著な差が出ます。
解像度を下げることがいかにVRAM使用量に影響を及ぼすか、これだけでも見て取れるのではないでしょうか。

ひとしきり解像度の変更を終えたら、**忘れずに左上の"Apply"ボタンを押しましょう**。
これによりlilAvatarUtils上で設定した数値が、Unityのインポート設定に反映されます。

最後に**ウィンドウ右上のリロードボタン**を押して、VRAM Sizeを再計算しておきましょう。
元の4Kテクスチャの数が多ければ、この部分がゴッソリ削れているはずです。

:::note
**テクスチャのMax Resolution"は2Kまでに留めること！**
普段から4K以上のテクスチャを身にまとっていても、その恩恵を感じる機会は少ないかと思われます。
近くでも荒さを感じず、かつ容量を抑えたいのであれば、とりあえず2Kに設定しておくといいでしょう。
:::

## 4. テクスチャには必ず圧縮をかけること
先ほど最重要ポイントを出したばかりですが、これも先ほどと同じくらい重要なポイントになります。

ここで言う「圧縮」とは、Unity側が用意している「**GPUでデータを扱うための圧縮形式**」のことで、これを設定するか否かで容量が大きく変化します。

Windowsプラットフォームであれば
・**RGB Compressed DXT1|BC1**
・**RGBA Compressed DXT5|BC3**

Androidプラットフォームであれば
・**RGB(A) Compressed ASTC 6x6 block**

などが代表的な圧縮形式として挙げられますが、この**形式名については覚える必要が無い**ので一旦忘れてください。

上に書いたこの形式は、Unity側の画像圧縮設定を"**Normal Quality**"にした時に使われる形式で、デフォルトではこれらが使われるようになっています。
ですが、稀に圧縮設定が"**None**"、つまりテクスチャを**全く圧縮していない状態**で配布されている衣装やアセットが存在します。
何か特別な事情が存在する可能性もあるのですが、基本的に圧縮前後で目立ってクオリティが落ちる、ということはあまりありません。
今から解説する方法で圧縮形式を設定しておきましょう。

### 1. 非圧縮のテクスチャを見つける
先ほども登場した"lilAvatarUtils"を使います。
先ほどと同じステップを踏むとアバターのテクスチャ一覧が表示されると思いますが、その中に、**同じ解像度のテクスチャと比較してVRAM Sizeが大きい**テクスチャがあれば要チェックです。

※この画像では解説のためにわざと圧縮を解除しています
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2016616/5478f4ae-d98e-2194-2fb9-e3bff03cc792.png)

ということで2Kのテクスチャを1個だけ圧縮解除してみたところ、他が2.7MB、透過を含む画像でも5.3MBのところ**16MB**まで膨れ上がりました。
元の画像は不透過ですので、2.7MBと16MBで実に**約6倍**もの容量差が生じています。

左の**名前の部分をダブルクリック**すると元データに飛べるので、そこから圧縮設定をしに行きましょう。

![スクリーンショット 2024-06-06 190929.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2016616/074a0791-ff10-2c96-a76b-f021a161f9f9.png)
元データに飛ぶと、**画面右の"Inspector"タブ**がこんな表示になります。
ここは画像データの**インポート設定**と呼ばれ、解像度や圧縮形式、ミップマップの有無などを設定することができます。
今回使うのはこのうち**画面下の赤枠の部分**です。

一番下の"**Compression**"の項目が"None"になっているので、その部分をクリックして"**Normal Quality**"に変更しましょう。
※最初から"Normal Quality"になっていれば問題ありません。
![スクリーンショット 2024-06-06 192113.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2016616/daf1866a-cb0a-d364-45cd-c8b27e369dec.png)
設定後、右下の"**Apply**"を押すことで、設定が反映されます。

では確認のため、lilAvatarUtilsの画面をもう一度見てみましょう。
左上のリロードボタンを押して最新の状態にしてから確認してください。
![スクリーンショット 2024-06-06 192500.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2016616/9578c72c-dce0-ca9c-fd88-e954ebdacddb.png)
他のテクスチャと同じくらいの容量になっていれば、作業は成功です。

ちなみに圧縮をかけない場合、高解像度になるほど影響が甚大になっていきます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2016616/e56b2f1c-3e37-7dde-0786-b2a046ac6bba.png)
はい、4K以降が悲惨なことになってますね。

:::note
**テクスチャには必ず圧縮をかけること！**
圧縮による微細な劣化を気にするよりも、容量増加の方がずっと深刻です。
そもそもデフォルトは"**Normal Quality**"ですので、非圧縮のテクスチャが一つでもあれば遠慮なく設定してしまいましょう。
:::

## 5. クランチ圧縮は出来るだけ避けること
最後四つ目ですが、これに関しては疑問に思った方もいらっしゃるのではないでしょうか。
その疑問にお答えするためにも、まずはクランチ圧縮について解説してしまいましょう。

クランチ圧縮とは、超ざっくり言うと「**品質を犠牲に画像データサイズを下げる**」圧縮方法で、通常の圧縮形式に比べてさらに**強い圧縮**をかける手法になります。
圧縮に長い時間がかかる一方で、展開は高速であるという特性を持っており、ワールドのテクスチャなどには有効な圧縮形式となります。

...と、ここまで聞くと超便利な圧縮手段であるかのように聞こえますが、もちろんデメリットもあります。

### 1. 品質劣化
圧縮してるので当然と言えば当然ですが、通常の圧縮よりその**劣化度合いは進行します**。
解像度を既に2K以下に落としているなら、容量的にも必要性は低いでしょう。

### 2. ランタイムの展開負荷
先ほど「展開は高速である」と言いましたが、結局のところ**展開処理が走るのに変わりはありません**。
加えてアバターはワールドと違い、**その人がJoinしたタイミングで**展開を行うことになるため、FPSの不安定化を招きかねません。

### 3. VRAM使用量に影響しない
一番最後にして一番大事なポイントです。
ダウンロードサイズがモリモリ減るので、さもアバターの負荷が軽くなったように感じてしまうのですが、実のところクランチ圧縮をかけても**アバターのVRAM使用量は一切減りません**。
これはクランチ圧縮の「展開するとデータサイズが元に戻る」性質に起因しており、サイズの大きい画像にクランチ圧縮をかけたとしても、結局展開時にはその容量に戻ってしまうためです。

といった感じで、一般的にクランチ圧縮は非推奨な方法となっています。
そもそもここまで上げた3つの方法を踏めば、ダウンロードサイズは既にかなり減っているはずなので、文字通り最終手段と言っていいでしょう。

:::note alert
**クランチ圧縮による容量削減は避けること！**
確かに容量は減りますが、それ相応に無視できないデメリットもあります。
加えてVRAM使用量には影響を及ぼさないので、そこを勘違いしないよう注意しましょう。
:::

---

以上が代表的なダウンロードサイズの削減手段です。
成果が気になるようであれば、一度テストアップロードを行い、VRChat上、あるいはVRWorld Toolkitにて**ダウンロードサイズを確認してみるといいでしょう**。

#### TIPS：VRWorld Toolkitの使い方
![スクリーンショット 2024-06-01 121838.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2016616/f5cea38b-bb00-7b50-0dcb-71291e3e38cb.png)

![スクリーンショット 2024-06-01 121940.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2016616/dd1ed965-8671-f87f-e151-894cfdfd0258.png)

赤線で示した"**Build Size**"という項目がアバターのダウンロードサイズになります。
右赤枠の"**Build Report**"では、ダウンロードサイズのうち何がどれだけの割合を占めているのか確認することができます。
ただしここに表示されている容量はVRChatSDKによる**圧縮がかかる前のもの**なので、常に正しいとは限りません。


## 2. 描画負荷の削減
まずはアバターダイエットに成功したということで、次はより踏み込んだ最適化に触れていきます。
具体的な方針としては、上の方で軽く触れた"Draw Calls"を基準に、その数を減らす方針で進めていきます。

### 0.現状確認
作業開始の前に、まずは現時点での数値を確認しておきましょう。
画像の通り、再生ボタンを押してから、画面右上の"Stats"をクリックすると、描画関連の情報が表示されます。
**※測定環境を揃えるため、予めリアルタイムライトの影をオフにしてください。**

![スクリーンショット 2024-06-01 144052.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2016616/a4a00621-9e14-f6f1-e094-9dc56a7c899e.png)


![スクリーンショット 2024-06-01 140455.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2016616/6daa7637-602d-85a8-fdbd-3438c12fad6b.png)

主に見るべきは、先ほども触れた
+ **Batches**
+ **SetPass calls**

以上二つになります。
このアバターでは、現時点でそれぞれ30と14であることが見て取れますね。

参考までに、私のホームワールドの全体を映したときの数値がこんな感じです。
まずもってそんなことは無いと信じたいですが、現状の数値がどちらか一つでもこれより大きいのであれば、危機感を持った方が良いです。
![スクリーンショット 2024-06-01 135234.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2016616/e5f4f28e-40c7-6a86-323e-cd1c43160025.png)


### 1. ドローコールを減らすには？
では具体的にどうすればそれらを減らすことができるのか。
この数値に直接影響を与えるコンポーネントを列挙して行こうと思います。

#### 1. Skinned Mesh Renderer / Mesh Renderer
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2016616/d2413d9c-c9b4-0e98-240d-b8ea64389563.png)

これは主に"**Batches**"に影響を与えるコンポーネントです。
Batchesは**メッシュの描画指示**ですので、メッシュが多ければそれだけその数は増えます。
なおかつアバターや服に使われる"**Skinned Mesh Renderer**"は、通常の"Mesh Renderer"と比較して負荷が高いです。

**メッシュを結合**することでBatchesを効果的に減らすことができますが、表示・非表示の切り替えも**結合後のメッシュが最小単位となる**ため、結合するメッシュの選定は慎重に行う必要があります。

#### 2. Material
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2016616/45dd37b5-801b-a566-8ef5-a10269a7eacc.png)

これは主に"**SetPass calls**"の方に影響を与えるコンポーネントです。
メッシュの質感やテクスチャ情報を持ったコンポーネントで、オブジェクトの色を変えたりテクスチャを入れ替えたりするときに出てきます。

これもメッシュ同様、結合を行うことで総数を減らすことができます。
同じ内容のマテリアルが複数ある場合、どれか一つに統一してしまうのも効果的ですね。

#### 3. Realtime Light
最後にしてラスボスの登場です。
一般的にアバターに同梱されたライトは「**アバターライト**」と呼ばれますが、このコンポーネントは"**Batches**"と"**SetPass calls**"の両方に**大きな影響を及ぼします**。

これに関しては実測値を見て頂いた方が早いでしょう。

---
##### 1. ワールドライトのみ(デフォルト)
![スクリーンショット 2024-06-01 144927.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2016616/b19a3f23-51e3-1904-84bc-1a79ac6d0ebb.png)
Batches：30
SetPass calls：14

---
##### 2. アバターライト1個(Spot/Point, 影の有無関係なし)
![スクリーンショット 2024-06-01 144821.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2016616/34adb422-22bf-440d-ef71-25784d3ffb93.png)
Batches：57
SetPass calls：41
(計測ミスってるかも？)

---
##### 3. アバターライト1個(Directional, 影なし)
![スクリーンショット 2024-06-01 144821.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2016616/34adb422-22bf-440d-ef71-25784d3ffb93.png)
Batches：57
SetPass calls：41

---
##### 4. アバターライト1個(Directional, 影あり)
![スクリーンショット 2024-06-01 145622.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2016616/418fc80c-f931-f105-6c50-2ec7af290ca0.png)
Batches：**99**
SetPass calls：56

---

見て頂くと分かる通り、アバターライトを入れたことによって両者の**数値が跳ね上がっている**のが見て取れると思います。
VRC公式も言及していますが、これは**ライトが触れる全ての物が2倍のドローコールでレンダリングされる**ためです。
...3倍以上に跳ね上がってるものもありますが、とりあえずそういう物なんです、はい。
元の2倍になる、ということは**元が重ければ大変なことになる**わけで...

最近だと某影システムなんかが代表例になりますが、それに限らずとも**恒常的にアバターライトを使用するギミックは、"普段使いは"避けた方が良い**でしょう。

VRChat公式では以下のように言及されています(和訳済み)
> 常に点灯しているライトは使用しないでください。アニメーションのオーバーライド(Animation Override)を使用して懐中電灯のオン/オフを切り替えるか、ライトをまったく使用しないでください。

:::note
**Material、Skinned Mesh Renderer, アバターライトは、描画負荷に直接響く！**
値として実測できる範囲では、以上三つが主要な削減対象になると考えています。
実際のところもっと多様な要素が影響を及ぼすのは間違いありませんが、まずはこの辺りを減らしておけば間違いはないでしょう。
:::


### 2. 描画負荷(ドローコール)を減らす
ではここからは、実際に上に挙げたコンポーネントを減らしていく作業になります。
やれメッシュだ結合だと難しい言葉を使った気がしますが、実際の内容としては**ツールを使うだけ**です。

それが皆様ご存じ**Avatar Optimizer**です。
先日のアップデートにて**Skinned Mesh Rendererの自動結合**に対応し、その総数を減らすことが格段に簡単になりました。
加えて手動での結合を行えば更なる削減も可能で、これを駆使すればGoodのアバターも夢ではなくなります。

駄弁ってても仕方ないのでそろそろ手を動かしましょう。
#### 1. アバターに"**AAO Trace And Optimize**"をアタッチする
アバターのルートオブジェクトを選択し、Inspectorタブの一番下に行くと、"Add Component"というボタンがあるはずです。
![スクリーンショット 2024-06-01 160821.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2016616/7f9cd8c5-4ab8-a323-98ec-b4e4893bb944.png)

これをクリックしたのち、検索欄に「AAO」と入力することで、Avatar Optimizerの各コンポーネントが出てきます。
その中から「AAO Trace And Optimize」を選択し、アバターにアタッチしましょう。

![スクリーンショット 2024-06-01 161030.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2016616/d94bf25f-c073-61f3-62d3-c6d5d38d33fd.png)

これで自動軽量化は終わりです。
このスクリプトをアタッチすることで、あとは**ビルド時に勝手に軽量化が走ります**。


#### 2. ActualPerformanceWindowを有効化する
効果を分かりやすくするため、パフォーマンスランク計算ツールを有効化します。
**Tools->anatawa12's gists selector** と進み、"ActualPerformanceWindow"にチェックを入れましょう。
![スクリーンショット 2024-06-01 162012.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2016616/2f6729f9-24ad-0cac-d7d4-e662e6d406bb.png)
![スクリーンショット 2024-06-01 162135.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2016616/1abca1ab-bd58-68eb-08c3-cfed3703b249.png)

#### 3. 効果確認
では再生ボタンを押してプレイモードに入ります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2016616/6bf2729c-c857-c05d-2b9d-ccf80a2bb012.png)
Statsタブを見ると、
+ Batches：30⇒23(-7)
+ SetPass calls：14⇒14(±0)

という結果になりました。

続いて先ほど有効にした"**ActualPerformanceWindow**"の方に目を向けると、

適用前
![スクリーンショット 2024-06-01 162945.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2016616/3cdb9fb5-85ab-6943-e526-eb06aa40a317.png)

適用後
![スクリーンショット 2024-06-01 162955.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2016616/47c28f0c-e46b-f2d4-0811-fc84e5a92e7f.png)

マテリアル、Skinned Mesh Rndererともに4つ減少し、アバターランクがPoorまで上がっているのが確認できるかと思います。

ちなみに**アバターライトは自動削除の対象とならない**ため、自分で消す必要があります。

正直なところ、描画負荷の低減と言っても、**既にほかの最適化を済ませたうえで**ここに居るので、これ以上あまりやることはありません。
もしメッシュの手動結合(AAO Merge Skinned Meshなど、**さらなる最適化を求める場合は**こちらの記事を参考にすると良いでしょう。

https://note.com/_haster_/n/n547fab694884#3905c83b-c60e-46bb-ab84-b83df52e8352

一応**手動結合を突き詰めると**、見た目を全く損なわずにここまで軽量化できます。
![スクリーンショット 2024-06-01 164500.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2016616/360f8ae4-106e-3f21-3aa6-e10c4201b617.png)
![スクリーンショット 2024-06-01 164454.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2016616/f003009e-a931-0db7-b806-67625e3cd36b.png)
![スクリーンショット 2024-06-01 164443.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2016616/368d0cf0-dd47-050c-e4a1-1dc1092524c0.png)

服の脱ぎ着などはオミットすることになりますが、元から大した頻度でしないので問題ありません。
Physboneだけ別のツールで削ってるので、そこだけご了承を！

:::note
**困ったらとりあえず「AAO Trace And Optimize」を付けよう！**
AAOには軽量化に役立つありとあらゆる機能が入っている、と言っても過言ではないので、是非とも積極的に活用していきましょう。

:::

## 3. パフォーマンスランクの向上
では最後のステップ、パフォーマンスランクの向上です。
今までのステップを全て踏んできているのであれば、基本的にこの先の作業は不要でしょう。
ここから先はGoodやMediumのアバターを条件とするイベントに行かれる方、あるいはその境地を目指したい方向けです。

これに関しては削減に関する共通のアドバイスが存在しないため、**足枷になりやすい項目とその対処法**について書いていこうかと思います。

まずはざっと表を出してみましょう。
各項目の数値がそのランクの上限値で、それを超えると1個下のパフォーマンスランクに落とされます。

|  | Excellent | Good | Medium | Poor |
|:-:|:-:|:-:|:-:|:-:|
| Triangles | 32000 | 70000 | 70000 | 70000 |
| Texture Memory | 40MB | 75MB | 110MB | 150MB |
| Skinned Meshes | 1 | 2 | 8 | 16 |
| Material Slots | 4 | 8 | 16 | 32 |
| Physbone Components | 4 | 8 | 16 | 32 |


### 1. Triangles
「ポリゴン数」と言った方が馴染み深いかもしれません。
モデルに含まれる三角面の数のことで、影響はそこまで大きくないものの、基本的にこの数が小さいほど低負荷なものと見なすことができます。
手の込んだ衣装やアバターを使っていると簡単に超えてしまう印象ですが、Avatar Optimizerのコンポーネントを使うことである程度は削減できます。
・[AAO Rmove Mesh By Blendshape](https://vpm.anatawa12.com/avatar-optimizer/ja/docs/reference/remove-mesh-by-blendshape/)
　貫通防止用シェイプキーが設定されている部分のメッシュを削除できる
 
・[AAO Remove Mesh in Box](https://vpm.anatawa12.com/avatar-optimizer/ja/docs/reference/remove-mesh-in-box/)
　Unity上で指定した範囲のメッシュを消すことができる

公式マニュアルになりますが、この辺りを参考にすると良いでしょう。

### 2. Texture Memory
端的に言ってしまえばVRAM使用量です。
この記事でお話ししたステップを踏んでいればまずVeryPoorにはならないと思います。
対処法としては
・**テクスチャの解像度を下げる**
・**参照する画像データを減らす**
・**適切に圧縮を設定する**

などが挙げられると思います。

### 3. Skinned Meshes / Material Slots
Skinned Meshes：衣装や素体など、アーマチュアに追従して動くメッシュの数
Material Slots：質感およびテクスチャ情報の数
衣装やアクセサリーをたくさん入れると、その分だけこの項目が増えます。
対象法としては
・[AAO Trace And Optimize](https://vpm.anatawa12.com/avatar-optimizer/ja/docs/reference/trace-and-optimize/)
　元のアニメーションを参照しつつ、見た目に変化がないように自動で結合する
 ・[AAO Merge Skinned Mesh](https://vpm.anatawa12.com/avatar-optimizer/ja/docs/reference/merge-skinned-mesh/)
 　マテリアルなどを参照しつつ、可能な限りそれらを1つにまとめる。
　アニメーションによる切り替えなどは考慮されない。

などが挙げられます。
さっきも出てきましたね。

### 4. Physbone Components
アバターに設定されたPhysboneの総数です。

AAOにもPhysboneを統合するツールはありますが、純粋に数を減らすのであれば、**VRCQuestTools**の[Remove Physbones](https://kurotu.github.io/VRCQuestTools/ja/docs/references/main-menu/remove-physbones/)という機能を使うのがおすすめです。

本来の用途であるQuest対応についてはこちらの記事をご参照ください。

https://qiita.com/Hellcat_152/items/116895e63cefd31c870a

---
制限をオーバーしやすい(と感じる)項目については以上の通りとなります。
ありがたいことに、パフォーマンスランク関連では**結構多くの記事が出回っています**ので、もしこれ以外でオーバーする項目がある場合は、**個別に調べて頂くのが手っ取り早い**かと思われます。

:::note
**パフォーマンスランクに関しては共通のアドバイスがしづらいです**
AAOやその他パッケージの機能を活用するほか、必要に応じて先人の方々の記事を参考にするとよいかと思います。
:::


# 第四章：私が今のVRChatに思うこと
そろそろまとめに入りたい頃合いですが、もう少しだけお付き合いください。

冒頭で「**要求スペックがAAAタイトル並み**」とふざけ半分に言いましたが、この点に関しては「おふざけ」で終わらせるべきではないと私は思っています。
やれ「VRAMは16GB欲しい」だとか、「世間のミドルレンジはローエンド」だとか
VRでのゲームプレイに、通常より高い処理能力が要求されるのは事実です。
しかしそれを踏まえても、今のVRChatの環境は**異常である**と私は考えます。

実のところ、VRChatがSteam上に掲示している推奨GPUは、"**Geforce GTX 970**"です。
推奨スペックが**長年更新されていない**ことも一因ではありますが、現在が当初の想定から逸脱した環境であるのもまた事実です。
ではどうしてこんな環境になってしまったのか。
他のプレイヤーを悪く言うつもりはなく、かつこれは私自身への戒めでもありますが、ひとえに「**最適化不足**」と言わざるを得ないのが現状です。
稀に「VRChatは重い・最適化不足」との言葉を耳にしますが...
### 違います
### 重いのは**我々プレイヤーのアバター、そしてワールドです**

ここで思い出して頂きたいのは、VRChat公式の掲げるスローガンです。

***Create, Share, Play***

我々プレイヤーは、プレイヤーであるとともにクリエイターでもあるのです。
何かを売っていなくても、世に出していなくても
自分好みの姿にカスタマイズしたアバターは、**各々の立派な創作物**です。

だからこそ！
そのアバターを気兼ねなく、最高の状態で使うためにも！
クリエイターとして、是非とも最適化に目を向けて頂きたい、と思うのです。

ここで大事なのは「**目を向けること**」であって、パフォーマンスランクやこの記事で触れた基準など**ではありません**。
たとえほかの人よりアバターが重くとも、最適化の意識を持つことがまず重要なのです。

そうしてプレイヤーに最適化意識が浸透し、いつしかそれが当たり前の行為になる。
勝手ながら、私はそんな理想をこの記事に込めています。


# 第五章：まとめ
今回の記事を通じて、私が皆様に伝えたかったことは
+ **ダウンロードサイズの減らし方**
+ **描画の仕組みと負荷の低減**
+ **パフォーマンスランクとの付き合い方**

そして
+ **プレイヤー各々が最適化意識を持つようになって欲しい**

ということです。
**無理にやれとは決して言いません。**
気が向いたときに少しでも取り組んでいただければ、それで充分です。

では長くなりましたが、今回はここまでです。
追加の情報や間違っている部分の修正など、今後も更新する可能性はあるので、その時はまたよろしくお願いします。
では皆様、よいVRChatライフを！


# おまけ
今回参考資料として使ったアバターは、CRIM SODA氏作の「Lemone」ちゃんです。
かわいい

https://booth.pm/ja/items/4222437

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2016616/ee535a7f-4521-bc9c-c70a-2e89bc492261.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2016616/be09c8ca-ca67-13ec-7eed-cf42b32523df.png)