Help us understand the problem. What is going on with this article?

UniVRM+SteamVR+Final IKで始めるVTuber

More than 1 year has passed since last update.

はじめに

この記事はVTuber Tech #1 Advent Calendar 2018の2日目の記事です
昨日の記事は@nkjzmさんによる、【初心者向け】UnityとLive2Dで拡張しやすいVTuber配信システムを作る方法です。

こんにちは。あきら(@sh_akira)です。
普段はバーチャルモーションキャプチャーというVRMモデルを操作するアプリを作っています。
今回はそれとは別のアプローチで自分だけのVTuber環境を作ってみようという記事です。

VR機器で3Dモデルを動かして、いわゆるVTuberのようなことが出来るようになるまでを
昨日の記事と同じくUnityを初めて触る人でも分かるように開発環境のインストールから全て説明しています。

今回モデルのファイル形式にVRM形式を使用しています。
VRM形式については、VRMの何がいいの?という話 こちらに分かりやすくまとまっていますのでご覧ください。

記事中のソースコードはパブリックドメインです。プラグインについては各ライセンスに従ってください。

完成品

必要な機材

  • HTC Vive (Oculus/WinMRは今回説明しませんが基本的に手順は変わらず動くはずです)
  • VR Ready PC (Windows 10です)

必要なプラグイン

  • UniVRM 0.45 (VRMモデルの読込をします)
  • SteamVR plugin 2.0.1 (VR機器を扱います) *** 最新だとエラーになるそうです。コメント欄参照ください ***
  • Final IK 1.8 (有料$90です。定期的にセールで半額になったりします。)
  • OVRLipSync 1.28.0
  • AniLipSync-VRM 1.0.0

使用モデル

  • ニコニ立体ちゃん(アリシア・ソリッド)のVRMモデル

開発環境

  • Unity 2017.4.15f
    最近VRChatの開発環境がこのバージョンになったので今回は同じバージョンで開発します。

開発環境構築

Unityのインストール

まずはUnityをインストールします。

ダウンロードページからUnity 2017.x内にあるUnity 2017.4.15のUnity インストーラーをダウンロードします。
image.png

ダウンロードされたUnityDownloadAssistant-2017.4.15f1.exeを実行してください。
基本的にはデフォルトの設定のままNextで進んでいき、
image.png
インストール先の指定でそのUnityのバージョン名をフォルダにつけると複数のバージョンのUnityがインストールできます。
今回は"C:\Program Files\Unity 2017.4.15f1"にしました。

image.png
インストールが完了すると"Launch Unity"のチェックがあるので、チェックを入れたままFinishしてUnityを実行します。

プロジェクトの設定をする

※初めてインストールした場合この段階でUnityのアカウント登録やログインが必要になる場合があります。
画面の指示に従ってログインしてください。

image.png
Unityを起動するとプロジェクトを開く画面が表示されるので右上のNewを押します。
image.png
Project Nameに好きなプロジェクト名を付けます。今回はMyVTuberとしました。
良ければCreate Projectを押します。

Unityの画面が表示されますが、分かりやすいようにレイアウトを変更します。
image.png
画面右上のDefaultを2by3に変更します

現在のScene(シーン)を保存します
image.png
画面中央のHierarchy内にあるUntitledを右クリックしてSave Scene Asを押し、シーンを保存します。
image.png
今回はそのままAssetsフォルダにプロジェクト名と同じMyVTuberという名前を付けて保存します。

Project内に今保存したMyVTuberが現れますが、このままだとアイコンが大きく見づらいため
image.png
画面下部のバーを一番左にしてリストに変更しておきます。

必要なアセットをインポートする

SteamVR Plugin

image.png
上部のWindowメニューからAsset Storeを開きます
image.png
検索欄にSteamVRと入力すると出てくるSteamVR Pluginを押します。
image.png
ダウンロードしてインポートを押します。するとImport Unity Packageの画面が出ますので、そのまま右下のImportを押してください。
image.png
その後このような画面が出た場合は、Accept Allを押します。

Unity 2018.3.xを使用する場合、メニュー内のPackageManagerからOpenVRをインストールする必要があります。この手順書通りのバージョンでは関係ありません

Final IK

image.png
次にAsset Storeの検索欄に Finalと入れると出てくるFinal IKを押します。
image.png
持ってない場合は購入して、ダウンロードしてインポートを押します。するとImport Unity Packageの画面が出ますので、そのまま右下のImportを押します。

UniVRM

image.png
UniVRMのリリースページからUniVRM-0.45_0c49.unitypackageをダウンロードします。
image.png
Unity上部のAssetsメニュー->Import Package->Custom Package...を押して、ダウンロードしたUniVRM-0.45_0c49.unitypackageを開きます。
image.png
するとImport Unity Packageの画面が出ますので、そのまま右下のImportを押してください。

