AvatarMakerを支える技術 (cluster)
これは クラスター Advent Calendar 2021、8日目の記事です。
yutoppです。今はクラスターでソフトウェアエンジニアをしています。
さて、11/1にAvatarMakerという機能がリリースされました。めでたいですね。
今回は、このAvatarMakerの技術的な背景の一部分を紹介したいと思います。こんな感じで動いているんだな~と楽しんでいただけたら幸いです。
要件
基本的な部分に関しては以下のようになっています。
03の 顔の造形を変更できること
は、プロシージャルに目のパーツや輪郭などが動くようになるわけでアバターの表現力が広がります。
一方で、メッシュのめり込みなど発生しやすくなりますし、デザイン・3Dモデル作成の量産・品質調整の難易度とのトレードオフになります。
04の 社内外のクリエイターがアバターのパーツをアップロードできること
は、シンプルにやることが多くなります。
データの作成を開かれた技術・適切な権限で、セットアップの手順やトラブルシュートも社外の第三者が問題なく行える品質を見据えて設計・実装をする必要があります。
大変ですね。
設計
新たにglTFをやり取りするフローと、clusterからVRMをアップロードするフローを追加しています。
ユースケースとしては、
- クリエイターはDCCツールとUnityでアバターパーツを作り、glTFで出力
- AvatarMakerは、そのglTFを組み合わせてアバター全体を編集
- 編集が終わったらAvatarMakerがVRMとしてアバターを保存
- 以降、通常のアバターとして利用可能
を考えていて、clusterの既存のアバターのエコシステムと共存するようにしています。
実は、別にAvatarMakerはホームでなくても実行でき、VRMとして保存しなくてもアバターで動き回れる設計にしてあるので、そのうちワールドやイベントでリアルタイムに着替えながら遊べる未来もできたらいいですね。今後に期待…
実装
全体
AvatarMakerはライブラリとスタンドアロンの開発実験アプリとして独立して作りはじめ、その後clusterにライブラリとして組み込みました。
クリエイターがパーツをアップロードできるようにするためには、アップロード部分のコードを配布する必要がありますし、動作確認用のミニマルなソフトウェアも必要になることを見越してこうしています。
メリットとしては、最小限の依存関係でビルドが高速なので、開発イテレーションをたくさん回せる点。また、ライブラリとして設計することにより境界が明確になるため、アプリケーション本体への影響が読みやすい点などを感じています。
データ形式:glTF
独自形式にするとUGCとの親和性が微妙なので、今回はオープンなデータ形式であるglTF2.0を採用しました。
AvatarMakerのパーツは上記のようにhumanoidの状態でメッシュ分割されており、それぞれが独立したglTFのバイナリ形式で配布されます(clusterのVRMアバターと同じく難読化されます)。 それぞれに、”瞳のテクスチャはどれ” だとか ”肌のマテリアルはどうなっている” といった様々な情報を、glTFのextrasに詰めています。
VJson, VGltf
上記で書いたとおりglTFの拡張をかなり利用するつもりだったので、丁度いい実装はないかなということで自作のライブラリをプロダクトに導入しました。glTFのimport/exportと、VRMのexportに利用しています。
この点は技術選定で弊社TechLeadにも相談したのですが、自作ライブラリの導入にGoサインが出るのはなかなか狂気で良いなと密かに思いました。
forkせずに公開されている実装をそのまま使っています。glTFの拡張をヘビーに使っていますが、ライブラリのコードベースには一切手を加えずに対応できているので便利だと思います。
サーバーサイド合成 vs クライアントサイド合成
有料のパーツなどが発生した場合にサーバーサイドでパーツを合成したほうが堅牢だという話がありましたが、今回はクライアントサイドで合成しています。
既存VRMアバターと同様の難読化形式で配信する前提を置けば、Unity側で合成したほうがリアルタイムに着替えられますし、パーツごとに細かくデータをロードできてさわり心地も良いと思われます。デバッグもしやすいですしね。
パーツ合成、切り替えで毎回やるか・export時に一度か
AvatarMakerのパーツは切り替えるたびにhumanoidのskeletonを差し替えて1つのAvatarに合成しています。
最初は複数のパーツごとにhumanoidを重ねて描画していました。これは、skeletonの付け替えコストが重たいと想像していたためです。
しかし、skeletonの付け替えを試したところ全くコストにならず、フレーム落ちにも気づかない程度だったので、今はhumanoidのボーン構造をマージしたものに付け替えて単一のhumanoidを作るようになっています。
テクスチャ合成・カラーバリエーション
AvatarMakerは、瞳・ハイライト・眉毛・まつ毛・アイラインなどなど(他にもたくさんあります)、1つのパーツにテクスチャがもりもりに入っています。
これらの複数のテクスチャを重ねて描画し・色味の変換も行うシェーダを今回実装しています。シェーダで描画しているのでリアルタイムに合成を行っても高速で感動しました。
最初はイケイケのCustomRenderTextureを用いて実装していたのですが、モバイルで真っ黒になるなど破滅したので、古典的な1x1のmeshをorthographicに描画するシェーダに途中で書き換えています。悲しいですが枯れた技術は今も最高ということです。
マテリアル問題
clusterのカスタムアバター制限に収まるようにテクスチャとマテリアルを設計しています。AvatarMaker専用の特製アバターアップロードAPIなどはなく、平等に同じAPI経由で作っています。
マテリアルの質感が同じ部分は、上記のシェーダでテクスチャを1枚に重ねてマテリアルを1つにまとめるなど工夫しています。アバター上限解放が先にリリースされていて欲しかったですね(乞うご期待…)
メモリ枯渇問題
開発中は最高のテクスチャをそのままに2kでデータを配布していましたが、案の定 iOSデバイス がメモリ枯渇でクラッシュするようになりました(それはそう)。
なので、すべて1kテクスチャに変換し合成時に1テクスチャのみ2kにupconvertするようにしています。なんと、2kのテクスチャを1kにするとメモリ使用量が4分の1になります(それはそう)。
いまよく考えるとupconvertもしなくても良い気がしてきたので、今後軽量化する可能性があります。
clusterでVRMアバターにした状態で使う場合は、サーバーで軽量化されて配信されているのでそこまで負荷にはなっていないはずです。
IL2CPP対応
想像の5億倍 IL2CPPで問題が発生しました。やはり自作ライブラリを動かすときはIL2CPPが鬼門になると学びましたね…。
一部ですがどのような罠にハマったかを紹介します(Unityは2019.4.22fです)。
- checkedの範囲が変わる
- Type/TypeInfoのGetCustomAttributesで返ってくるインスタンスがキャッシュされていそう (実装依存っぽいですが…)
- code strippingによって静かに実行時エラーが発生
最後の方は (Windows, Mac, iOS, Android, Linux) x IL2CPPビルドで動作確認するような用心深さになりました。
さいごに
いかがでしたか?
開発以外にもたくさんの人々の力によってリリースされたAvatarMakerの技術的裏側の紹介でした。今後も開発を続けていくので、よろしくお願いいたします。
クラスター広告
クラスターは本物の3DCGクリエイターを募集しています。
謝辞
【Jump King】最難関超鬼畜ゲーをクリアします!!!ぺこ!【ホロライブ/兎田ぺこら】 - YouTube
開発初期にこの配信を見て精神を保っていました。感謝します。