WebVR はじめよう

近年、バーチャルリアリティ（VR）が話題を集めています。[Cardboard](https://www.google.com/get/cardboard/) や [Oculus](https://www.oculus.com/ja/) などの VR デバイスが登場に加え、医療分野[[1](http://www.telegraph.co.uk/technology/2016/04/14/watch-the-worlds-first-surgery-streamed-in-virtual-reality-live/)]、教育分野[[2](http://fortune.com/2016/02/25/school-districts-teaching-through-virtual-reality/)]など応用分野もどんどん広がっております。せっかくなら「消費する側」ではなく「作る側」としてこのビッグウェーブに乗っかりたいですよね。
でも、そのためには映像や 3D に関する特殊な勉強が必要なのでしょうか？いえいえ、そんなことはありません。VR コンテンツをつくる敷居は下がってきており、今ではウェブエンジニアにとってはおなじみの HTML と Javascript だけで作ることができます。

*そう、WebVR ならね。*

ここでは、最低限のコードで WebVR を開発する手順を紹介します。

# WebVR とはなにか？
WebVR とは、ウェブブラウザ上で VR 体験を提供することです。狭義には、ウェブブラウザ上で 3D グラフィクスを表現する技術である WebGL をつかって、VR 体験を提供する技術を指します。
残念なことに、WebGL を直接利用して 3D グラフィクスを作るには、シェーダーやポリゴンといった 3D グラフィクスに特有の技法を学ぶ必要があります。そのため、WebGL を直接扱うのではなく [ThreeJS](http://threejs.org/) をはじめとする WebGL の記述を楽にしてくれる Javascript ライブラリを用いて作られることが多いです。

## WebVR のいいところ
WebVR で VR コンテンツを作る利点としては、以下が挙げられます。ネイティブアプリとは一味違う VR 体験を提供できることがわかると思います。

- アプリをインストールする必要がない。ブラウザさえあれば動作するので、Google Play や App Store からアプリをダウンロードすることなく VR 体験を楽しむことができる。
- 端末を選ばない。スマートフォンでもタブレットでもデスクトップでも楽しむことができるコンテンツを作ることできる。
- 動的なコンテンツ。ウェブをベースにすることで、アプリをアップデートすることなくコンテンツを更新することができる。また、メッセージ投稿機能や画像投稿機能など、ユーザとの相互作用性が高い機能を提供できる。（後者はネイティブアプリでもできなくはないですが）

## WebVR の実装方法
では、WebVR を実装するには ThreeJS 以外にも方法はあるのでしょうか？WebVR 開発をより簡単にするために様々なライブラリが登場していますが、ドキュメントや実装例の多さという観点では ThreeJS に軍配が上がるでしょう。

- [ThreeJS](http://threejs.org/)
    - 言わずと知れた 3D グラフィクス描写ライブラリ。歴史も古く、豊富なドキュメントと実装例が強み。WebVR に特化しているわけではありません。
- [Aframe](https://aframe.io/)
    - Mozilla が発表した WebVR 構築ライブラリ。まるで HTML を書くように、要素を追加していくようにして WebVR 体験を記述することができます。
- [WebVR starter kit](https://github.com/povdocs/webvr-starter-kit)
    - Three.JS ベースの WebVR 構築ライブラリ。
- [VR View](https://github.com/google/vrview)
    - Google が提供する WebVR 構築ライブラリ。360 画像や動画に特化している（という理解）。
- GUI で操作できるもの
    - http://vizor.io/
    - http://spaces.archilogic.com/3d/template/new?mode=edit&view-menu=none

# ThreeJS 入門
では、ここから実際に ThreeJS を用いて WebVR を作っていきます。

## WebVR 開発の概要
WebVR を作ると言っても、基本的にやることはウェブサイトを作る時と同じです。つまり、HTML, CSS, Javascript を書くだけです。

### HTML is キャンバス
WebVR を作るには複雑な HTML を書くことが必要でしょうか？いえ、WebVR の場合はほとんど書くことはありません。必要な CSS ファイルや Javascript ファイルを読み込むだけで、他に書くことはほとんどありません。

```html:index.html
<!doctype html>
<html>
  <head>
    <meta charset="utf8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <link rel="stylesheet" type="text/css" href="default.css">
  </head>
  <body>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r76/three.min.js"></script>
    <script src="app.js"></script>
  </body>
</html>
```

### CSS でキャンパスを画面いっぱいに広げる
CSS も特に複雑なことはしません。`canvas` 要素を画面いっぱいに広げるだけです。

```css:default.css
body {
  margin: 0;
}
canvas {
  height: 100%;
  width: 100%;
}
```

なんとも画面いっぱい感が伝わる CSS ですね！

### Scene, Camera, Render!
Javascript のコーディングに入る前に今一度 3D グラフィクスの世界観について確認しておきましょう。
3D グラフィクスの基本となる構成要素は、シーン、カメラ、そしてレンダラーです。映画を撮影するシーンを想像してもらうとわかりやすいと思いますが、何はともあれ撮影する対象となる情景（シーン）、それを撮影する機材（カメラ）が必要ですよね。レンダラは撮影した映像の現像部分を担っていると考えてください。これに加えて、光源（ライト）や物体（オブジェクト）を駆使して 3D グラフィクスを作っていきます。
![scene1.png](https://qiita-image-store.s3.amazonaws.com/0/15840/0421c2e4-8b92-3535-2610-9871c78aa74c.png)

### Animation
この撮影の手順を繰り返して、コマ撮りを行うことでアニメーションを表現することもできます。
![scene2.png](https://qiita-image-store.s3.amazonaws.com/0/15840/f06fd27d-148e-339f-f567-ce934217f992.png)

## ステップ 1: 基本要素のセットアップ
それではいよいよ  Javascript のコーディングに入っていきます。

```js:app.js
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(
    75, window.innerWidth / window.innerHeight, 0.1, 1000);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
```

まずは、先程述べた 3D グラフィクスの 3 つの基本要素、シーンとカメラとレンダラを作ります。`THREE.PerspectiveCamera`に設定したパラメタは一旦気にせず行きましょう。ただ、`window`がブラウザすなわち画面を表しているので、その縦横比 (width / height) を指定しているんだな〜ということが理解できれば OK です。WebGL で 3D グラフィクスを表現するので、`WebGLRenderer`を使います。このレンダラにも縦横比を設定しています。(`setSize`) 最後に、このレンダラがレンダリング結果を出力する要素 `domElement` を返しますので、`document.body.appendChild` を用いて HTML に貼り付けてあげましょう。
ここまで書いて index.html を表示すると、真っ黒な画面が現れるはずです。まだライトもオブジェクトもないので、真っ黒になります。（実はまだレンダリングもしていません！）
![step1.png](https://qiita-image-store.s3.amazonaws.com/0/15840/c31af394-060c-c472-d3d6-1414604efb36.png)

**変な余白がある？**
おそらく `body {margin: 0;}` が効いていません。CSS が正しく読み込まれているか、記述が正しいか確認してください。

**ちっちゃい黒い四角形が見える？**
おそらく `canvas {height: 100%; width: 100%}` が効いていません。CSS が正しく読み込まれているか、記述が正しいか確認してください。

## ステップ 2: 箱を置く
続いて、JavaScript にライトとオブジェクトを置きましょう。

```js:app.js
//  続き
var geometry = new THREE.BoxGeometry(1, 1, 1);
var material = new THREE.MeshBasicMaterial({color: 0x00ff00});
var cube = new THREE.Mesh(geometry, material);
cube.position.set(0, 0, -5);
scene.add(cube);
renderer.render(scene, camera);
```

このコードを書き下すと、

- (幅, 高さ, 奥行き) = (1, 1, 1) の箱の形状を作る。
- 0x00ff00（＝#00ff00）色の表面素材を作る。
- 箱の形状に表面素材をかぶせて箱を作る。
- 箱を (x, y, z) = (0, 0, -5) の位置に置く。
    - z は値が大きければ大きいほど手前、小さければ小さいほど奥に行きます。
- その箱をシーンに加える。
- レンダリングする。

となります。
少しカメラを引く理由は、箱の位置とカメラの位置がまったく同じになってしまうためです。位置が同じだと、箱の中にカメラが収まってしまうので、少し引くことで箱の外に出ます。
ここまでの結果をブラウザでみると、画面の中央に緑色の正方形が見えるはずです。
![step2.png](https://qiita-image-store.s3.amazonaws.com/0/15840/f8cf969b-8ff1-9d48-baf1-4343fdd7b518.png)

## ステップ 3: 箱を回転させる
続いて、アニメーションを導入しましょう。

```js:app.js
//  レンダリング部分を書き換える
// renderer.render(scene, camera);
function render() {
  cube.rotation.x += 0.1;
  cube.rotation.y += 0.1;
  requestAnimationFrame(render);
  renderer.render(scene, camera);
}
render();
```

さっきのレンダリング部分を上のように書き換えます。`requestAnimationFrame` はアニメーションを実現するのに役立つ関数で、ブラウザが画面を再描写する準備が整った時点で、引数に渡された関数（今回は `render`）を実行します。つまり、一旦 `render` が実行されると、`requestAnimationFrame(render)` によってまた `render` が実行され・・・`render`が繰り返し実行されることになります。この再描写によってコマ撮りが実現できます。今回は１コマ撮るたびにすこし箱を回転させることにしましょう。活発に回転する緑の箱が見えればクリアです。
![step3.gif](https://qiita-image-store.s3.amazonaws.com/0/15840/a3318426-b7e1-b7b2-7efe-34a15e46bbda.gif)

## ステップ 4: VR を導入する。
ついに、この 3D グラフィクスを VR 体験に変換します。

### WebVR Boilerplate
ここでは、[WebVR Boilerplate](https://github.com/borismus/webvr-boilerplate) を用いて VR 体験を導入することにします。Web VR Boilerplate は Bris Smus 氏によってメンテナンスされているライブラリで、幾つかのコントローラを読み込むだけで簡単に ThreeJS で作成した 3D グラフィクスに VR 体験を導入することを可能にします。
最低限必要なのは、下記の 4 つのファイルです。まずはこれをダウンロードするところからはじめましょう。

- https://github.com/borismus/webvr-boilerplate/blob/master/node_modules/three/examples/js/controls/VRControls.js
- https://github.com/borismus/webvr-boilerplate/blob/master/node_modules/three/examples/js/effects/VREffect.js
- https://github.com/borismus/webvr-boilerplate/blob/master/node_modules/webvr-polyfill/build/webvr-polyfill.js
- https://github.com/borismus/webvr-boilerplate/blob/master/build/webvr-manager.js

### HTML の更新
ひとまず、ダウンロードしたファイルを app.js と同じフォルダに配置したら、下記のように HTML を書き換えます。

```html:index.html
<!doctype html>
<html>
  <head>
    <meta charset="utf8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <link rel="stylesheet" type="text/css" href="default.css">
  </head>
  <body>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r76/three.min.js"></script>
    <script src="VRControls.js"></script>
    <script src="VREffect.js"></script>
    <script src="webvr-polyfill.js"></script>
    <script src="webvr-manager.js"></script>
    <script src="app.js"></script>
  </body>
</html>
```

### Javascript の記述
`app.js` を下記のように書き換えます。ステップ 3 までのコードを見比べて何が変わったのか確認してください。

```js:app.js
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(
        75, window.innerWidth / window.innerHeight, 0.1, 1000 );
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

var controls = new THREE.VRControls(camera);
controls.standing = true;

var effect = new THREE.VREffect(renderer);
effect.setSize(window.innerWidth, window.innerHeight);

var manager = new WebVRManager(renderer, effect);

window.addEventListener('resize', onResize, true);
window.addEventListener('vrdisplaypresentchange', onResize, true);

var geometry = new THREE.BoxGeometry(1, 1, 1);
var material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
var cube = new THREE.Mesh( geometry, material );
cube.position.set(0, 0, -5);
scene.add( cube );

function render() {
  cube.rotation.x += 0.1;
  cube.rotation.y += 0.1;
  requestAnimationFrame(render);
  controls.update();
// renderer.render(scene, camera);
  manager.render(scene, camera);
}
render();

function onResize(e) {
  effect.setSize(window.innerWidth, window.innerHeight);
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
}
```

まず、`controls` が加わりました。これは Camera Control と呼ばれるもので、デバイスの向きとカメラの向きを合わせる働きを担っています。`effect` は画面を分割したり、レンズディストーションを適用したりすることで VR 効果を担っています。`manager` はそれらを統括して 3D グラフィクスを表示する部分を担っています。そのため、`render` を担うのもこの `manager` になります。また、ここからは全画面表示になったりと頻繁に画面サイズが変わるので、その際の画面調整をするための `onResize` メソッドも記述しておきます。

この時点で、WebVR の導入は完了です。デスクトップではマウスのドラッグアンドドロップによって世界を見渡すことができます。スマートフォンで見ると、デバイスの向きに合わせて世界を見渡すことができるようになっています。そして、VR デバイスをつけると視差によって立体的に見えるはずです。

ひとまず手持ちのスマートフォンですぐ確認したい場合は、Dropbox にファイル一式をアップロードすると便利です。
**参考** [Dropboxでホームページを公開するまでの流れをまとめてみた | CARAMEL SOURCE.](http://caramelsource.net/archives/8542.html)

![step4.png](https://qiita-image-store.s3.amazonaws.com/0/15840/12744c73-cfa5-b26a-88f3-200ca904b93b.png)


# まとめ
最低限のコードで WebVR 体験を実現することを目標に、ThreeJS と WebVR Boilerplate を用いた WebVR 開発手順を紹介しました。いかがでしたか？簡単に実装できそうじゃないですか？
さらに面白い WebVR 体験を提供するために、次のようなステップが考えられます。すでに WebVR の基礎はできています。ここまでのコードをしっかり理解して、自分の実現したい VR を目指して開発を進めていきましょう。

- インタラクションの導入。デバイスボタンのクリックまたは一定時間の凝視を判定することで、ただ眺めるだけではなくインタラクションが可能なコンテンツを作ることができます。
- パフォーマンス・チューニング。レンダリングするオブジェクトの数が増えると処理が重くなり、VR 体験がスムーズにならなくなることがあります。Chrome 開発者ツールなどを用いてパフォーマンスを確認して、パフォーマンスを改善する工夫が必要になります。
- ダイナミックなコンテンツ。WebGL では、`canvas` や `video` といった DOM 要素をテクスチャとして用いることができます。つまり、より動きのあるコンテンツを 3D グラフィクスの中に埋め込むことができます。また　AJAX を駆使することで、どんどんコンテンツが更新されていくような WebVR コンテンツを作ることも可能です。

素敵な WebVR ライフを！