image.png
以上で必要なアセットのインポートは完了なので、左上のSceneタブを押して元の画面に戻します。
正しくインポートできていれば右側のProject内のAssetsフォルダの下にPlugins、SteamVR、VRMの3フォルダがあるはずです。

VRMモデルをインポートする

image.png
Project内のAssetsを右クリックして出るメニューからCreate->Folderを選択して出来た新しいフォルダーにModelsと名前を付けます。
image.png
出来たModelsフォルダを右クリックしてShow in Explorerを押してエクスプローラを開きます。
image.png
Assetsフォルダが開くはずなので、Modelsを開いてください。まだ中身は空です。
image.png
ニコニ立体ちゃん(VRM)のページからダウンロードを押してダウンロードしたzipファイルを解凍します。
image.png
Alicia_VRM\Alicia\VRM内にAliciaSolid.vrmファイルがあるので、先ほど開いたModelsフォルダにコピーしてください。
image.png
Unityに戻ると自動でインポートが始まります。終わったらModelsフォルダを開いてください。AliciaSolidのPrefab(青い立方体のアイコン)が表示されていればインポート完了です。
image.png
VRMファイルにはそれぞれライセンスが設定されています。Prefabを選択するとInspector内のVRM Meta (Script)に

アバターの人格に関する許諾範囲(Personation / Charaterization Permission)

  • アバターに人格を与えることの許諾範囲(A person who can perform with this avatar)
    • アバターを操作することはアバター作者にのみ許される(Only Author)
    • 明確に許可された人限定(Explictly Licensed Person)
    • 全員に許可(Everyone)
  • このアバターを用いて暴力表現を演じることの許可(Violent acts using this avatar)
    • 不許可(Disallow)
    • 許可(Allow)
  • このアバターを用いて性的表現を演じることの許可(Sexuality acts using this avatar)
    • 不許可(Disallow)
    • 許可(Allow)
  • 商用利用の許可(For commercial use)
    • 不許可(Disallow)
    • 許可(Allow)
  • その他のライセンス条件(Other License Url)
    • 上記許諾条件以外のライセンス条件がある場合はそのライセンス文書へのURLを記述

VRMファイルに設定できるライセンスデータ

このようなライセンス情報が入っていますので、必ず使用するモデルの規約に従ってください。

実際に作ってみる

SteamVR Pluginを配置する

image.png
Project内のAssets\SteamVR\Prefabsにある[CameraRig]をドラッグしてHierarchyのシーン内に配置します。
配置したら画面上部の▶(Play)ボタンを押して実行してみます。
SteamVRが立ち上がって、HMDを認識していれば
image.png
このような画面が出ますのでYesを押します。何回か同じような画面が出るので全てYesを押してください。
image.png
するとこのような小窓が出てくると思いますので、Save and generateを押します。
image.png
しばらく処理されて元の画面に戻ったらSteamVR Inputの画面はxで閉じて構いません。
image.png
そうしたら再度画面上部の▶(Play)ボタンを押して実行してみます。HMDにGameタブと同じ画面(何もない空間と太陽とコントローラー)が表示されているはずです。SteamVRのメニューが表示される場合があるので、コントローラーのSteamVRメニューボタンで閉じます。これでVRの準備は完了です。もう一度画面上部の▶(Play)ボタンを押して実行を止めてください。

モデルを配置する

image.png
Project内のAssets\ModelsにあるAliciaSolidのPrefab(青い立方体アイコン)をドラッグしてシーン上に配置します。その際[CameraRig]の中に入れてしまったりしないように注意してください。画像の通りに配置されていればよいです。
image.png
また▶(Play)ボタンを押して実行してみます。アリシアさんを眺めるだけVRが完成しました。しばらく眺めて満足したら▶(Play)ボタンを再度押して実行を止めておきます。

Final IKを設定する

image.png
Herarchy上のCameraRigの中にあるController(Left)を右クリックしてCreate Emptyを押して出来たGameObjectを右クリックし、Renameを押して名前をLeftHandにします。
同様にController(Right)にRightHandを、CameraにHeadを作り、
image.png
LeftHand、RightHand、Headがそれぞれ画像のように配置されるようにします。
image.png
Hierarchy上のAliciaSolidを選択して、Inspectorを一番下までスクロールしたところにあるAdd Componentを押して、vrikと入力するとVR IKが出てきますので、それを選択します。
image.png
するとInspectorの一番下にVRIK (Script)が追加されるので、Solverを開き、SpineとLeft ArmとRight Armをそれぞれ開いておきます。
image.png
開いたSpine内のHead Targetに先ほど作ったHeadを、Left ArmのTargetにLeftHandを、Right ArmのTargetにRightHandをそれぞれドラッグして登録します。
image.png
この段階で一度▶(Play)ボタンを押して実行してみます。HMDを被って両手にコントローラーを持って動かしてみてください。手の位置に合わせて腕は動くし、頭の位置に合わせて体も移動するようになっているはずです。動かしてみると分かると思いますが、まだ色々問題があります。まず頭の位置が身長と合ってないし、自分の頭が見えてしまっていてリボンや頭の中が見えて困ります。それからコントローラーの位置に手が来ていますが、明らかに思っている位置と手の向きではないはずです。次はひとつづつこれを直していきます。何度も言っていますが、一度Playした後編集する際は▶(Play)ボタンを再度押して実行を止めておきます。

