【VTuber】OVRLipsyncでUnity上のキャラをリップシンクさせる方法

Unity上のキャラをリップシンクさせるには二通りあります。
1つ目はMMD4MecanmLipSyncPlugin、2つ目がOVRLipsyncを使う方法。

しかし、Oculusのほうが精度が高いと感じたため、今回は**OVRLipsyncを使ってリップシンクさせる方法**をまとめてみました。


こちらの記事を参考にさせていただいております。
http://tips.hecomi.com/entry/2016/02/16/202634
##完成イメージ
![kizunaai_lipsync3_gif.gif](https://qiita-image-store.s3.amazonaws.com/0/242611/641b0190-cbdf-ef2a-0960-cba2a76c3f8a.gif)


##用意するもの
- [OVRLipsync](https://developer.oculus.com/downloads/package/oculus-lipsync-unity/)
- [Oculus Lift](https://www.oculus.com/rift/)
- [3Dモデル](http://kizunaai.com/download-page/)
- [Unity](https://unity3d.com/jp)

##OVRLipsyncをダウンロードして、空オブジェクトにアタッチする
[OVRLipsync](https://developer.oculus.com/downloads/package/oculus-lipsync-unity/)をダウンロードしてインポートします。

そしたら空オブジェクトを作成しましょう。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/242611/df76a0c1-5fcb-dd90-264d-005e0f7bf20e.png)

空のObjectに**OVRLipSync**をアタッチ。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/242611/d123f470-df4c-5fda-e763-612a317dd48e.png)


##音声によって口の動きを変形させられるようにする

キズナアイ(3Dモデル)Objectに
 **OVRLipSyncContext** ,
 **OVRLipSyncContextMorphTarget**
をアタッチ。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/242611/fac8a1e6-5a46-a1a6-7911-3dcb5967cd89.png)

OVRLipSyncMorphTarget > Skinned Mesh Rendererに、頭のパーツを当てはめましょう。キャラによって違いますが、キズナアイの場合はU_char_1になります。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/242611/ed8fe24f-3d8b-b9be-d182-373012f819bc.png)
これによって、キズナアイのモーフを操れるようになりました。

もし「どれを当てはめるのか分からない！」という方は頭周辺のパーツを探り**BlendShapes**が付いているものを当ててみるといいかも。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/242611/807a6e8e-d89a-feb2-0ef4-2e47e650d368.png)


次にOVRLipSyncMorphTarget > Viseme To Blend を開き、
Targetの母音部分を対象のモーフのインデックス番号を指定します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/242611/eed50579-6f6e-c89f-0795-0a6f09d331ce.png)

まず、Visme To Blendは15種類の口形素があります。(詳しくは[こちら](http://www.eejournal.ktu.lt/index.php/elt/article/viewFile/5657/3062)に載ってますが勉強する必要はありません)
0 sil(無音) 
1 PP（p,m,b）
2 FF（f,v）
3 EH(@r)
4 DD(t,d)
5 kk(k,g,x)
6 CH(tS,S,Z)
7 SS(ts,s,z)
8 nn(n,l,r)
9 RR(@r)
10 aa（あ）
11 E（え）　　
12 ih（い）　　
13 oh（お）
14 ou（う）

例えば、「あ」と、発音したしたときに、Element10に当てはめた**モーフのインデックス番号**が反映される仕組みです。
**モーフのインデックス番号**はSkinned Mesh Renderer > BlendShapesから確認できます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/242611/be73ccc5-b624-1e38-f8e7-8885cbcbb380.png)
ここでは1~32がインデックス番号に当たり、その番号に対するモーフ（表情）がそれぞれ対応しています（キャラによって数が違う）
中でも、9　→「あ」、10 →　「い」　11　→　「う」12 →　「え」13　→　「お」となっていることに気づくと思います。

これらが、先程当てはめたモーフ番号になります。
つまり、現状ではこんな感じ。

| 発音 | インデックス番号 |口の形 |
|:-----------|------------:|:------------:|
| 0 sil(無音)       | 0        |
| 1 PP（p,m,b)     |   0    | 
| 2 FF（f,v）      | 0        |
| 3 EH(@r)      | 0  | 
| 5 kk(k,g,x)  | 0       | 
| 6 CH(tS,S,Z) | 0    |
| 7 SS(ts,s,z)| 0     |
| 8 nn(n,l,r) | 0     |
|9 RR(@r)  | 0     |
| 10 aa（あ） | 9    |あ      |
| 11 E（え） | 12    |え    |
| 12 ih（い） | 10    |い        |
| 13 oh（お） | 13    |お       |
| 14 ou（う） | 11    |う        |
10 aa（あ）と発音したときに、9「あ」の口の形をする
14 ou（お）と発音したときに、14「お」の口の形をする
といった感じです。

他にも、pp であれば「ポ」、FFなら「ファ」の発音になるので、それぞれの発音に対するモーフを当てはめていただけれければと。

次に、OVRLipSyncContextMorphTargetの**Update()**を**LateUpdate()**に変更しておきます。

```OVRLipSyncContextMorphTarget.sc

void LateUpdate () {
  if ((lipsyncContext != null) && (skinnedMeshRenderer != null)) {
    // trap inputs and send signals to phoneme engine for testing purposes
    // get the current viseme frame
    OVRLipSync.Frame frame = lipsyncContext.GetCurrentPhonemeFrame();
    if (frame != null) {
      SetVisemeToMorphTarget(frame);
    }
    // TEST visemes by capturing key inputs and sending a signal
    CheckForKeys();
  }
}
```


##マイクで入力させる

キズナアイ(モデル)に**OVRLIpSyncMicInput**をアタッチします。

そしたら常に音声を認識させられるように、Mic Controlの部分を**ConstantSpeadk**にしておきます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/242611/6ce6c25a-01c4-d031-9797-d49829aa5195.png)

もし口の動きが小さくて分かりづらかったら、OVRLipSyncContext > Gainの値をお起きすると口が動きやすくなります。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/242611/18c5f560-2a94-bb23-4bf3-503b2204c2dc.png)


これで完成。

##まとめ
Viseme To Blend Targetsでマイクから拾ってきた音を認識し、その音声に従ってSkinned Mesh Rendererのモーフが適応されているというイメージかと思います。

もし口が動かない場合は、他のデバイスが音声を拾っていないか、Gainの値が小さすぎないかを確かめていただければと。