「原神」のMMDキャラクタをスマホVRの世界に呼び込む

前回の投稿「[「原神」のMMDをスマホのブラウザでVR・ARする](https://qiita.com/poruruba/items/a1b41a10ca714c58e044) 」で、スマホのブラウザでVRのステレオ表示させました。

ですが、原神のキャラクタがいる世界に入れたにもかかわらず、徘徊して、自分の好きな場所・好きな角度・好きな距離で見ることができませんでした。
そこで、コントローラを使って、VRの世界を移動し、好きなところから原神のキャラクタを眺められるようにします。

こんな感じで、ドアップにできます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261826/08e9bce7-27e8-f2a2-c0d4-0d840f14c9ca.png)

毎度の通り、以下のGitHubに上げておきました。
poruruba/MmdViewer
　https://github.com/poruruba/MmdViewer/

#コントローラの選択

コントローラには、HTML5のGamePad APIに対応したコントローラを使います
いろんな種類があって、どれが対応しているか正直わからないのですが、手元にあった以下の2つで試してみました。

JC-VRR01
　https://www.elecom.co.jp/products/JC-VRR01BK.html
JC-U3412S
　https://www2.elecom.co.jp/peripheral/gamepad/jc-u3412s/

JC-VRR01は、Selectボタンを押しながら電源を入れると、GamePadモードになるようです。
JC-U3412Sは、LEDが赤点灯のアナログモードがおすすめです。

※Windowsでは、SONY DualShock4のUSB接続でも認識したので、それでもいけるかも。

前者の方は、Bluetooth3.0接続なので、ワイアレスでつながります。
後者は、USBドングルをAndroidスマホに挿さないといけないので、VRゴーグルに入らなくなるかもしれません。

#VRモード(改)

前回の投稿で、requestAnimationFrame呼び出しをrenderer.setAnimationLoop呼び出しに変えることで、簡単にVRモードにしました。
この方法でもよいのですが、カメラを動かせないなど、いろいろ制約があるので、自分でVRモード化します。といっても、そのためのパーツはあるのでそれらを組み合わせるだけです。
ということで、requestAnimationFrameの呼び出しのままです。ちなみに、three.jsはJavascript版ではなく、module版の方です。

①スマホのモーションセンサをカメラに連動させる。

以下に示す通り、DeviceOrientationControls にカメラを渡してあげるだけで、勝手に、モーションセンサの動きに合わせて、カメラの方向を変えてくれます。

```js:js/MmdViewer.js
    if( this.vr ){
      // VRモードの場合、スマホのモーションセンサでカメラを制御
      this.controls = new DeviceOrientationControls(this.camera, true);
      this.controls.connect();
    }
```

②ステレオ表示

以下のように、VRモードにしようとする場合、StereoEffectというエフェクトをレンダラーにかませることで、左右2つのキャンパスを生成し、表示してくれます。

```js:js/MmdViewer.js
    if( this.vr )
      this.effect = new StereoEffect( this.renderer ); // VRモードの場合、左右2つのキャンバスで表示
    else
      this.effect = this.renderer; // 通常の場合
```

③スマホのモーションセンサの反映

繰り返しrequestAnimationFrameを呼び出すanimate()の中で、以下を呼び出します。

```js:js/MmdViewer.js
    if( this.vr ){
      // VRモードの場合

      // モーションセンサの動きをカメラに反映
      this.controls.update();
```

# VR空間の中での移動

VR空間の中で移動させるトリガとして、GamePadコントローラを使います。
この機能は、three.jsというより、HTML5の機能です。
都度、navigator.getGamepads() を呼び出して、GamePadコントローラの存在を確認し、見つけたGamePadから、gamepad.axes[X]でアナログジョイスティックの傾きを取得したり、gamepad.buttons[X].pressedでボタンの押下状態を取得します。
どのXがどの方向に割り当たるかは、コントローラの機種によって異なるので、機種に合わせて変えてください。

```js:js/MmdViewer.js
      var gamepadList = navigator.getGamepads();
      for(var i = 0; i < gamepadList.length; i++){
        var gamepad = gamepadList[i];
        if(gamepad){
          // GamePadからの操作で、カメラを移動
          this.camera.translateOnAxis(new THREE.Vector3(0, 0, -1), -gamepad.axes[1] * delta * 10); // Z軸方向に移動
          this.camera.translateOnAxis(new THREE.Vector3(-1, 0, 0), -gamepad.axes[0] * delta * 10); // X軸方向に移動
          this.camera.translateOnAxis(new THREE.Vector3(0, -1, 0), gamepad.buttons[0].pressed * delta * 10); // Y軸の正の方向に移動
          this.camera.translateOnAxis(new THREE.Vector3(0, -1, 0), -gamepad.buttons[1].pressed * delta * 10); // Y軸の負の方向に移動
          {
            // 原点を中心に回転
            var startVec = new THREE.Vector3(this.camera.position.x, this.camera.position.y, this.camera.position.z);
            var axis = new THREE.Vector3(0, 1, 0);
            const q = new THREE.Quaternion();
            q.setFromAxisAngle(axis, Math.PI * 2 / 180 * gamepad.axes[2] / 2);
            var vertex = startVec.applyQuaternion(q);
            this.camera.position.copy(vertex);
          }
        }
      }
    }
```

あとは、this.camera.translateOnAxis で指定する各軸を基準にして移動させています。
```var delta = this.clock.getDelta();``` で取得した経過時間も移動量に加味しています。

```原点を中心に回転``` のところは、その通りなのですが、正直もっと良いやり方があるような気がします。（ご存じの方がいらっしゃいましたらご教授いただけると助かります）

④全画面表示

最後に全画面表示です。
これは、Three.jsやVRモード特有のことではなく、ブラウザの仕様です。

```html:index.html
            if( fullscreen )
                $('#canvas_0')[0].webkitRequestFullscreen();
```

上記を最初に呼び出しています。それによって、アクションバーもない全画面表示となります。上記のコードは、Chromeの場合です。別のブラウザの場合は少々違うようです。

また、上記の呼び出しは、ユーザジェスチャをトリガとしないといけないので、開始するボタンをHTMLに用意しておきました。

#終わりに

VRの世界に入れて、2人っきりの状態にはなれたのですが、音がないので、ちょっと寂しいです。次回は音を入れたいなあ。

以下のページを参考にさせていただきました。ありがとうございました。

three.jsの基本ライブラリのみで超シンプルにwebVRを実装する【最初】
　https://cardboardclub.jp/lab/chromeex-01/

以上