自分の頭がHMD内に映らないようにする/体がたまに消えるのを直す

VRMには自分の頭を非表示にするためのVRMFirstPersonというスクリプトが付いていますので、この機能を使えるように設定します。
image.png
メニューのEdit->Project Settings->Tags and Layersを開きます。
image.png
User Layer 9にFIRSTPERSON_ONLY_LAYER、User Layer 10にTHIRDPERSON_ONLY_LAYERと入力します。
※この9,10の数値や名称は今後のUniVRMの更新で自由に設定変更可能になる可能性があります。UniVRM0.45時点でのデフォルトです。
image.png
次にHierarchyの[CameraRig]内のCameraを選択して、InspectorのCamera内にあるCulling Maskの選択肢からTHIRDPERSON_ONLY_LAYERのチェックを外します。
このCameraは自分のHMDでの視点になっているカメラです。FIRSTPERSON_ONLY_LAYERというのは自分目線で表示するレイヤー(頭を除く体だけ)で、THIRDPERSON_ONLY_LAYERは他人から見たときに表示するレイヤー(頭も含む)なので、THIRDPERSON_ONLY_LAYERを外すことで自分視点から頭の表示を消します。
image.png
HierarchyからAliciaSolidを選択して、Inspector一番下のAdd Componentを押して、New Scriptを選択します。
image.png
NameにFirstPersonSetupと入力しCreate and Addを押します。
image.png
追加されたFirst Person Setup (Script)を右クリックしてEdit Scriptを押して、スクリプトを開きます。

FirstPersonSetup.csが表示されたら、void Start(){ }内に

GetComponent<VRM.VRMFirstPerson>().Setup();
foreach (var renderer in GetComponentsInChildren<SkinnedMeshRenderer>(true))
{
    renderer.updateWhenOffscreen = true;
}

を追加して、

FirstPersonSetup.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class FirstPersonSetup : MonoBehaviour {

    // Use this for initialization
    void Start () {
        GetComponent<VRM.VRMFirstPerson>().Setup();
        foreach (var renderer in GetComponentsInChildren<SkinnedMeshRenderer>(true))
        {
            renderer.updateWhenOffscreen = true;
        }
    }

    // Update is called once per frame
    void Update () {

    }
}

このようにしたら保存して画面を閉じます。追加したのはVRMのFirstPersonを初期化する関数の呼び出しと、カメラ位置によって体が描画されなくなるのを防ぐためにすべてのSkinnedMeshRendererのUpdate When Offscreenをオンにする処理です。
image.png
この状態でまた▶(Play)ボタンを押して実行してみます。先ほどまで自分の頭が見えて邪魔だったはずですが、表示が消えて体だけ見えるようになっているはずです。確認したら▶(Play)ボタンを押して実行を止めます。

手の位置と向きを直す

手の向きと位置が今のままだと間違っているのでこれを直します。Final IKのVRIKは"手首"の位置を設定するものですが、SteamVRのコントローラーは、コントローラーの先端位置を示しているのでこのようなずれが生じます。なのでコントローラー側を手首の位置と向きになるようにオフセットしてあげることにします。
image.png
Hierarchy内LeftHandを選択し、InspectorのTransformのPositionに-0.03,0.025,-0.12を、Rotationに-40,0,90を入力します。
image.png
同様にRightHandを選択し、InspectorのTransformのPositionに左手と逆回転の0.03,0.025,-0.12を、Rotationに-40,0,-90を入力します。
image.png
また▶(Play)ボタンを押して実行してみます。手の位置が大体実際の位置に合うようになっていればOKです。モデルによっては少し位置が違うかもしれないので数値はお好みで調整してください。

モデルの大きさと実際の体型のずれを合わせる

今のままだとアリシアさんと自分の身長や腕の長さが合わず、立つと視点が高くなっていたり、腕を伸ばしきる前にアリシアさんの腕が伸びきっていて、手からコントローラーが離れていくようになってしまっていると思います。このずれをなるべく無くしていきます。
image.png
▶(Play)ボタンを押して実行します。Hierarchyから[CameraRig]を選択してInspectorのTransform内のScaleの値を変更します。
まずはXとZの値を両手を広げたときにちょうどモデルの手が真っ直ぐになるように(実際に手をまげたらモデルも同じように曲がるように)なる数値を入れます。Play中は即座に反映されるので、0.8あたりから0.79,0.78,0.77・・・という風に微調整して自分の体形に合った数値を探してください。XとZには同じ数値を入れてください。今回は0.8を入れました。
次にYの値を調整します。真っ直ぐ立って画面上のアリシアちゃんのかかとが浮かず、膝が曲がらない程度にYの値を調整してください。今回は0.85にしました。
この状態のまま入力したX,Y,Zの値を覚えておいてください。再度▶(Play)ボタンを押して実行を停止して、入力したものが元に戻ってしまっているはずなので、再度覚えておいたX,Y,Zを入れなおしてください。

