エムスリーテックブログ

エムスリー(m3)のエンジニア・開発メンバーによる技術ブログです

3Dアバター化しながらプレゼンするWebアプリを作った話

f:id:owl-m3:20191218160740p:plain (デモ用の3Dモデルは「ニコニ立体ちゃん」をお借りしています)

概説

この記事は エムスリー Advent Calendar 2019 の19日目の記事です。

好きなマスコットはGopherくん、好きな言語はRuby! 最近の業務は主にJavaScriptとKotlin! エムスリーエンジニアリンググループのowlです。

さて、弊社内で毎週行われる『テックトーク』ではエンジニアが好きな技術の話を発表し合っています。最近になってこの発表を画面/音声キャプチャして保存し、後からいつでも発表内容を視聴できるシステムが構築されました。🎉

www.m3tech.blog

ただ、このシステムでは当然ながら発表者の姿が録画内容には含まれません。もし、プレゼン中の画面にアバター化した発表者を常時表示すれば発表の雰囲気をより良く伝えることが出来るはずです。今回はこの課題を解決するアバター表示プレゼンWebアプリを開発しました🎉。本当は自分がどうしてもGopherくん(のアバター)になりたかったので作っただけですが。

最近ではこのようにアバターを表示させながらプレゼンする人々をしばしば見ることが出来ます。例えばProduct Manger Conferenceでピクシブ社の方がアバター登壇しているのが有名ですね。いつか見慣れた光景になる可能性もまったくないとは言えません。そうゼロではない。

技術スタック

このアプリの開発にはいくつか制約があります。まず「MacBookに標準装備されているWebカメラ以外のデバイスを使わないこと」。次に「インストール不要であること」。要するにサクっと気軽に使えることを目的とします。そもそもこういった要件を気にしなければ既存のソフトウェアを併用するだけで開発せずに実現できる要件でもあります。

ここでは制約をクリアするために開発をすべてJavaScriptだけで行います。

  • Vue.js
  • TypeScript
  • Buefy
  • three-js
  • three-vrm
  • clmtrackr-js

three-js/three-vrm

まずWebGLで3Dモデルを表示するためにthree-jsライブラリ、そしてthree-jsで3Dモデル形式の一つであるVRMを扱うためにpixiv/three-vrmを利用します。これらによってまずVRM形式の3Dモデルを画面上に表示し任意のパラメータを設定することでポーズ/表情を操作することが出来ます。

VRMは2018年から公開されたファイル形式ですが、ドワンゴ社やピクシブ社によってUnityやWebGLで扱えるようになったりモデリングツールが提供されたりと新規ながら既に活用の幅が広い新鋭ファイル形式なので今回も採用していきます。

clmtrackr-js

今回のアプリでは比較的ポピュラーな方法である顔認識によるトラッキングを実装しています。それを実現するライブラリがclmtrackr-jsです。Webカメラの入力をclmtrackr-jsでリアルタイム解析することによって取得できるのは眼や口に鼻といった各パーツのX,Y座標であり、一方で3Dモデル側の各Boneパラメータはいわゆる角度であるため少しややこしい変換が必要になります。なお、ナイーブに各パーツの相対距離などから算出してみても良い感じにならなかったため地道にチューニングします。

実装

実装すること自体はシンプルです。

まずthree.jsのcameraやrendererなどを初期化、モデルのロードをしてキャンバスを作成しておきます。生成されるプレゼン資料用のiframe部分に被るようにCSSを組めばUI部分は終わりです。前述の通りプレゼン資料の部分はただのiframeなので読み込み先のサーバが許容すればWebページなら何でも表示可能です。今回はすべてJavaScriptでやるというコンセプトなのでreveal.jsというスライドショーを作成するjsライブラリを使っています。

モデル表示をするための実処理としては、Webカメラからの入力をclmtrackr-jsが解析し、数値を3DモデルのBody用に変換・設定して表示しなおす、というループをrequestAnimationFrameで回し続けています。

