AITuberが棒立ちで無表情だと、どうしても機械っぽく見えてしまいます。「結城のあ」では、嬉しい話のときは笑顔、困った話のときは困り顔、というように、会話の感情に合わせてアバターの表情を切り替えています。表情を出すのは VTube Studio(VTuber向けのアバター表示ソフト)で、外部から操作するために Public API を使っています。
感情はどこから来るのか
表情の切り替えに使う感情ラベルは、会話の応答を生成するときに、LLMがテキストと一緒に出力しています。返答文と感情を一度のLLM呼び出しでまとめて出す設計の話は 全体構成の記事 に書きました。ここでは、受け取った感情ラベル(喜び・困り・驚き…といった19種類ほど)を、どうやってアバターの表情に反映するかを扱います。
VTube Studio Public API の基本
VTube Studio は、WebSocket 経由でアバターを外部操作できる Public API を備えています。最初に認証トークンを取得して接続を確立し、あとは目的に応じたリクエストを JSON で送る形です。表情の操作に使うのが ExpressionActivationRequest です。
- あらかじめ VTube Studio 側で、感情ごとの表情(exp ファイル)を用意しておく
- API で表情ファイル名を指定し、
activeを true/false で切り替える - true でその表情をON、false でOFFにできる
つまり「喜びの表情をONにして、ほかをOFFにする」を感情ラベルが変わるたびに送れば、表情が切り替わります。理屈は単純なのですが、ここに落とし穴がありました。
つまずき: 表情の切り替わりがちらつく
素直に実装すると、「今ONになっている表情を全部OFF → 目的の表情をON」という順序になりがちです。ところがこれをやると、切り替えの一瞬だけどの表情もONでない「素の顔」が見えてしまい、パッ…パッと表情がちらつきます。配信で見るとかなり気になります。
OFFにする処理とONにする処理の間に、わずかですが「何も表情が出ていない瞬間」が生まれる。これがちらつきの正体でした。
直し方: 先に目的の表情をONにしてから、他を消す
順序を逆にするだけで消える
解決はシンプルで、順序を逆にするだけです。「先に目的の表情をONにして」から「それ以外の表情をOFFにする」。こうすると、切り替えの瞬間も常にどれかの表情がONになっているので、素の顔が一瞬たりとも見えず、ちらつきがなくなります。
処理の流れを言葉にするとこうなります。
- 新しい感情に対応する表情を
ExpressionActivationRequestで先にONにする - そのあとで、ONになっている他の表情を順にOFFにする
- すでに目的の表情がONなら、何もしない(無駄な再送をしない)
「全消ししてから付け直す」という直感的な手順が、実は見た目を悪くしていた、という分かりやすい例でした。状態の切り替えで一瞬の「空白」を作らない、という考え方は表情に限らず応用が効きます。
音声との同期
表情だけ先に変わって、声が後から来るとズレて見えます。「結城のあ」では、感情ラベルを VOICEVOX の音声合成パラメータにも反映していて、表情の切り替えと発話のタイミングをできるだけ揃えるようにしています。喜びの声色のときは喜びの表情、というように、声と顔の感情が一致していると、ぐっと生きているように見えます。
まとめ
感情に合わせた表情切り替えは、VTube Studio の ExpressionActivationRequest を送るだけ…と思いきや、「OFFしてからON」だとちらつくという罠があります。先にONしてから他をOFFにするだけで解決します。小さな順序の違いですが、配信の見た目の自然さに直結する大事なポイントでした。
続けて読む: ローカルLLMの全体構成 / pgvectorで長期記憶を持たせる / 自宅k3sでAI基盤を組む
実際に表情が動く様子は noa.aituber-tkg.com や配信でどうぞ。