こんにちは、VRoid部のkeshigomuです。
普段は主にVRoid Hubのフロントエンドエンジニアとして、3Dキャラクターを表示するビューワーの開発に携わっています。また@pixiv/three-vrmという、Web上で3Dモデルを使ったコンテンツを開発するためのOSSライブラリの運用も行っています。
今回、ブラウザで簡単に3Dキャラクターと会話できる技術デモ「ChatVRM」とそのコードをオープンソースで公開しました。
「ChatVRM」は、テキスト・口頭で話しかけた言葉にキャラクターがフルボイスで回答してくれる「キャラクターと会話できる」デモです。WEBブラウザ上で動作でき、3Dキャラクターのインポート・切り替え、キャラクターに併せて声を調整することもできます。
(2023/07/10追記) 読み上げ音声の生成に使用していたKoeiro APIの提供終了に伴い、以前のデモとコードは動作しなくなりました。 正式リリースされたKoemotionのKoeiromap 1.0 APIに対応したデモとコードはChatVRMのリポジトリをご確認ください。
デモページとサンプルコードについて
このデモは、GitHubPagesからお試しいただけます。
▼デモはこちら
https://chatvrm.glitch.me
※実際に動かすには、ChatGPTのAPI keyが必要です
また、GitHub上でオープンソースでコードを公開しています。
ブラウザで3Dキャラクターとコミュニケーションするサービスや機能を実装する際の参考に自由にお使いいただけます。
▼サンプルコードはこちら
https://github.com/pixiv/ChatVRM
「ChatVRM」の技術概要
「ChatVRM」における、3Dキャラクターとのテキスト・音声での会話部分の実装は、以下に紹介する4つの技術によって実現されています。
①3Dキャラクターの表示
@pixiv/three-vrm
https://github.com/pixiv/three-vrm
②ユーザーの音声の認識
Web Speech API(SpeechRecognition)
https://developer.mozilla.org/ja/docs/Web/API/SpeechRecognition
③返答文の生成
ChatGPT API
https://platform.openai.com/docs/api-reference/chat
④読み上げ音声の生成
Koeiro API
http://koeiromap.rinna.jp/
①3Dキャラクターの表示
ブラウザ上での3Dキャラクターの表示には@pixiv/three-vrm
を使用しています。
@pixiv/three-vrm
はthree.js を使ってブラウザ上でVRMを読み込んで表示するためのライブラリであり、ピクシブがオープンソースとして公開しています。
自社内では、VRoidHubの開発に利用しており、個人企業問わずWEBアプリケーションで使われています。
three.jsと@pixiv/three-vrmを使ってVRMを読み込むコードは以下になります。
import * as THREE from 'three'; import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; import { VRMLoaderPlugin } from '@pixiv/three-vrm'; const scene = new THREE.Scene(); const loader = new GLTFLoader(); loader.register((parser) => { return new VRMLoaderPlugin(parser); }); loader.load( '/sample.vrm', (gltf) => { const vrm = gltf.userData.vrm; scene.add(vrm.scene); } );
詳しくはhttps://github.com/pixiv/three-vrmを確認してください。
今回のデモでは3Dキャラクターには以下のように簡単なリップシンクや、
src/features/vrmViewer/model.ts
public update(delta: number): void { if (this._lipSync) { const { volume } = this._lipSync.update(); this.emoteController?.lipSync("aa", volume); } ...
定期的な瞬きをしてもらっています。
デモの該当箇所はsrc/features/emoteController/autoBlink.tsです。
public update(delta: number) { if (this._remainingTime > 0) { this._remainingTime -= delta; return; } if (this._isOpen && this._isAutoBlink) { this.close(); return; } this.open(); } private close() { this._isOpen = false; this._remainingTime = BLINK_CLOSE_MAX; this._expressionManager.setValue("blink", 1); } private open() { this._isOpen = true; this._remainingTime = BLINK_OPEN_MAX; this._expressionManager.setValue("blink", 0); }
②ユーザーの音声の認識
ユーザーの音声の認識にはWeb Speech APIのSpeechRecognitionを使用しています。
マイクボタンを押してユーザーにマイクの使用を許可してもらった後、
result
イベントでテキストとして受け取りつつ随時表示し、発言の終了時に返答文の生成へ移行しています。
デモでの該当箇所はsrc/components/messageInputContainer.tsx
です。
const SpeechRecognition = window.webkitSpeechRecognition || window.SpeechRecognition; const recognition = new SpeechRecognition(); recognition.lang = "ja-JP"; recognition.interimResults = true; // 認識の途中結果を返す recognition.continuous = false; // 発言の終了時に認識を終了する recognition.addEventListener("result", handleRecognitionResult); recognition.addEventListener("end", handleRecognitionEnd);
// 音声認識の結果を処理する const handleRecognitionResult = useCallback( (event: SpeechRecognitionEvent) => { const text = event.results[0][0].transcript; setUserMessage(text); // 発言の終了時 if (event.results[0].isFinal) { setUserMessage(text); // 返答文の生成を開始 onChatProcessStart(text); } }, [onChatProcessStart] )
③返答文の生成
返答文の生成にはChatGPT APIを使用しています。デモではGPT-3を使用するように設定しています。
始めの応答を出来るだけ早くするためにStreamで随時受け取り一文ごとに読み上げ音声の生成を行っています。
https://github.com/openai/openai-cookbook/blob/main/examples/How_to_stream_completions.ipynb
デモでは以下のような文章をsystemロールで送っています。
この返答に含まれる[happy]
などの文字からキャラクターの表情か声色を変更しています。
... 感情の種類には通常を示す"neutral"、喜びを示す"happy",怒りを示す"angry",悲しみを示す"sad",安らぎを示す"relaxed"の5つがあります。 会話文の書式は以下の通りです。 [{neutral|happy|angry|sad|relaxed}]{会話文} あなたの発言の例は以下通りです。 [neutral]こんにちは。[happy]元気だった? ...
④読み上げ音声の生成
読み上げ音声の生成(Text-to-Speech)にはKoeiro APIを使用しています。
Koeiromap と Koeiro APIでは2つのパラメータを指定することで、様々な声色の音声を生成することができます。
モデルに合わせた声色をユーザーが自由に調整できるので、今回のデモに非常に適しています。
Koeiromap と Koeiro APIの使い方や利用規約は
http://koeiromap.rinna.jp/
をご確認ください。
また、Koeiromap と Koeiro APIでは喜怒哀楽などの感情ごとに音声を生成することが出来るので、
生成された返答文に含まれる[angry]
等の感情タグからstyleを設定し、音声での喜怒哀楽を表現しています。
デモでの該当箇所はsrc/features/koeiromap/koeiromap.tsです。
import { TalkStyle } from "../messages/messages"; export async function synthesizeVoice( message: string, speaker_x: number, speaker_y: number, style: TalkStyle ) { const param = { method: "POST", body: JSON.stringify({ text: message, speaker_x: speaker_x, speaker_y: speaker_y, style: style, }), headers: { "Content-type": "application/json; charset=UTF-8", }, }; const koeiroRes = await fetch( "https://api.rinna.co.jp/models/cttse/koeiro", param ); const data = (await koeiroRes.json()) as any; return { audio: data.audio }; }
基本的なところは以上です。
ChatGPT APIの返答文からKoeiro APIの声色とVRMの表情を変更することで、より生き生きとした印象を与えることができました。
特定のキャラクターに特化した表現などもあると思います。その場合は設定メニューからsystemロールの文章を変更したり、リポジトリからフォークしてぜひ機能追加などをしてみてください。
最後に
「ChatVRM」はオープンソースで展開しておりますので、3Dキャラクターとのコミュニケーションを可能にするサービスや機能の参考に積極的に活用してください。
ピクシブ社は、「未来にあって当たり前のものを作り続ける」ことを目指して、日々プロダクトを開発しております。
おまけ
今回実装した待機モーションは、mocopiで収録したBVHデータを、現在仕様策定中のVRMアニメーション形式へ変換し使用しています。
興味がある方はぜひ以下の「ChatVRM」のコードや、VRMの現状の仕様を確認してみてください。
github.com
デモの該当箇所はsrc/lib/VRMAnimation/VRMAnimation.tsです。