工夫をつける

まばたき

clmtrackr-jsのトラッキングはFaceRigなどの(界隈で)一般的なツールに比べると精度がやや劣り、特に瞬きの検知については基本的に出来ません。一般にも顔認識の瞬きは対象が眼鏡をかけていると検出されにくく、またエンジニアはだいたい眼鏡をかけているため不可避的な問題が発生しがちです。

特に、まぶたの開閉は左右それぞれで検出できたり出来なかったりするため同期がズレてしまい片目だけ閉じた状態になってしまうなど不自然な表示になりがちです。この問題に対するよく知られたハックとして、モデル側の両目と人間側の片目だけを同期させることでウィンクは出来ませんが代わりに自然な瞬きに見せることが出来ます。

しかし今回の場合はそもそも片目でも検知できないので更に踏み込んでまばたきはカメラ入力と同期せずに一定期間で自動的に開閉するようにします。実質モーションの組み込みでカメラ入力と同期させることを諦めているわけですが、このタイプの用途では「リアルなトラッキング」より「実物よりも良い感じに見えるトラッキング」のほうが望ましいので問題ありません。

リップシンク

まばたきと同様に口の動きもトラッキングするのが難しい箇所です。特にVRM形式では口の開閉に関するパラメータがA-I-U-E-Oの5種類に分かれています(つまり母音ごとに異なる口の開き方をコントールできる)。しかし、この顔認識ライブラリが出せる精度でこれらを厳密に適用するのは難しいのでこのアプリでは単に口の開閉度を算出してAパラメータと同期させています。

アプリには未実装ですがこの問題に関するよく知られたハック手法として、リップシンク(唇の同期)をカメラ入力で行うのはでなく、代わりにマイク入力の音量をソースにして同期させる方法があり比較的容易に自然な表示が可能になります。

パラメータのバリデート

トラッキングの精度があまり良くないとトラッカーが異常値を出力してしまうことがしばしばあり、その異常値を素直にパラメータに設定してしまうとモデルの首が絶対に曲がっちゃいけない角度へ曲がってしまい放送事故になります。VRM自体には関節の可動域という概念は存在せずBoneは360度(肩で言えば体の内側方向も含む)すべて曲げることが可能なためです。

そこで心臓の弱い視聴者に配慮すべく、モデル表示に反映する入力値のレンジを決めておいてレンジを越えるような値が来た場合は表示反映をスキップするという実装をしています。そもそも入力変換時点で適切な実装がされていれば発生しないのかもしれませんが。

まとめ

f:id:owl-m3:20191219112022g:plain

無事に動くようになりましたがトラッキングの解析値が微妙にブレるため、これを滑らかに見せる工夫の余地がまだまだありそうです。デモgifでは胴体も動いていますが体の向きをトラッキングしている訳ではなく、顔の角度を弱めたものを適用してそれっぽくしています。

アバター化を活用して良いプレゼンライフを!

余談

一般的にはプレゼンをする時、発表者は立って話をしますね。
このときノートPCは発表者の腰あたりに置かれます。するとWebカメラが顔を写せません。つまり顔認識ができません。それはそう。

なるほど、盲点でしたね。

慌てずに空気椅子をすることで発表時のデモは乗り越えました。この課題については次からは椅子というツールを利用して対応しようと思っています。

f:id:owl-m3:20191218181419p:plain

これはボクセルアートで3Dモデルが作れないかと思って試作してみた可愛いGopher f:id:owl-m3:20191218160745j:plain

We are hiring!

今のところ可愛い3Dモデルを作る業務はありませんが、JavaScriptを上手に書ける人などエンジニアは常に募集中です。また記事でも言及したように社内のエンジニアが毎週末に数名ずつ好きな技術トークをしているので興味があればぜひオフィスへ遊びに来て下さい! 発表の自由さはこの記事の通りです。

エンジニアリンググループ 募集一覧

jobs.m3.com