yutopp's blog

サンドバッグになりたい

AvatarMakerを支える技術 (cluster)

これは クラスター Advent Calendar 2021、8日目の記事です。

qiita.com


yutoppです。今はクラスターでソフトウェアエンジニアをしています。

さて、11/1にAvatarMakerという機能がリリースされました。めでたいですね。

note.com

今回は、このAvatarMakerの技術的な背景の一部分を紹介したいと思います。こんな感じで動いているんだな~と楽しんでいただけたら幸いです。

要件

基本的な部分に関しては以下のようになっています。

  1. cluster内でアバターを作成できること (VR/NonVR)
  2. 服装を切り替えられること
  3. 顔の造形を変更できること
  4. 社内外のクリエイターがアバターのパーツをアップロードできること

03の 顔の造形を変更できること は、プロシージャルに目のパーツや輪郭などが動くようになるわけでアバターの表現力が広がります。 一方で、メッシュのめり込みなど発生しやすくなりますし、デザイン・3Dモデル作成の量産・品質調整の難易度とのトレードオフになります。

04の 社内外のクリエイターがアバターのパーツをアップロードできること は、シンプルにやることが多くなります。 データの作成を開かれた技術・適切な権限で、セットアップの手順やトラブルシュートも社外の第三者が問題なく行える品質を見据えて設計・実装をする必要があります。

大変ですね。

設計

f:id:yutopp:20211207181752p:plain
AvatarMaker増築前

f:id:yutopp:20211207181906p:plain
AvatarMaker増築後

新たにglTFをやり取りするフローと、clusterからVRMをアップロードするフローを追加しています。

ユースケースとしては、

  • クリエイターはDCCツールとUnityでアバターパーツを作り、glTFで出力
  • AvatarMakerは、そのglTFを組み合わせてアバター全体を編集
  • 編集が終わったらAvatarMakerがVRMとしてアバターを保存
  • 以降、通常のアバターとして利用可能

を考えていて、clusterの既存のアバターのエコシステムと共存するようにしています。

実は、別にAvatarMakerはホームでなくても実行でき、VRMとして保存しなくてもアバターで動き回れる設計にしてあるので、そのうちワールドやイベントでリアルタイムに着替えながら遊べる未来もできたらいいですね。今後に期待…

実装

全体

f:id:yutopp:20211207182129p:plain
コード構成

AvatarMakerはライブラリとスタンドアロンの開発実験アプリとして独立して作りはじめ、その後clusterにライブラリとして組み込みました。

クリエイターがパーツをアップロードできるようにするためには、アップロード部分のコードを配布する必要がありますし、動作確認用のミニマルなソフトウェアも必要になることを見越してこうしています。

メリットとしては、最小限の依存関係でビルドが高速なので、開発イテレーションをたくさん回せる点。また、ライブラリとして設計することにより境界が明確になるため、アプリケーション本体への影響が読みやすい点などを感じています。

データ形式:glTF

独自形式にするとUGCとの親和性が微妙なので、今回はオープンなデータ形式であるglTF2.0を採用しました。

f:id:yutopp:20211207182419p:plain

AvatarMakerのパーツは上記のようにhumanoidの状態でメッシュ分割されており、それぞれが独立したglTFのバイナリ形式で配布されます(clusterのVRMアバターと同じく難読化されます)。 それぞれに、”瞳のテクスチャはどれ” だとか ”肌のマテリアルはどうなっている” といった様々な情報を、glTFのextrasに詰めています。

VJson, VGltf

上記で書いたとおりglTFの拡張をかなり利用するつもりだったので、丁度いい実装はないかなということで自作のライブラリをプロダクトに導入しました。glTFのimport/exportと、VRMのexportに利用しています。

github.com

github.com

この点は技術選定で弊社TechLeadにも相談したのですが、自作ライブラリの導入にGoサインが出るのはなかなか狂気で良いなと密かに思いました。

forkせずに公開されている実装をそのまま使っています。glTFの拡張をヘビーに使っていますが、ライブラリのコードベースには一切手を加えずに対応できているので便利だと思います。

サーバーサイド合成 vs クライアントサイド合成

有料のパーツなどが発生した場合にサーバーサイドでパーツを合成したほうが堅牢だという話がありましたが、今回はクライアントサイドで合成しています。

既存VRMアバターと同様の難読化形式で配信する前提を置けば、Unity側で合成したほうがリアルタイムに着替えられますし、パーツごとに細かくデータをロードできてさわり心地も良いと思われます。デバッグもしやすいですしね。

パーツ合成、切り替えで毎回やるか・export時に一度か

AvatarMakerのパーツは切り替えるたびにhumanoidのskeletonを差し替えて1つのAvatarに合成しています。

最初は複数のパーツごとにhumanoidを重ねて描画していました。これは、skeletonの付け替えコストが重たいと想像していたためです。
しかし、skeletonの付け替えを試したところ全くコストにならず、フレーム落ちにも気づかない程度だったので、今はhumanoidのボーン構造をマージしたものに付け替えて単一のhumanoidを作るようになっています。

テクスチャ合成・カラーバリエーション

AvatarMakerは、瞳・ハイライト・眉毛・まつ毛・アイラインなどなど(他にもたくさんあります)、1つのパーツにテクスチャがもりもりに入っています。

f:id:yutopp:20211207182754p:plain

これらの複数のテクスチャを重ねて描画し・色味の変換も行うシェーダを今回実装しています。シェーダで描画しているのでリアルタイムに合成を行っても高速で感動しました。

最初はイケイケのCustomRenderTextureを用いて実装していたのですが、モバイルで真っ黒になるなど破滅したので、古典的な1x1のmeshをorthographicに描画するシェーダに途中で書き換えています。悲しいですが枯れた技術は今も最高ということです。

answers.unity.com

マテリアル問題

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です)。

最後の方は (Windows, Mac, iOS, Android, Linux) x IL2CPPビルドで動作確認するような用心深さになりました。

さいごに

いかがでしたか?

開発以外にもたくさんの人々の力によってリリースされたAvatarMakerの技術的裏側の紹介でした。今後も開発を続けていくので、よろしくお願いいたします。

クラスター広告

クラスターは本物の3DCGクリエイターを募集しています。

herp.careers

herp.careers

herp.careers

謝辞

【Jump King】最難関超鬼畜ゲーをクリアします!!!ぺこ!【ホロライブ/兎田ぺこら】 - YouTube

開発初期にこの配信を見て精神を保っていました。感謝します。