がに股なのを直す

見てわかる通り、足が開き過ぎていてしゃがんだりするとひざが外側に向いたりして可愛くないです。これを直します。
image.png
HierarchyからAliciaSolidを選択し、InspectorのVRIK内のLeft Leg、Right Leg、Locomotion内の値をそれぞれ変更します。
ひざの向きを変えるために
Left Leg内Swivel Offsetを15
Right Leg内Swivel Offsetを-15
歩幅を変えるために
Locomotion内の
Foot Distanceを0.15
Step Thresholdを0.4
Max Velocityを0.3
Velocity Factorを0.3
Root Speedを30
に変更します。すべての値は画像を確認してください。
歩幅などは自分の好みで変更してください。

配信用の環境を作る

ここまでで自分の体をVR機器で良い感じに動かせるようになりました。VTuberになるためにはこの動きを別のカメラで撮ってその映像を見せないといけません。
そのための設定をしていきます。

メインカメラの表示順を上げる

今までのままだと実行中のGame画面にはHMDと同じ映像が表示されています。これを別のカメラからの映像に切り替えて、カメラを正面に持ってくることにします。
image.png
Hierarchy上のMain Cameraを選択し、まずはTransformのPositionを0,1,-2に設定します。
それからCameraのDepthを1に、Target EyeをNone (Main Display)に設定します。
image.png
▶(Play)ボタンを押して実行します。HMDには今まで通り自分の視点での画面が表示されて、Unity側のGame画面には配置したMain Cameraの映像が表示されているはずです。確認出来たら▶(Play)ボタンを押して実行を止めます。

自分の姿をHMD内で確認できるようにする

今のままだとHMDを被ってしまうと自分の姿がどのようにカメラに写っているか確認できないので、カメラの映像を表示させるパネルを設置します。
image.png
ProjectのAssetsフォルダを右クリックして、Create->Custom Render Textureを選びます。
出来たファイルにはMainCameraTextureと名前を付けます。
image.png
出来たMainCameraTextureを選んで、InspectorのSizeに1920x1080と入力します。
image.png
Hierarchy上のMain Cameraを右クリックし、Duplicateを選択します。
image.png
新しく増えたMain Camera(1)を選択して、先ほど作ったMainCameraTextureをTarget Textureにドラッグ&ドロップして設定します。
image.png
HierarchyのMyVTuberを右クリックしてGameObject->3D Object->Quadを選択します。
出来上がったQuadを右クリックしてRenameでMonitorPanelにします。
image.png
作成したMonitorPanelのTransformのPositionを0,0.9,-2、Rotationを0,180,0、Scaleを-3.2,1.8,1に設定します。
ScaleのXをマイナスにすると、左右の表示が反転するためミラー表示が出来ます。自分の動きを確認するときに左右が逆だととても操作しづらいのでミラーにしています。
そして先ほど作成したProjectのAssetsフォルダにあるMainCameraTextureをHierarchy上のMonitorPanelにドラッグ&ドロップします。
image.png
MonitorPanelのInspector内の一番下にあるMainCameraTextureのShaderをStandardからUnlit->Textureに変更します。
image.png
▶(Play)ボタンを押して実行します。目の前に鏡のように自分の姿が映っているはずです。動いて確認してみてください。動くのを確認したら▶(Play)ボタンを押して実行を止めます。

髪の毛が正しく揺れていないのを修正する

UniVRMとFinal IKを同時に使用した場合、特に設定をしないとその場で頭だけ動かしたときに髪の毛が正しく揺れてくれません。なのでこれを修正します。
image.png
UnityのEditメニューからProject Settings->Script Execution Orderを開きます。
image.png
出てきたScript Execution Orderの右下の+ボタンを押して、
image.png
大量にメニューが出てくるので下向き三角▼を押し続けて一番下から2番目の、VRM.VRMSpringBoneを選びます。
image.png
VRM.VRMSpringBoneがリストの一番下に10400(一番大きい数字)で追加されたことを確認します。
同様にVRM.VRMSpringBoneColliderGroupも入れて10500になったら、右下のApplyを押してください。
出来たらそのまま▶(Play)ボタンを押して実行します。その場で頭を左右に動かしてみてください。髪の毛が綺麗に揺れるようになっていると思います。動くのを確認したら▶(Play)ボタンを押して実行を止めます。

リップシンクを入れる

今のままでは喋っても口が動きません。リップシンクをいれてみます。

OVRLipSync 1.28.0

OVRLipSync 1.28.0のページに行き、
image.png
規約に同意する場合チェックを入れてダウンロードしてファイルを展開してください。
Unity上部のAssetsメニュー->Import Package->Custom Package...を押して、展開した中のLipSync\UnityPlugin\OVRLipSync.unitypackageを開きます。
image.png
今までと同じように、Import Unity Packageの画面が出ますのでImportを押してください。

