VRコンテンツをWebアプリのように開発するReact VR登場、その1【がっつりReact!シリーズ 第1回】
サブシリーズ【がっつりReactシリーズ】として、React関連技術を紹介していきます。 初回はVirtual Reality技術の一つ、React VRを紹介します。
ソフト道場のがっつり試し食い!
- 2017年05月11日公開
はじめに
本コーナー「がっつり試し食い!」の執筆者に新しく加わりました、NTTテクノクロスの上原です。よろしくお願いします。
今回以降、サブシリーズ【がっつりReact!シリーズ】として、React関連技術を紹介していきます。 初回はVirtual Reality技術の一つ、React VRを紹介します。 React VRを選んだのは、本コーナー前回までの「スマホブラウザでお手軽VR」の流れからですが、 次回以降、Reactに関連した他の技術もテーマにしていく予定です。
Reactとは
React VRの説明に入る前にReactを簡単に説明しておきましょう。 Reactは、もともとFacebook社が開発したユーザインターフェース構築のためのJavaScriptのライブラリです。 SPAに代表されるモダンなWebアプリケーションの開発において、従来のjQueryなどに代わるライブラリとして人気が高まっています。 Reactは、現時点で「主流である」とまでは言えないかもしれませんが、 この数年でのJavaScriptでのフロントエンド開発において、大きなトレンドを生み出した技術と言えるでしょう。
Reactが人気を得た理由は多くあるでしょうが、その一つに、機能範囲を「ユーザーインターフェイスの構築」 に限定していることがあると思います。 React本体はアプリケーションのビジネスロジックやデータ管理について何も提供しません。 Reactでの開発では、 ロジックの実装は他の支援用ライブラリやフレームワークを組合わせて行うこともできるし、 あるいはReactだけを使いその他は自前でまかなうこともできます。この自由度がReactの適用範囲を大きく広げています。
Reactのユーザーインターフェイス構築にかかわる機能の主なものは以下のとおりです。
- 画面の構成要素を階層的にツリー状に組み立てていく。
- 画面の構成要素をコンポーネント(部品)として名前をつけ定義する。
- コンポーネントを組み立て、連携させ、管理する。また再利用可能にする。
- 同時並行的に発生する多種多様なユーザ入力を受けとりハンドリングする
- 画面を乱したり反応を遅延させることなく、処理結果を高速に画面に反映する
このような特長を持つReactをコアとして、多種多様なツールやライブラリ群がエコシステムを形成しています。
React Native
Reactの機能「画面の構成要素を階層的にツリー状に構成する」や「非同期に発生する入力イベントをハンドリングし、 画面を乱さないようにUIスレッド等を通じて画面更新させる」といった基本的な処理の流れは、 Webアプリケーションに固有ではなく、ユーザーインターフェースを持つ多くのシステムや環境に共通します。
この共通性に着目し、ReactをブラウザやDOMと無関係なモバイルネイティブアプリケーションの開発に適用できるようにしたものがReact Nativeです。 React NativeではReactを用いてiOSやAndroid上で動作するネイティブアプリケーションを開発することができます。例えば有名どころでは FacebookやInstagram、Airbnbのモバイル版アプリなどがReact Nativeで開発されています。
React Nativeで開発されたアプリは、「ハイブリッドアプリ」などと呼ばれる他のWebViewベースの技術で開発されたアプリと比べ、 一線を画するレベルできびきびと動作しますが、 このような高いレスポンス性能を達成するために、JavaScriptが動作するスレッドとOSのUIスレッドの連携制御がポイントになっています。[脚注1]
React Nativeではクロスプラットフォームのライブラリ(NativeBase)等を利用できますが、プラットフォーム間の差異を100%吸収することを目標としてはいません。ネイティブOSが提供するUIや機能を必要に応じて呼び出すようになっています。しかし、ReactでWeb開発をした経験者であれば、そこで得た知識をネイティブアプリ開発で活かすことができます。このことは「Learn Once, Write Anywhere(一回学べば、どのプラットフォームでも書ける)」と表現されています。
そしてReact VRへ
前置きが長くなりましたが、今回の本題、React VRについてです。 React VRは、React技術を用いて、WebVRベースのVRアプリケーションを簡単に開発できるようにしたものです。2016年にプレリリース版が公開、 そしてこの2017年4月に正式公開されました。
React VRは、Facebookが2014年にOculus社を買収した直接的な成果の一つと見ることができるでしょう。
React VRはブラウザ上で動作するので、Reactを直接ベースにしていると思われるかもしれませんが、 実際にはReact Nativeベースです。そうなっている理由は主に性能です。VR環境においては、 60fpsもしくは90fpsで画面更新できないと没入感が損われたり、いわゆる「VR酔い」が発生しやすくなるなど致命的な問題が発生します。
画面更新の高速化は、React Nativeでもずっと取り組まれてきた課題であり、React VRではその成果を取り込んでいます。 ソースコードの記述の仕方としても、iOSやAndroidなどと同列の「プラットフォームの一つ」としてVR環境を扱います。
ちなみに、本コーナーで去年取り上げられたA-FrameとReact VRはいずれも「ブラウザ上のWebVR環境で、JavaScriptを用いて簡単にVRコンテンツを開発する」という同じ目的をもっており、競合であると言えます。
React VRの利点
React VRの利点は、ブラウザ含め多種のデバイスが利用できるというWebVRの利点そのものに加え、 Reactの利点を得られることです。たとえば、ホットリローディングによる高速開発、仮想DOMによる効率の良い画面更新、テスト手法(jest,..)、状態管理(Redux,Flux..)、非同期処理(Thunk,Observable, Saga,..)、通信(Relay,GraphQL,..)、静的型付与(Flow,..)、静的解析(ESLint)など、実績あるReact開発でつちつかわれたノウハウやツールが活用できます。ここらへんはReact Nativeの「Learn Once, Write Anywhere」を文字通り実現しており、React経験のある開発者は、低い学習コストでVRコンテンツの開発に参入できることになります。
ターゲット環境
React VRは、WebVRを必要としますが、各種のモダンブラウザが対応を始めています。その他、HMDとしてHTC VIVE, Oculus RiftやGear VRが利用可能とのことです。iPhoneでは今のところ利用できないようです。 本記事では以下をターゲットとします。
- MacOS X版Chrome 58以降
- Android版Chrome Canary版+Google VRサービス
いずれの場合でも、Chromeの設定で「chrome:///flags#enable-webvr」を「有効にする」に設定します。
React VR開発環境のインストール手順など
公式サイト含め多数のページで設定方法やチュートリアルなどが紹介されておりますので、 ごく簡単に説明します。前提としてNode.JS環境が準備済みであるとして、以下を実行します。
$ npm install -g react-vr-cli
$ react-vr init ReactVrSample
上記で指定している「ReactVrSample」はプロジェクトの名称であり、 ルートコンポーネントの名称にもなります。 これで以下のようなプロジェクト雛形が構築されます。
ReactVrSample
├.babelrc
├.flowconfig
├.gitignore
├.watchmanconfig
├index.vr.js
├package.json
├rn-cli.config.js
├── __tests__
│ └── client.js
├── node_modules
├── static_assets
│ └── chess-world.jpg
└── vr
├── client.js
└── index.html
以降の説明では、プロジェクトのトップディレクトリを<PROJECT>と表記します。 この時点で生成されているpackage.jsonの内容は以下のとおりです。
{
"name": "ReactVrSample",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "node -e \"console.log('open browser at http:/localhost:8081/vr/\\n\\n');\" && node node_modules/react-native/local-cli/cli.js start",
"bundle": "node node_modules/react-vr/scripts/bundle.js",
"open": "node -e \"require('xopen')('http://localhost:8081/vr/')\"",
"test": "jest"
},
"dependencies": {
"ovrui": "~1.0.0",
"react": "~15.4.1",
"react-native": "~0.42.0",
"three": "^0.80.1",
"react-vr": "~1.0.0",
"react-vr-web": "~1.0.0"
},
"devDependencies": {
"babel-jest": "^19.0.0",
"babel-preset-react-native": "^1.9.1",
"jest": "^19.0.2",
"react-test-renderer": "~15.4.1",
"xopen": "1.0.0"
},
"jest": {
"preset": "react-vr"
}
}
次に、以下を実行します。
$ cd ReactVrSample
$ npm start
この状態で、WebVRに対応したブラウザで以下のURLを閲覧します。
http://localhost:8081/vr/?hotreload
「View in VR」のクリックでVRモードを開始します。
ホットリロード
先にアクセスしたURL末尾の「?hotreload」はいわゆるホットリロードを実行するための指定で、たとえば、ブラウザ閲覧中に react_vr_sample/index.vr.js中の以下のコード:
paddingLeft: 0.2,
paddingRight: 0.2,
textAlign: 'center',
textAlignVertical: 'center',
transform: [{translate: [0, 0, -3]}],
}}>
hello
</Text>
</View>
を以下に書き換えて保存してみます。
paddingleft: 0.2,
paddingright: 0.2,
textalign: 'center',
textalignvertical: 'center',
transform: [{translate: [0, 0, -3]}],
}}>
NTTテクノクロス
</text>
</view>
するとリロードボタンを押さなくても以下のようにvr画面が更新されます。視線や位置を変更せずに、コンテンツを変更していくことができます。 HMDを使ってVRに没入した状態でコンテンツを書き換えていくこともできるでしょう(でもたぶん手元のキーボードが見えませんが)。
...と思ったら日本語が化けてますね(「******」のところ)。
日本語フォントのインストール
こちらによれば、現時点ではフォントをOVRUIのfontsフォルダからダウンロードし、初期化コードに修正を加える必要があるようです。 まずは、以下を実行します。
$ mkdir static_assets/fonts
$ cd static_assets/fonts
$ wget https://github.com/facebook/react-vr/raw/master/OVRUI/fonts/japanese.png
$ wget https://github.com/facebook/react-vr/raw/master/OVRUI/fonts/japanese.fnt
さらに、<PROJECT>/vr/index.htmlと<PROJECT>/vr/client.jsを以下のように書き換えます。
<PROJECT>/vr/index.html
<html>
<head>
<title>ReactVrSample</title>
<style>body { margin: 0; }</style>
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
</head>
<body>
<!-- When you're ready to deploy your app, update this line to point to your compiled client.bundle.js -->
<script src="./client.bundle?platform=vr"></script>
<script>
// Initialize the React VR application
ReactVR.init(
// When you're ready to deploy your app, update this line to point to
// your compiled index.bundle.js
'../index.vr.bundle?platform=vr&dev=true',
// Attach it to the body tag
document.body,
{},
['../static_assets/fonts/japanese.fnt',
'../static_assets/fonts/japanese.png']
);
</script>
</body>
</html>
<PROJECT>/vr/client.js
// Auto-generated content.
import {VRInstance} from 'react-vr-web';
import * as OVRUI from 'ovrui';
function init(bundle, parent, options, fonts) {
OVRUI.loadFont(
...fonts
).then((font) => {
const vr = new VRInstance(bundle, 'ReactVrSample', parent, {
// add custom fonts
font,
// Add custom options here
...options,
});
vr.render = function() {
// Any custom behavior you want to perform on each frame goes here
};
// Begin the animation loop
vr.start();
});
}
window.ReactVR = {init};
再起動すると以下のように文字化けすることなく日本語の表示ができました。
静的コンテンツの生成
以下でReact VRで開発したコンテンツを静的ファイルとして<PROECT>/vr/buildに出力することができます。
$ npm run bundle
$ cp -r ./static_assets vr/build/static_assets
$ cp vr/index.html vr/build
さらに、上記でコピーした<PROJECT>/vr/build/index.htmlに以下の変更を行います。
$ diff vr/index.html vr/build/index.html
9c9
< <script src="./client.bundle?platform=vr"></script>
---
> <script src="./client.bundle.js?platform=vr"></script>
15c15
< '../index.vr.bundle?platform=vr&dev=true',
---
> './index.bundle.js?platform=vr',
19,20c19,20
< ['../static_assets/fonts/japanese.fnt',
< '../static_assets/fonts/japanese.png']
---
> ['./static_assets/fonts/japanese.fnt',
> './static_assets/fonts/japanese.png']
修正後のファイル<PROJECT>/vr/build/index.html全体は以下です。
<html>
<head>
<title>ReactVrSample</title>
<style>body { margin: 0; }</style>
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
</head>
<body>
<!-- When you're ready to deploy your app, update this line to point to your compiled client.bundle.js -->
<script src="./client.bundle.js?platform=vr"></script>
<script>
// Initialize the React VR application
ReactVR.init(
// When you're ready to deploy your app, update this line to point to
// your compiled index.bundle.js
'./index.bundle.js?platform=vr',
// Attach it to the body tag
document.body,
{},
['./static_assets/fonts/japanese.fnt',
'./static_assets/fonts/japanese.png']
);
</script>
</body>
</html>
これで<PROJECT>/vr/build配下を静的コンテンツとしてデプロイすることができます。
スマートフォンでのVR体験
React VRでもGoogle Cardboard、ハコスコなどの簡易HMDと、Androidスマートフォン上のChromeを用いたVR体験が可能ですが、以下に留意ください。
- Android端末はジャイロを搭載している必要があります。
- Android版Chromeは、標準でWebVR対応できるが、支障がある(ブラックアウトする、上下反転するなど)場合、Canary版を使用します。
- Chromeの設定「chrome://flags#enable-webvr」を「有効」にしておきます。
- Google VRサービスをインストールしておきます。なお、Google VRサービスはジャイロ非搭載端末にはインストールできませんし、無理にインストールしても動作しません。
- ChromeのWebVR環境はHTTPS通信でVRコンテンツを取得する必要があります。したがって、静的コンテンツはhttpsでホスティングされなければならないし、「npm start」でローカルで閲覧する場合、React Native Packagerはhttpsをサポートしていないので、httpsのリバースプロキシサーバを立てたりする必要があります。具体的な方法についてはこちらを参照ください。
設定ののち、以下に用意したURLをchromeで閲覧し「View in VR」をクリックしてみてください。
- React VRデモ: https://uehaj.github.io/rect-vr-samples/index.html (ソースコード)
問題なければ以下のように表示されます。WebVRに対応したブラウザでも閲覧できます。
おわりに
ではReact VRコンテンツを作成してみましょう、というところで紙面が尽きました。 次回はインタラクティブなReact VRコンテンツを作成していく予定です。お楽しみに。
(執筆: 上原潤二)
脚注
- [脚注1] React Nativeでは速度向上のためにWeb Workerを使用するreact-native-workersというプロジェクトがありますが、後述のReact VRでも再描画のための計算処理をWebWorkerスレッド上でバックグラウンドで実行します。このことによって、視線移動に伴なうシーンの再描画が、React側の処理に妨げられずにスムーズに実行されます。このことは、React v16で導入が予定されているReact Fiberが目指すところを、React Nativeではすでにマルチスレッドでストレートに実現していると言えるかもしれません。React Fiberはシングルスレッド環境での再描画のレイテンシを向上させるための並行リコンシリエーション機能です。
参考リンク
ソフトウェア開発全般、プログラミング言語処理系、クラウドアーキテクチャ、 Reactを中心としたフロントエンド開発に興味をもっている。Groovy/Grailsの普及活動等に従事、 JGGUG(日本Grails/Groovyユーザグループ)運営委員。 「プログラミングGroovy(技術評論社)」のメイン部分を執筆、「Grails徹底入門(翔泳社)」執筆メンバー、その他多くの雑誌記事を執筆。 横浜市在住。7才長女&3才次男の子育て奮闘中。