前回、React VRでパノラマ写真を見るというコンテンツを作成しました。
サンプルを試していただいた方は「なんか結構できたじゃん感」を満喫できたと思います。それもそのはず、360度写真のみのコンテンツは、天球にパノラマ画像を貼り付けた時点で、目的の99%を達成できたといえるからです。ほとんど完成といっていい状態でしょう。
しかし、他のVRコンテンツを触ったことがある人間なら、少しもの足りなさを感じることと思います。一つはインタラクティブ性のなさ、そしてもう一つはモノの存在感のなさです。
インタラクティブ性のなさは、イベント駆動型のVRコンテンツの作成というハードルが待っていますが、今回はそれはさておき、モノの存在感について書いていきます。
パノラマ画像だけで構成された世界は、「一見、目の前で展開されている感じなのに、触ることができないほど遠くに広がる世界」と言えます。目の前の芝生に近付いて触ることはできませんし、奥の人に近付くこともできません。
仮に頑張って、インタラクティブ性を高めて奥に進めるように作ったとしても、そこにあるのは拡大されて画素が粗くなった人のようなナニカでしかありません。
VRのモノとしての存在を確固たるものにするために大事なことは、VR空間に実際にモノを置いてみるということになります。
ということで、今回はReact VRに用意されているコンポーネントのうち、モノとして置けるコンポーネント、3Dプリミティブについて扱っていきます。
いろいろなタイプのReact VR Components
公式ドキュメントにあるReact VR Componentsの目次は下記のような構成になっています。
- AmbientLight
- Box
- LiveEnvCamera
- Cylinder
- CylindricalPanel
- DirectionalLight
- Model
- Pano
- Plane
- PointLight
- Scene
- Sound
- Sphere
- SpotLight
- Video
- VideoControl
- VideoPano
- VrButton
いろいろありますね。「あれ?、これだけしかないの?」という見方もあるかもしれません。主要なものを役割ごとにカテゴライズしてみましたので順に見ていきましょう。各種コンポーネントの実装コードに興味がある方は、GitHubのReact VRリポジトリのViews以下を見てみるとよいでしょう。
空間を作るタイプのコンポーネント
まず、チェックしておきたいのは空間を作るタイプのコンポーネントです。
- VideoPano
- CylindricalPanel
- Pano
Pano
は、既に登場しましたが、360度写真をVR空間に貼り付けるタイプのコンポーネントです。
VideoPano
は、Panoと同様ですが、貼り付け可能コンテンツが360度動画となります。CylindricalPanel
は、ちょっと特殊なコンポーネントで、内側にテクスチャを貼ることができ、子コンポーネントを収録できるタイプです。今回は触れません。
ビデオ・オーディオ関連のコンポーネント
ビデオの話が少し出ましたが、ビデオ・オーディオ系に関係してくるコンポーネントは以下となります。
- Sound
- Video
Sound
は、シーンに配置するコンポーネントですが、何か形があるわけではありません。音の発生源として配置する形になります。3Dオーディオをサポートしており、ヘッドマウントディスプレイを被って顔を左右に振ったりすると、音源の場所に応じたサウンドになることが確認できます。
Video
は、2Dのビデオを再生するためのコンポーネントです。これをユーザーの前面に貼ると巨大なテレビが目の前に広がったような演出ができます。
ユーザーインターフェースとなるコンポーネント
続いて、VR世界のユーザーインターフェースとなるコンポーネントが下記2つです。
- VrButton
- VideoControl
VrButton
は、ボタンです。応用性が高く、使い勝手の良いコンポーネントと言えるでしょう。VideoControl
は、ビデオをコントロールするためのユーザーインターフェースとなります。
Video
コンポーネントとVideoControl
コンポーネントを組み合わせると、VR空間にビデオプレーヤーを作ることができます。
形状コンポーネント
そして、今回扱う形状コンポーネントです。
- Box
- Cylinder
- Plane
- Sphere
- Model
上記のうち、Modelを除いた4つは3Dプリミティブコンポーネントと呼ばれます。Box
は立方体や直方体などを作成することができるタイプのコンポーネントです。Cylinder
は、円筒形の形状コンポーネントで、設定によっては円錐形の形状を作成することができます。Plane
は、フラットな平面コンポーネントです。
ちなみに、Model
は空間に配置するコンポーネントだけど、別の3Dソフトなどで作成したモデルデータを使用するためのもので、今回は一旦スキップします。
これ以外にもライティングに関するコンポーネントも存在しますが、実際に使うところで解説していきます。
では、今回は実際に3Dプリミティブコンポーネントを、シーンにいろいろと配置し、様々なライティングをしてみましょう。
3Dプリミティブコンポーネントを配置してみよう
さて、これから3Dプリミティブコンポーネントを配置していろいろ実験していくわけですが、その前に、何か仮想現実の実験ぽくするための会場づくりをしましょう。React VRで何も背景に敷かない場合、漆黒の空間に単なる立方体が浮かぶことになります。それでは何も趣がないですよね。
ということで、VR空間をホロデッキ(アメリカのTVドラマ・映画のスタートレックに登場するホログラフィックルーム)として定義してみましょう。ホロデッキってなあに?って方は、Wikipediaのホロデッキに関する説明と写真をご覧ください。
ホロデッキは、黒い部屋に、方眼紙のような感じで黄色の線が入っている部屋です。これを実現するためにPano
コンポーネントを使います。Pano
コンポーネントは360度写真を閲覧するために前回使いましたが、Pano
コンポーネントはもう一つ使い方があって、6つのテクスチャを配列で渡してあげると、立方体のの内側にテクスチャが貼られた部屋を作ることができます。
では、まず新規React VRプロジェクトを作成するところから始めてみましょう。
1. 新規React VRプロジェクトを作成し、Panoコンポーネントで部屋を作る
まずはなにはともあれ、新規ReactVRプロジェクトを立ち上げましょう。名前はHello3DObject
としましょう。
$ react-vr init Hello3DObject $ cd Hello3DObject
続いて、Pano
コンポーネントを使って、テクスチャが貼られた立方体の空間を作っていきます。プロジェクトルートにある「index.vr.js」のView
コンポーネント以下を編集していきます。今回Pano
コンポーネントの内側には、前後左右上下の6面にテクスチャを貼ります。貼るテクスチャは、フリーのグリッドパターン – 20 Seamless Photoshop Grid Patternsのうち一枚をPhotoShopで修正した格子状のものです。
上記リンク先でダウンロードしたファイルを解凍し、中に入っている「holodeck.png」を「static_assets」ディレクトリ以下に配置します。
Pano
コンポーネントに対して六面にテクスチャを貼るには、source
属性にuri
プロパティを持ったオブジェクトを6つ含んだ配列を渡します。asset
関数は、ファイル名を渡すと{uri: パス名}
の形式で返るので、asset
関数をそのままsource
属性へ渡せばいいことになります。
<View> <Pano source={[ asset('holodeck.png'), // 右 asset('holodeck.png'), // 左 asset('holodeck.png'), // 上 asset('holodeck.png'), // 下 asset('holodeck.png'), // 後 asset('holodeck.png') // 前 ]} /> </View>
さて、まずはここで一旦ビルドしてみましょう。
$ npm start
ブラウザで、http://localhost:8081/vr/
を開いてみましょう。スタートレックのホロデッキのような空間が出現したと思います。テクスチャのつなぎ目とか細かい点はサンプルということでご容赦ください。
2. Box
をいくつか並べて配置してみる
ホロデッキ的な空間が出来たところで、Box
コンポーネントを配置してみましょう。Box
コンポーネントはシンプルなコンポーネントですが、よくありがちな失敗として、Box
コンポーネントをインポートし忘れるということがあります。これから登場するCylinder
やSphere
も同様にimportし忘れがないようにしましょう。
import { AppRegistry, asset, Pano, Box, Cylinder, Sphere, View, } from 'react-vr';
では、Box
コンポーネントを目の前に3つ配置してみます。Pano
コンポーネントの下にBox
コンポーネントを下記のように3つ記述します。
<View> <Pano ... /> <Box dimWidth={0.5} dimDepth={0.5} dimHeight={0.5} style={{ color: '#00f', transform: [{translate: [1, 0, -2]}], }} lit={false} /> <Box dimWidth={0.3} dimDepth={0.5} dimHeight={1} style={{ color: '#f00', transform: [{translate: [0.2, 0, -2]}], }} lit={false} /> <Box dimWidth={0.7} dimDepth={1} dimHeight={0.3} style={{ color: '#0f0', transform: [{translate: [-0.7, 0, -2]}], }} lit={false} /> </View>
dimWidth
、dimHeight
、dimDepth
の各属性は、幅、高さ、奥行きのサイズに相当します。この比率を変えることで、立方体や柱のようなもの、平らな板のようなものを作り出すことができます。
また、style
属性で色が定義されていることもわかります。3つの色は右から青、赤、緑の原色です。style
属性にtransform
というプロパティが定義されていますが、ここで配置を指定しています。位置関係の変形は、translate
で指定します。配列で指定し、左からX座標、Y座標、Z座標となります。Z座標は奥行きです。ちなみにマイナスの数値になればなるほど初期状態の位置から奥へ向かって移動する形になります。
lit
という属性がありますが、こちらはライティングに関するプロパティで、後ほど解説します。
Box
コンポーネントを配置できたところで、他のコンポーネントを配置していきましょう!
3. Box
だけでなく、Cylinder
やSphere
を配置してみる
さて、今度はCylinder
やSphere
もシーンに配置してみます。一旦Box
コンポーネントのパートで作った3つのボックスは消してしまいましょう。勢いあまってホロデッキっぽくしているPano
まで消さないようにしてくださいね、情感がなくなってしまいますので。
まず、Cylinder
コンポーネントについて。Cylinder、つまりカタカナでいうシリンダーは、円筒を意味しますが、Cylinder
コンポーネントはプロパティ次第で円錐形にもなります。ポイントはradiusTop
とradiusBottom
で、radiusTop
が小さければ山のようになっていき、radiusBottom
が大きければ、ラッパのような形状になっていきます。どちらの値も等しい時だけ円筒形になります。
続いてSphere
コンポーネントですが、Sphereというのは球体です。Sphere
コンポーネントはなかなか使い出があるコンポーネントで、四角形や円筒形を使う機会はそれほどないかもしれませんが、球体というのはテクスチャを貼り付ければ惑星になったりと応用性が高い3Dプリミティブコンポーネントです。下記のようにシーンに配置してみました。それぞれ一つずつ配置しています。Sphere
コンポーネントにはwidthSegments
とheightSegments
というプロパティがありますが、これは球体を構成するセグメント(分割数)を定義しています。この数が増えるとなめらかな球体へ近づいていきます。
<View> <Pano ... /> <Box dimWidth={0.3} dimDepth={0.3} dimHeight={0.3} style={{ color: '#00f', transform: [{translate: [0.5, -0.5, -1]}] }} lit={false} /> <Cylinder radiusTop={0.1} radiusBottom={0.4} dimHeight={0.8} segments={20} style={{ color: '#f00', transform: [{translate: [-0.2, -1, -3]}], }} lit={false} /> <Sphere radius={0.3} widthSegments={15} heightSegments={15} style={{ color: '#0f0', transform: [{translate: [-2, -1, -5]}], }} lit={false} /> </View>
今回、ちょっと配置に工夫をしています。translate
プロパティの値を変更していて、手前に立方体、奥に行くにしたがって円筒形、球体と並ぶようにしています。これで3Dプリミティブの配置は完了したわけですが、何かもの足りません。のっぺりしていて立体感がないのです。ということで、この3Dオブジェクトに光を当てていきましょう。
4. 3Dプリミティブオブジェクトに光を当ててみる
React VRでライティング(照明)を行うには、用意されている4つのコンポーネントのいずれかもしくは複数の組み合わせを使用します。
用意されているReact VRコンポーネントは下記の4つです。
- AmbientLight
- DirectionalLight
- PointLight
- SpotLight
AmbientLight
は、環境光と呼ばれるもので、シーン全体に均一な影響を与える光源を定義します。これは無指向性で、すべてのオブジェクトが等しく明るくなります。
DirectionalLight
は、指向性があるが、距離による明かりの減光は発生しないタイプの光源です。太陽の明かりのようなものと言えます。
PointLight
は、ある一点から発生する光源で、距離による減光もあります。見えない間接照明のようなものと言えるでしょう。
SpotLight
は、文字通りスポットライトのような特性を持つととらえられる光源です。指向性があり、距離による範囲の拡大と減光があります。
20程度のコンポーネントしか用意されていないReact VRにおいて、ライティング関係だけで、4つもコンポーネントが用意されているのは、一つはVRにおいてそれだけ重要であるということと、React VRが使用しているThree.jsにおいても手厚いサポートがされていること、そしておそらくはこうしたライティング関係は、カスタムコンポーネントとしての作成が困難であろうことなどが推測されます。
まずは普通の光を当てましょう。太陽のように上から降り注ぐ普通の明かりです。こういった明かりは、DirectionalLight
コンポーネントを使うことで実現できます。
では、まず準備段階として、先ほど作成した3種類の3Dプリミティブオブジェクトが持っているlit
プロパティをtrue
にしてみましょう。これで光源の影響を受けるようになります。試しにtrue
にした段階で、ブラウザをリロードしてみてください。まだ光が当たっていないため、真っ黒のオブジェクトになってしまったと思います。
続いて、DirectionalLight
コンポーネントを配置します。Pano
コンポーネントの下あたりでOKです。こんな感じにします。(importを忘れずに)
<View> <Pano ... /> {/* Lighting */} <DirectionalLight intensity={0.5} style={{ color: 'white', transform: [ {translate: [0, 0, 0]}, {rotateX: 45}] }}/> <Box ... /> <Cylinder ... /> <Sphere ... /> </View>
intensity
プロパティは、光の強度を設定します。また、光源にも色をつけることができ、スタイルシートで定義します。transform
プロパティでrotateX
の設定をしていますが、これは、ちょっと斜め前方から光を当てたいからです。本当の真上からあたる光は、側面すら陰になってしまい、あまり面白みはありません。
次はPointLight
コンポーネントを配置してみましょう。PointLightは点光源なので、任意の位置に配置すると、その周囲を照らしてくれます。うまく使うと印象的な効果を与えることができます。さて、今回は、PointLightをちょっと手前に配置し、しかもちょっと青系の光として照らしてみましょう。どんな感じになるでしょうか。
DirectionalLight
コンポーネントの下に、下記のようにPointLight
コンポーネントを設置します。
<View> <Pano ... /> {/* Lighting */} <DirectionalLight intensity={0.5} style={{ color: 'white', transform: [ {translate: [0, 0, 0]}, {rotateX: 45}] }}/> <PointLight intensity={5} style={{ color: '#00f', transform: [{translate: [0.1, -1, -2]}] }}/> <Box ... /> <Cylinder ... /> <Sphere ... /> </View>
intensity
プロパティはちょっと強めの5
にしました。色は青色、そして位置はちょっと手前でちょっと下から照らす感じにしています。
すると、こんな感じで、青い光がオブジェクトに反射するような雰囲気になりました。ちょっとムーディで怪しげですね。
さて、これで終了にしてもいいのですが、全体的にちょっと暗いですよね、そこで光の調整をしたいと思います。
ではAmbientLight
コンポーネントを配置してみましょう。AmbientLightは環境光で、全てのオブジェクトに均等に光を当てるという機能を持っています。AmbientLight
コンポーネントを使うことで、全体的な光の加減を微調整することもできます。
<View> <Pano ... /> {/* Lighting */} <DirectionalLight intensity={0.5} style={{ color: 'white', transform: [ {translate: [0, 0, 0]}, {rotateX: 45}] }}/> <PointLight intensity={5} style={{ color: '#00f', transform: [{translate: [0.1, -1, -2]}] }}/> <AmbientLight intensity={0.3}/> <Box ... /> <Cylinder ... /> <Sphere ... /> </View>
intensity
プロパティは、ほんの少しだけ設定しました。これでブラウザをリロードしてみると、ほんのりと明るくなったことがわかると思います。
いろいろな形状のものを置き、ライティングを終えたところで、こんな風に思った方はいないでしょうか。
「現実世界で立方体や球なんて、積み木とかにしか存在しないじゃないか!」
と。
四角や丸を置くためにVRを始めたわけじゃないんだという気持ちはよくわかりますので、次回は応用編として既存の3DモデルをReact VRで扱う方法について解説していきます。