AniLipSync-VRM 1.0.0

AniLipSync-VRM 1.0.0のページからAniLipSync-VRM.unitypackageをダウンロードして、
Unity上部のAssetsメニュー->Import Package->Custom Package...を押して、ダウンロードしたAniLipSync-VRM.unitypackageを開きます。
image.png
同じように、Import Unity Packageの画面が出ますのでImportを押してください。
AniLipSync-VRMはこの記事のために新しく作りました。

リップシンクの設定

image.png
まずProject内のAssets\Oculus\PrefabsフォルダにあるLipSyncInterfaceをHierarchyにドラッグして配置します。
image.png
次にAssets\AniLipSync-VRM\PrefabsにあるAniLipSync-VRMをHierarchyにドラッグして配置します。
image.png
Inspectorの一番下、Anim Morph Target (Script)内のBlend Shape Proxyに配置しているVRMモデルをドラッグ&ドロップして設定します。

以上でリップシンク設定は完了です。▶(Play)ボタンを押して実行します。マイクに向かってあ、い、う、え、おと声を出して口が動くか確認してください。確認したら▶(Play)ボタンを押して実行を止めます。

まばたきを入れる

今はずっと目が開きっぱなしで瞬きをしないので怖いです。自動まばたきを入れます。
image.png
HierarchyからAliciaSolidを選択し、Inspectorの一番下のAdd Componentでblinkerと検索すると出てくるBlinkerを選びます。
image.png
Blinker (Script)が設定されるので値を好きなように変更します。

  • Inter Val(次のまばたきまでの秒数、0秒~指定秒数までのランダムでまばたきします)
  • Closing Time(閉じたままの時間)
  • Opening Seconds(開くアニメーションの時間)
  • Close Seconds(閉じるアニメーションの時間)

今回はそのままにしておきます。▶(Play)ボタンを押して実行します。指定秒数内でランダムにまばたきすることを確認します。確認したら▶(Play)ボタンを押して実行を止めます。

手首がねじれるのを解決する

HMDで自分の手首を見てみると、雑巾絞りのようにねじれてしまっているように見えます。これは手の回転を手首だけ突然回しているため発生しています。
Final IKにはこれを緩和するためのTwistRelaxerスクリプトが入っているのでこれを適用します。
image.png
HierarchyからAliciaSolidを選択し、InspectorのVRIK (Script)内のReferencesの中にあるLeft Forearmに設定されているelbow_L (Transform)をダブルクリックします。
image.png
ひじの関節部が選択されるので、InspectorからAdd Componentを押してtwistと検索すると出てくるTwist Relaxerを選択します。
image.png
標準の設定だと強度と角度が強すぎて、逆におかしな回転をするようになるので、
Weightを0.4、Parent Child Crossfadeを1に設定します。

全く同じことをVRIK内のReferencesのRight Forearmに対しても設定します。
両肘にTwist Relaxerが設定された状態になったら再び▶(Play)ボタンを押して実行します。実行中にお好みの回転の値を探して確認後に設定しなおしも出来ます。確認したら▶(Play)ボタンを押して実行を止めます。

表情を変える

まばたきとリップシンクを設定したのである程度顔は動くようになりましたが、VRMは表情制御を行うことができます。なので今回はコントローラーのタッチパッド(スティック)を4分割して設定した表情に変えられるようにスクリプトを追加します。
image.png
ProjectのAssetsを右クリックしてCreate->C# Scriptを選択し、出来たスクリプトの名前をControllerInputManagerにします。
出来たControllerInputManagerをダブルクリックしてスクリプトを開き、中身を全て消して、

ControllerInputManager.cs
using AniLipSync.VRM;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using Valve.VR;
using VRM;

public class ControllerInputManager : MonoBehaviour
{
    public VRMBlendShapeProxy blendShapeProxy;
    public Blinker blinker;
    public AnimMorphTarget animMorphTarget;

    private SteamVR_ActionSet[] actionSets;

    // VRMの表情
    private Dictionary<BlendShapeKey, float> shapeValueDictionary = new Dictionary<BlendShapeKey, float> {
            {new BlendShapeKey(BlendShapePreset.Neutral), 0.0f }, // NEUTRAL(標準)
            {new BlendShapeKey(BlendShapePreset.Blink), 0.0f }, // BLINK(目を閉じる、まばたき)
            {new BlendShapeKey(BlendShapePreset.Joy), 0.0f }, // JOY(喜び)
            {new BlendShapeKey(BlendShapePreset.Angry), 0.0f }, // ANGRY(怒り)
            {new BlendShapeKey(BlendShapePreset.Sorrow), 0.0f }, // SORROW(悲しみ)
            {new BlendShapeKey(BlendShapePreset.Fun), 0.0f }, // FUN(楽しみ)
            {new BlendShapeKey(BlendShapePreset.Blink_L), 0.0f }, // BLINK_L(左目閉じる)
            {new BlendShapeKey(BlendShapePreset.Blink_R), 0.0f }, // BLINK_R(右目閉じる)
            {new BlendShapeKey("><"), 0.0f }, // ><(アリシアちゃん専用表情)
        };
    private List<BlendShapeKey> keyList;

    private int GetKeyIndex(BlendShapePreset preset) { return keyList.FindIndex(d => d.Name == preset.ToString().ToUpper()); }
    private int GetKeyIndex(string preset) { return keyList.FindIndex(d => d.Name == preset.ToUpper()); }
    private BlendShapeKey GetKey(BlendShapePreset preset) { return keyList[GetKeyIndex(preset)]; }
    private BlendShapeKey GetKey(string preset) { return keyList[GetKeyIndex(preset)]; }

    // Use this for initialization
    void Start()
    {
        keyList = new List<BlendShapeKey>(shapeValueDictionary.Keys);
        actionSets = SteamVR_Input.actionSets;
        if (actionSets == null) { actionSets = SteamVR_Input_References.instance.actionSetObjects; }
    }

    // Update is called once per frame
    void Update()
    {
        if (blendShapeProxy == null) return;

        var sources = SteamVR_Input_Source.GetUpdateSources();
        foreach (var source in sources)
        {
            if (source == SteamVR_Input_Sources.Any) continue;
            foreach (var actionSet in actionSets)
            {
                foreach (var action in actionSet.allActions) //ボタン
                {
                    if (action is SteamVR_Action_Boolean)
                    {
                        var actionBoolean = (SteamVR_Action_Boolean)action;
                        if (actionBoolean.GetStateDown(source))
                        {
                            var name = actionBoolean.GetShortName();
                            if (name == "InteractUI") // トリガー半引き
                            {

                            }
                            else if (name == "GrabPinch") // トリガー全引き
                            {

                            }
                            else if (name == "Teleport") // タッチパッド押し
                            {

                            }
                            else if (name == "GrabGrip") // グリップボタン押し
                            {

                            }
                        }
                    }
                    else if (action is SteamVR_Action_Single) //Axis
                    {
                        var actionSingle = (SteamVR_Action_Single)action;
                        if (actionSingle.GetChanged(source))
                        {
                            var name = actionSingle.GetShortName();
                            var axis = actionSingle.GetAxis(source);
                            if (name == "Squeeze") // トリガー
                            {

                            }
                        }
                    }
                    else if (action is SteamVR_Action_Vector2) // Padやスティック
                    {
                        var actionVector2 = (SteamVR_Action_Vector2)action;
                        if (actionVector2.GetChanged(source))
                        {
                            var name = actionVector2.GetShortName();
                            var axis = actionVector2.GetAxis(source);
                            //
                            // タッチパッドの座標(両手とも)
                            //        (0, 1)
                            // (-1, 0)(0, 0)(1, 0)
                            //        (0,-1)
                            //
                            // タッチパッドを離した時は(0,0)が飛んでくる
                            //
                            if (name == "TouchPad")
                            {
                                var isLeft = source == SteamVR_Input_Sources.LeftHand;

                                //全ての表情を一旦無効にする
                                foreach (var key in keyList)
                                {
                                    shapeValueDictionary[key] = 0.0f;
                                }

                                if (Mathf.Approximately(axis.x, 0.0f) && Mathf.Approximately(axis.y, 0.0f)) //中心(離した時)
                                {
                                    //まばたきとリップシンクを復活させる
                                    blinker.enabled = true;
                                    animMorphTarget.curveAmplifier = 100f;
                                    shapeValueDictionary[GetKey(BlendShapePreset.Neutral)] = 1.0f;
                                }
                                else
                                {
                                    //表情とぶつからないようにまばたきを止めてリップシンクを弱くする
                                    blinker.enabled = false;
                                    animMorphTarget.curveAmplifier = 0.1f;
                                    if (axis.x < 0) // 左
                                    {
                                        if (axis.y > 0) // 左上
                                        {
                                            shapeValueDictionary[GetKey(isLeft ? BlendShapePreset.Blink_L : BlendShapePreset.Blink)] = 1.0f;
                                        }
                                        else if (axis.y < 0) // 左下
                                        {
                                            shapeValueDictionary[GetKey(isLeft ? BlendShapePreset.Joy : BlendShapePreset.Angry)] = 1.0f;
                                        }
                                    }
                                    else if (axis.x > 0) // 右
                                    {
                                        if (axis.y > 0) // 右上
                                        {
                                            shapeValueDictionary[isLeft ? GetKey("><") : GetKey(BlendShapePreset.Blink_R)] = 1.0f;
                                        }
                                        else if (axis.y < 0) // 右下
                                        {
                                            shapeValueDictionary[GetKey(isLeft ? BlendShapePreset.Sorrow : BlendShapePreset.Fun)] = 1.0f;
                                        }
                                    }

                                }
                                //表情を適用する
                                blendShapeProxy.SetValues(shapeValueDictionary.ToList());
                            }
                        }
                    }
                }
            }
        }
    }
}

このコードに置き換えてください。置き換えたら上書き保存してください。
image.png
HierarchyのMyVTuberを右クリックしてGameObject->Create Emptyを選び、出来たGameObjectを右クリックしてRenameでControllerInputManagerにしてください。
image.png
作成したHierarchy上のControllerInputManagerを選択して、InspectorのAdd Componentでcontrollerinputと検索して出てくるController Input Managerを選択してください。
image.png
画像のようにBlend Shape ProxyにAliciaSolid、BlinkerにもAliciaSolid、Anim Morph TargetにAniLipSync-VRMをそれぞれドラッグ&ドロップで登録してください。
image.png
次にタッチパッドをSteamVRで使えるように設定します。
Hierarchyの[CameraRig]内のController (Left)を選択してInspectorのSteam VR_Behaviour_PoseのPose Actionの選択肢を開き、Add...を選択します。
image.png
こんな画面が出るはずなので、ActionsのIn側の+ボタンを押して、
image.png
NameにTouchPad(大文字小文字が区別されますので正しく)、Typeをvector2にして、Save and generateを押します。
image.png
途中このような画面が出たらSaveを押してください。
しばらく出力に時間がかかった後、同じ画面に戻るので今度はOpen binding UIを押してください。
image.png
ブラウザでSteamVRのBinding設定画面が開きますので、Editを押します。
image.png
出てきた画面を一番下までスクロールして左側のTrackpadの+ボタンを押します。
image.png
選択肢が出ますので一番上のTRACKPADを選んでください。
image.png
左下にTRACKPADが追加されるのでその中のPositionのNoneを押し、選択肢からtouchpadを選びます。
image.png
下のMirror Modeにチェックが入ってなかったらチェックを付けて、Save Personal Bindingボタンを押します。
image.png
名前を付けるか聞かれるのでそのままSaveを押します。
これで設定は完了なのでブラウザは閉じて構いません。
image.png
Unityに戻って、この画面はもう大丈夫なので右上の×ボタンで閉じます。

ここまで来たらいよいよ表情が動かせるはずです。▶(Play)ボタンを押して実行します。
タッチパッドを左上、右上、左下、右下とそれぞれ4分割してキーを割り当てています。割り当ては次の表のとおりです。
左手

ウインク(左) ><(アリシア専用)
喜び 悲しみ

右手

両目閉じる ウインク(右)
怒り 楽しい

確認したら▶(Play)ボタンを押して実行を止めます。

この表情変更スクリプトには、表情変更時にリップシンクで口が破綻しないように抑制する機能と、自動まばたきを一時停止する機能が入っているので表情が崩れません。

仕上げ

カメラにコントローラーが映らないようにする

自身の目からはコントローラーが見えるように(分かりやすさのため)するが、配信には映したくないのでカメラからは見えないようにします。
image.png
Hierarchyの[CameraRig]内のController (left)のModelを選択し、InspectorのLayerを9:FIRSTPERSON_ONLY_LAYERにします。
同様にController (right)のModelのLayerもFIRSTPERSON_ONLY_LAYERにしてください。
image.png
次にMain Cameraを選択し、InspectorのCulling MaskのFIRSTPERSON_ONLY_LAYERのチェックを外します。
同様にMain Camera(1)にも同じ設定をします。
この状態で▶(Play)ボタンを押して実行します。Unity上のGame画面と、HMD内の鏡にはコントローラーが映らなくなっているはずです。自分の手元にだけコントローラーが見えていたらOKです。確認したら▶(Play)ボタンを押して実行を止めます。

配信の合成用に背景をグリーンバックにする

この後好きな背景と合成して録画をするために自分以外の背景をGB(グリーンバック)にします。
image.png
Main CameraのClear FlagsをSolid Colorに変更し、Backgroundの色(最初は紺色)をクリックするとColorパネルが表示されます。
Rを0、Gを255、Bを0にしてグリーンバックにしたらColorパネルを閉じます。
image.png
足元の水色の枠(プレイエリア表示)も消すために[CameraRig]を選択して、Layerを9:FIRSTPERSON_ONLY_LAYERにします。
image.png
その際子要素も変更するか聞かれますが、変更したくないのでNo, this object onlyを押してください。

この状態で▶(Play)ボタンを押して実行します。Unity上のGame画面はモデル以外全て緑色になっていて、HMD内の空間と鏡は今まで通りになったはずです。HMD内も緑にすると目がチカチカするのでこのままで完成です。確認したら▶(Play)ボタンを押して実行を止めます。

完成!

ついに自分のVTuberシステムが完成しました。実行可能形式に出力しましょう。
image.png
UnityのFileメニューからBuild Settings...を選びます。
image.png
設定は特に変えずにそのままBuildボタンを押してください。
image.png
いくつかファイルが出力されるので、空のフォルダ(今回はMyVTuber)を作成して、そこで保存を押します。
image.png
ビルドが終わるのを待つと・・・
image.png
出力先のフォルダが開きました。選択されているMyVTuber.exeが完成品になります。
早速MyVTuber.exeを実行してみてください。
image.png
解像度選択画面が表示されます。Windowedにチェックを入れて、好きな解像度を指定したらPlay!を押します。
image.png
グリーンバックのモデルが表示されました!ですが表情変更が出来ないはずです。これはBindingの設定がされていないためです。
HMDを被ってコントローラーのメニューボタンでSteamVRのメニューを開いてください。
image.png
このような画面になるので、Closeを押してEditを押します。
image.png
上で1度設定したのと同じ画面が表示されているので同じ要領で再度Trackpadの設定をします。
タッチパッドをなぞると画面はスクロールできます。
image.png
やることは全く同じです。Trackpadの+を押して、TRACKPADを選択したら、PositionのNoneをtouchpadに変えて、Save Personal Bindingします。
設定したらコントローラーのメニューボタンでSteamVRの設定画面を閉じます。
今度はタッチパッドを触れば表情が変わるようになっているはずです。

以上でVTuberアプリの設定は全て完了です。これでいつでも起動してVTuberになることが出来ます。

録画・配信

アプリを作っても録画や配信をして見せてこそのVTuberです。簡単に録画の説明もします。
録画に使用するアプリはOBSです。前日の@nkjzmさんのOBSの説明と少し重複しますが、こちらはWindows版で説明をします。

OBSのインストール

まずはOBSのダウンロードページからダウンロード インストーラボタンを押してOBSをダウンロードします。
そのまま実行してインストールを勧めます。特に何もなくNextを押して進めていけば良いです。
image.png
このままFinishしてOBSを起動させます。
image.png
自動構成ウィザードが出るのではいを押して実行します。
image.png
とりあえず録画を選んで次へ(後から変えられます)
image.png
解像度は1920x1080のまま次へ
image.png
私の環境ではこのようになりました。環境によっては違う設定になる場合もあります。このまま設定を適用を押します。
image.png
画面右下の設定を押します。
image.png
出力の録画フォーマットをmp4に変更します。flvは扱いにくいです。
そして録画ファイルのパスを確認します。このフォルダに録画された映像が保存されるので、覚えておくか好きなフォルダに変更してください。
確認出来たらそのままOKを押して設定を閉じます。

背景を設定する

今回はこちらの画像をお借りしました。
image.png
OBSのソース欄にある+ボタンから画像を選択します。
image.png
名前を背景画像に変更してOKを押します。
image.png
参照ボタンで好きな画像を選んだらOKを押します。
※画像の著作権等には注意してください。今回はフリー素材を使用しています。

モデルを合成する

MyVTuber.exeを起動して、グリーンバックにモデルが表示された状態にします。
image.png
シーンの+ボタンを押してゲームキャプチャを選択します。
image.png
こちらはMyVTuberと名付けてOKします。
image.png
モードを特定のウインドウをキャプチャに変更し、ウインドウに[MyVTuber.exe]: MyVTuberを選択します。少し待つと画面が表示されるので、OKを押してください。
image.png
背景の緑を透過させる設定をします。ソースのMyVTuberを右クリックしてフィルタを選択します。
image.png
左下の+ボタンからクロマキーを選択します。
image.png
フィルタ名はそのままクロマキーでOKします。
image.png
デフォルトで色キーが緑になっており、背景が透明になっています。類似性や滑らかさなどはモデルによって変わるので、適宜調整してください。
今回は特に問題なさそうなのでデフォルトのまま閉じるを押します。

マイクの設定をする

image.png
モデルが合成されたのを確認したら今度はマイクの設定をします。ミキサーのマイクの横にある歯車ボタンを押してプロパティを開きます。
image.png
デバイスが既定になっているので、実際に使用するマイク(今回はHTC Vive)を選択してOKを押します。
image.png
この状態で「あー」と声を出してみると、マイクのバーが動き、画面上のモデルの口は"あ"の口に開くはずです。
これで設定は完了です!

録画する

あとは録画開始ボタンを押すだけです!歩き回ったり表情を変えてみたりイケてる動画を撮ってください!
出来上がったアプリで撮った動画は記事冒頭の完成品の動画です。

おわり

明日は、@broken55さんによる「OBSを使った配信の基礎とTips」です。

VTuber Tech #1 Advent Calendar 2018
VTuber Tech #2 Advent Calendar 2018

sh_akira
Win/iOS/Android/Linux/組込 C/C#/Xamarin.Forms/Unity VR機器でVRMの3Dモデルをコントロール!バーチャルモーションキャプチャーの開発をしています 特に明示されていない場合、記事中のソースコードはパブリックドメインです
https://sh-akira.github.io/VirtualMotionCapture/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした