ちら帳

喉元を過ぎると熱さを忘れる自分の為の、ちら裏メモ帳ブログです。

React VR 番外編:THETA Sで撮った写真や動画を使う

こんにちは。

TourSampleも公開されたことですし、そろそろチェスボード以外の背景が必要とされている時期なのではないかと思うので、今回はTHETAで撮った写真や動画をReactVRで使うお話をしたいと思います。

とはいっても、React VR要素がほぼ0に近しいので、今回は番外編としました。 あと、別にリコーの回し者じゃないです。

準備

パソコン用アプリケーションをダウンロードしておきます。

ダウンロード | RICOH THETA

上記ページから基本アプリをダウンロードしておいてください。

写真

まずはカメラ内の画像をPCに移します。 カメラとPCをケーブルで繋いで画像をインポートするのが手っ取り早いかと思います。

次に基本アプリを使って写真に天頂補正をかけます。

天頂補正の機能を使うと撮影の際に傾いてしまった写真を補正することができます。

Windows用の基本アプリでの天頂補正】

  1. 基本アプリを立ち上げて、ビューアに画像をドラッグ&ドロップします。
  2. ファイル>天頂補正書き出し>XMP付きJPEGデータを選択して適当な名前でファイルを書き出します。

Mac用のアプリは試していませんが、同じ要領で補正ができるのではないかと思います。

補正済みの画像を書き出せたら、React VRのプロジェクト内のstatic_assetsディレクトリ内に画像を置いて、Panoコンポーネントで画像を表示させましょう。

動画

動画も写真と同じようにPCに移してきましょう。

写真と同じ要領でPCに動画を移してくると気付くことがあります。 魚眼画像を2つ並べたような動画になっているのです。

全天球動画として表示するには正距円筒図法の映像になっている必要があります。

専用ビューア以外では全天球表示できないのかと悩みましたが、基本アプリを使うことで魚眼画像の動画を正距円筒図法の動画に変換することができました。

f:id:chira_pym:20170812013228p:plain

ちなみに天頂補正のオプションはデフォルトでチェックがついています。 特に理由がなければつけておいた方がよいと思います。

これで正距円筒図法の動画が出力できます。 このあたりの技術の詳細が気になる方はこちらをご確認ください。

jp.ricoh.com

動画を変換できたら、React VRのプロジェクト内のstatic_assetsディレクトリ内に動画を置いて、VideoPanoコンポーネントで動画を表示させましょう。

全天球動画の再生に関しては公式サンプルがあるので、以下のページを参照してください。サンプルをコピペするだけで動画の再生ができると思います。 github.com

注意点としてフルHDで撮影してもHMDで動画を見る場合には映像は粗めになります。

これに関しては、以下の記事のような試みをされている方がいらっしゃるので、画像処理能力の高いマシンをお持ちの方は参考にされてみてはいかがでしょうか。 izm-11.hatenablog.com

THETA のモデルについて

THETAのサイトではTHETA SとTHETA SCの2つのモデルが紹介されています。 最新モデルはSCですが、Sが最上位モデル、SCはスタンダードモデルの位置づけです。

色々と違いがあるようですが、5分以上の動画を撮りたいのであればSを購入した方が良いです。

SCの購入を検討している方は、9月から初音ミクさんコラボモデルの受注販売が開始されるので、それを待ってもいいのかもしれません。 japanese.engadget.com

またSの購入を検討している方も、4k動画、360°空間音声に対応した次世代モデルが年内に発売予定との話もあるので、こちらの購入を検討するのも良いかと思います。

今日のひとこと

全天球カメラもいいけれども、全天球CGを作れるようになった方が制作の幅が広がる気がします。

React VR: 最近の活動

最近ブログの更新をさぼりがちなちらです。 今回はとりあえず活動履歴的なものを更新してお茶を濁したいと思います。

MMDモデルの表示に挑戦

とはいっても、ツールで手動でMMDモデルをOBJとMTLに変換してVR空間内に表示させようとしただけなのですが、それでもかなり苦労しました。 結局、PMDファイルはアクセサリーとかを外して、objのデータを少しいじって、テクスチャを上下左右反転させたら、かろうじてうまくいきました。PMXファイルもやってみましたが、足のテクスチャがうまく表示できませんでした(´・ω・`)

Pairsサンプルを動かす

React VRに新しくPairsというサンプルが追加されていたので動かしてみました。

READMEにはA real-time, multiplayer virtual reality matching pairs game と書いてあったのですが、pairs gameが分からなくて、動かすまで一切なんのゲームかわかりませんでした。神経衰弱ゲームって英語でpairs gameっていうんですね。

ターン制じゃないマルチプレイ神経衰弱ゲームで、まだまだ開発途中なのか、ゲームとしてはあまり完成されていませんでした。 ローカルで動かした感じ4人までマッチングできましたが、すぐ通信が切れてしまいました。

見た目が簡素なのであまり面白味はないかもしれませんが、マルチプレイのコンテンツを作る際には参考になりそうな気がします(ソースをちゃんと読んでいないので詳細が語れない)

github.com

ツアーコンテンツを作ってみた

GitHubにいくつかReact VRで作ったツアーコンテンツがあったので、見様見真似で自分も作ってみました。 会社周辺を撮ったパノラマ画像を使って、駅から会社までの間を移動できるコンテンツを作ったのですが、家で見たらすっごくげんなりしました。

ちなみに完成してから数日後、React VRのリポジトリにもTourSampleが追加されたのでそちらも動かして遊びました。

画面遷移やUIの作り方など色々と参考になるので、 こちらはぜひ一度は動かしてみるべき&ソースを読むべきサンプルだと思います。

github.com

WebVRのUIについて考えてみた

WebVRのUIはどういうのが使いやすいのかを色々と考えてみましたが、結局のところ、とりあえずマテリアルデザインみたいな動きとかしてればいいのかなと思いました。

UIはインタラクティブであった方が分かりやすいですし、ユーザーも不安にならないと思うので、適度にアニメーションは入れた方が良いと思います。個人的には少なくともVrButtonのonEnterには必ずアニメーションを入れるようにしたいと思っています。

↓注視すると大きくなる画像

↓注視するとモーダルに変形するボタン

あとローディングに時間のかかるような場面で単にくるくるするアニメーションを使うのはやめた方がいいではないかと思っています。 VRって処理が重いイメージがあるので、読み込み経過が分からないと結構不安になります。

活動を振り返る

Reactの知識がほぼなく、趣味の範囲でやっているため、なかなか思うようにコンテンツが作れてないのが現状です(´・ω・`) JSフレームワークを勉強するならVue.jsと思っていたのですが、VRのためにReactの勉強を頑張ってみようと思います。

また、今回紹介した活動内容ですが、いくつかは気が向いたら詳細な記事を書きたいと思っています。 その頃にはやった内容を忘れていそうですが。

ではでは。

今日のひとこと

スプラトゥーン2を買いました。進捗よさようなら。

React VR : 仮想キーボードを使った入力

React VRで仮想キーボードを扱えるパッケージを見つけたので、使ってみました。

www.npmjs.com

外観はこんな感じになります。キー入力はカーソルを合わせてクリックする方式です。

f:id:chira_pym:20170621205247p:plain

使い方

インストー

React VRのプロジェクト下に移動して仮想キーボードのパッケージをインストールします。

npm install react-vr-textinput

react-vr-textinput/static_assets 内にあるup.pngとdown.pngをプロジェクト内のstatic_assetsにコピーしておきます。

カーソルの設定

キーボードを使うにはカーソルが必要になります。 注視カーソルで入力するのはつらいので、コントローラーかマウスを使うことにします。

コントローラー

コントローラーは公式のサンプルにあるThreeDOFRayCasterを利用すると簡単に実装できます。

github.com

コントローラーの表示はこんな感じになります。

f:id:chira_pym:20170701191344p:plain

マウス

マウス用のraycasterはもともと用意されているものを拡張したクラスを用意します。

import {
    MouseRayCaster
} from 'ovrui';

export default class MyMouseRayCaster extends MouseRayCaster {

    drawsCursor() {
        return true;
    }
}
client.js

raycastersオプションに各raycasterを指定します。加えてコントローラーの描画にsceneの指定も必要になります。

// Auto-generated content.
// This file contains the boilerplate to set up your React app.
// If you want to modify your application, start in "index.vr.js"

// Auto-generated content.
import {VRInstance} from 'react-vr-web';
import ThreeDOFRayCaster from '../src/ThreeDOFRayCaster';
import MyMouseRayCcaster from '../src/myMouseRayCaster';
import * as THREE from 'three'

function init(bundle, parent, options) {
  const scene = new THREE.Scene();
  const vr = new VRInstance(bundle, 'TestInput', parent, {
    // Add custom options here
    raycasters: [
        new ThreeDOFRayCaster(scene),
        new MyMouseRayCcaster()
     ],
     scene: scene,
     cursorVisibility: 'auto',
    ...options,
  });
  vr.render = function() {
    // Any custom behavior you want to perform on each frame goes here
  };
  // Begin the animation loop
  vr.start();
  return vr;
}

window.ReactVR = {init};

入力フィールドの表示

サンプルコードをもとに仮想キーボードの入力フィールドを表示します。 Submitを押すとフィールドの隣に入力した文字列が表示されるようになっています。

import React from 'react';
import {
  AppRegistry,
  asset,
  Pano,
  Text,
  View
} from 'react-vr';
import TextInput from 'react-vr-textinput'

export default class TestInput extends React.Component {

  constructor(props) {
    super(props);
    this.state = {
      text : ''
    };

    this.submitHandler = this.submitHandler.bind(this);
  }  
  
  submitHandler(string) {
    this.setState({text : string});
    console.log('the text received by the submitHandler is ' + string);
  }
   
  render() {
    return (
      <View>
        <Pano source={asset('chess-world.jpg')}/>
        <TextInput onClick={this.clicked} onSubmit={this.submitHandler} rows={1} 
        cols={20} x={-1} y={0.2} z={-1.5} rotateY={null} rotateX={null} textColor={'black'} backgroundColor={'pink'} keyboardColor={null} keyboardOnHover={null}/>
        <Text style={{transform: [{ translate: [0.2, 0.63 , -1.5]}]}}>{this.state.text}</Text>
      </View>

    );
  }
}

AppRegistry.registerComponent('TestInput', () => TestInput);

動作確認

コントローラー

f:id:chira_pym:20170701223222g:plain

マウス

2回目の入力でテキストの位置がおかしくなっていますが、マウス入力だからというわけではなく、 コントローラーで入力しても同じことが起きます。

f:id:chira_pym:20170701223048g:plain

遭遇した不具合

  • 入力フィールドが表示されず、TextInputがないとエラーが出た
      node_modules/react-vr-textinput/index.jsの内容に不備がありました。
//module.exports = require('./src/TextInput'); 
module.exports = require('./src/textInput');

追記:Issueを書いたら迅速に直してもらえました。ありがたや。

  • VRモードにすると入力フィールドが上の方に表示される
      HMDを被った瞬間、上の方に行ってしまうのはなぜなのでしょう。

  • VRモードにするとキーボードにカーソルが合わなくなる
      コントローラーから注視カーソルに変えたら直ったので、コントローラー側の実装とかみ合っていなかったようです。

  • VRモードでカーソルの動きにhoverのアニメーションがついてこれていない
      しばらく待っていると、あとからカーソルを合わせた順にキーが点滅していくので、表示の処理が追いついていないっぽいです。

  • VRモードでたまに入力内容がフィールドに反映されない
      おかしいなと思ってHMDを外して画面を見てみると、ちゃんと入力されているので、表示が間に合っていないようです。

GitHubの方に今後は処理速度の向上を目指していくみたいな記述があったので、アップデートに期待ですね。

This module is fully functional. Our next goal is to refactor for speed and also further enhance customizability through providing users even more props. We are also working on adding auto-complete support for faster typing. This will be an optional component that users can select to enable with their text input box and virtual keyboard. Please feel free to ask for additional functionality and we will try to add features as soon as possible.

今日のひとこと

ZenFone ARが発売されたせいか、最近 DayDreamやTangoについてのツイートを見かける機会が多くなりました。

楽しそうでうらやましいです。

Qiitaのぐるぐるまわるコメントテスト会場が面白い

とにかくはっちゃけてて面白いので未読の方はぜひ一読を。

qiita.com

ぐるぐるぐるー





<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<span class="fa fa-spin" style="color:#ff7f7f"><span class="fa fa-spin" style="color:#ff7fbf"><span class="fa fa-spin" style="color:="#ff7fff""><span class="fa fa-spin" style="color:#bf7fff"><span class="fa fa-spin" style="color:#7f7fff"><span class="fa fa-spin" style="color:#7fbfff"><span class="fa fa-spin" style="color:#7fffff"><span class="fa fa-spin" style="#7fffbf"><span class="fa fa-spin" style="color:#7fff7f"><span class="fa fa-spin"style="color:#bfff7f"><span class="fa fa-spin" style="color:#ffff7f"><span class="fa fa-spin" style="color:#ffbf7f"></span></span></span></span></span></span></span></span></span></span></span></span>

2017/6/16 追記

元の記事が盛り上がりすぎて重たくなっていますね。自分のスマートフォンからはもう見れなくなってしまいました。
もしかすると元の記事を見れずにこちらの記事を見ていただいてる方もいらっしゃるかもしれないので、とりあえず概要的なものを追記しておきます。

Font Awesomeという、とても有名なWebアイコンフォントがありまして、アイコンに対して表示効果(アニメーションとか)を付与するクラスなんかも提供されてたりします。

fontawesome.io

それで今回、Font Awesomeの効果をアイコン以外に使う遊びが流行っているのです。

発端となった記事

qiita.com

入力にHTMLタグを許容するタイプの入力フォームは対応大変そうですね。

技術的には

 .markdown-form * { animation: none !important; } 

とか指定しておけばアニメーションを止められると思うのですが、要件的にそれやっていいのかどうかとか色々とあると思うので。

おわり

2017/6/20 追記

QiitaでHTML要素の属性指定ができなくなったそうです(一部の外部サービスの貼り付けを除く)

http://blog.qiita.com/post/162037299989/disable-html-class
blog.qiita.com

過去にはbootstrapの属性を利用したXSS脆弱性もあったようですし、こういうのって色々としょうがないのかなと思う今日この頃です。

ただWebフォントを使えなくなるのはちょっと残念だなと思いました。
emojiじゃなくてシンプルなアイコンフォントを好んで使っていた方もいたのではないかと思うので。 これを機にemojiが拡張されたりとか…厳しいかなあ。

React VR : 注視カーソルでクリックする

React&Three.js初心者のため、週一で記事を書くのがそろそろ厳しくなってきました。ちらです。

今回はVR界隈ではちょくちょく見かける、見つめているとクリックできるボタンを作りました。

作ったボタン

ボタンを見つめている間、何も起きないと面白くないので、次のような効果もつけました。

  • ボタンを見つめているかどうかでボタンのテキストが変わる
  • ボタンを見つめている間はボタンがぶるぶるする
  • 物理クリック or 注視クリックされたらボタンのテキストが変わってクリック音もする

結果、できたものがこちらです(GIFを撮る都合上、VRモードにはしていません) f:id:chira_pym:20170612222738g:plain

こうして見てみると、もっとまともな例を作るべきだった気がしてなりませんが、寝ている顔文字をぶるぶるさせるのも、クリック時にお布団から飛び出るようにするのも実装しているときは結構楽しかったです。

実装

カーソルの表示

注視入力を実装するにあたりカーソルを表示させる必要があります。

詳しい説明については、以前記事に書いたので割愛します。 chira-memo.hatenablog.com

注視クリックできるボタンの実装

注視でボタンをクリックできるようなAPIはないので、onEnterの発火時にsetTimeoutを使って、クリックしたときに実行したい処理を1秒後に実行するようにします。
視線がボタンから外れたり、入力機器を使ってボタンがクリックされたときにはclearTimeoutでタイマーを解除します。

今は自力で実装する必要がありますが、UI的にはよくあるパターンのひとつなので、そのうち標準APIになったりするのかもしれません。

サウンドの再生についてはこちらを参考にしました。

facebook.github.io

アニメーションは…ドキュメントを読んでもあまりよく分からなかったので、参考になりそうなコードを見つけてきて見様見真似で書きました。変な書き方になってたらごめんなさい。

そのうちアニメーションについても記事が書けるといいなと思います。

"use strict";

import React from "react";
import {
    AppRegistry,
    asset,
    Pano,
    Text,
    Image,
    View,
    VrButton,
    Animated,
    VrSoundEffects
} from "react-vr";

import CylindricalPanel from "CylindricalPanel";

const Easing = require("Easing");

class ReactVrSample extends React.Component {
    render() {
        return (
            <View>
                <Pano source={asset("chess-world.jpg")} />
                <CylindricalPanel
                    layer={{ width: 2000, height: 720 }}
                    style={{ position: "absolute" }}
                >
                    <View
                        style={{
                            opacity: 1,
                            width: 2000,
                            height: 720,
                            alignItems: "center",
                            justifyContent: "center",
                            marginTop: 100
                        }}
                    >
                        <SleepingButton />
                    </View>
                </CylindricalPanel>
            </View>
        );
    }
}

class SleepingButton extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            text: "(¦3[___]",
            shakeValue: new Animated.ValueXY({ x: 0, y: 0 })
        };

        this.clicked = this.clicked.bind(this);

        VrSoundEffects.load({
            wav: asset("click.wav"),
            ogg: asset("click.ogg"),
            mp3: asset("click.mp3")
        });
    }

    render() {
        return (
            <Animated.View
                style={{
                    transform: this.state.shakeValue.getTranslateTransform()
                }}
            >
                <VrButton
                    onEnter={() => this.enter()}
                    onExit={() => this.exit()}
                    onClick={() => this.click()}
                >
                    <Text
                        style={{
                            margin: 10,
                            width: 400,
                            fontSize: 70,
                            fontWeight: "300",
                            borderRadius: 20,
                            backgroundColor: "pink",
                            textAlign: "center"
                        }}
                    >
                        {this.state.text}
                    </Text>
                </VrButton>
            </Animated.View>
        );
    }

    enter() {
        this.timeout = window.setTimeout(this.clicked, 1000);
        this.setState({ text: "(:3[___]" });

        const shakeValue = 1;

        this.animation = Animated.parallel([
            this.shake(shakeValue, shakeValue)
        ]);

        this.animation.start();
    }

    exit() {
        this.timerReset();
        this.clearAnimation();
        this.setState({ text: "(¦3[___]" });
    }

    click() {
        this.timerReset();
        this.clicked();
    }

    clicked() {
        this.clearAnimation();
        this.setState({ text: "(:з )=[___]" });
        VrSoundEffects.play({
            wav: asset("click.wav"),
            ogg: asset("click.ogg"),
            mp3: asset("click.mp3")
        });
    }

    timerReset() {
        if (!this.timeout) {
            return;
        }

        window.clearTimeout(this.timeout);
        this.timeout = null;
    }

    clearAnimation() {
        if (!this.animation) {
            return;
        }

        this.animation.stop();
        this.animation = null;
    }

    shake(x, y) {
        return Animated.sequence([
            Animated.timing(this.state.shakeValue, {
                duration: 30,
                toValue: { x, y },
                easing: Easing.linear
            }),
            Animated.spring(this.state.shakeValue, {
                toValue: { x: 0, y: 0 },
                friction: 0.5,
                tension: 400
            })
        ]);
    }
}

AppRegistry.registerComponent("ReactVrSample", () => ReactVrSample);

ボタンじゃなくてカーソルの方をアニメーションしたい

できればカーソルをリング型にして、進捗状況が分かるようなプログレスリングにしたいところなのですが、 カーソルの描画部分はライブラリの方にハードコードされてしまっているので難しいのが現状です。

github.com

makeDefaultCursorメソッド内にこんな感じのコメントがついているのでそのうちカスタマイズできるようになるかもしれません。

// TODO: Customize color or change color on hit.
// TODO: Support image provided by us or by user.

自分はおとなしくアップデートを待ちたいと思います(´・ω・`)

今日のひとこと

JSXのインデントのつけ方が全然分からないのでJsPreitterをSublime Textに入れました。
プラグインなしでは生きていけない体になりつつあります。

React VRでVrButtonに効果音をつける

今日も今日とてひっそりとReact VRをいじっております。ちらです。

今回は画面内にボタンを置いて、フォーカスが当たる・クリックされるといったイベントが発生したら効果音を鳴らすようにしてみます。

f:id:chira_pym:20170605031426p:plain

ボタンを表示する

ボタンの表示にはVrButtonコンポーネントを使います。 HTMLの<button>にとてもよく似ていますが、VrButtonはイベントをキャプチャするためのラッパーであり、外観を持たないようです。

実際にはTextやImageをラップして使います。

詳しくは例によってこちら。 facebook.github.io

VrButtonの入力に使えるボタンやキー

VrButtonでクリックイベントを検知できる入力については公式ドキュメントに以下の4つが挙げられています。

  • XboxゲームパッドのAボタン
  • キーボードのスペースキー
  • マウスの左クリック
  • スクリーンタッチ

XboxゲームパッドのAボタンが挙げられていますが、他のゲームパッドでも入力を検知することができます。 ただし、必ずAボタンが対応しているというわけではなく、Gamepad APIのGamepad#Buttons配列のインデックスが0のボタンが入力ボタンとなります。

ちなみに私はWindows 環境にOculus Touchを繋いで開発しているのですが、入力ボタンはスティックキーの押し込むところになっていました。

UnityだとOculus TouchのAボタンはjoystick button 0 が割り当てられているのですが、Gamepad APIだとキーマップが違うのでしょうか。

音を鳴らす

onClickイベントを検知して音源再生のAPIを叩く…のでもいいのですが、それをやってくれる専用のpropがVrButtonには用意されています。

  • onClickSound :ボタンにフォーカスが当たっている状態でクリックした時(厳密にはボタンなどを押して離した瞬間)に音源を再生します。
  • onLongClickSound :ボタンにフォーカスが当たっている状態で長めにクリックした時(厳密にはボタンなどを押して離した瞬間)に音源を再生します。
  • onEnterSound :ボタンにフォーカスが当たった瞬間に音源を再生します。
  • onExitSound :ボタンからフォーカスが外れた瞬間に音源を再生します。

なお、ボタンを押して離したタイミングではなく、ボタン押下時に音を鳴らしたい場合は、onButtonPressイベントを検知して、音源を再生する処理を書く必要がありそうです。

具体的にはあらかじめ再生したい音源をVrSoundEffects.loadでロードしておき、VrButttonのonButtonPressイベントが検知されたタイミングで、VrSoundEffects.playを使って音源を再生するといった流れになると思います。

サンプルプログラム

それでは前置きが長くなりましたが、VrButtonのonClick, onLongClick, onEnterSound, onExitSoundを使って効果音を鳴らすプログラムがこちらになります。

import React from 'react';
import { AppRegistry, asset, Pano, Text, View, VrButton} from 'react-vr';

export default class ButtonSample extends React.Component {

    constructor() {
        super();

        this.state = {
            sound:  {
                        wav: asset('click.wav'),
                        ogg: asset('click.ogg'),
                        mp3: asset('click.mp3')
                    }
        };
    }

    render() {
        const sound = this.state.sound;
        return (

            <View>
                <Pano source={asset('chess-world.jpg')}/>

                <View style={{
                    flex: 1,
                    flexDirection: 'row',
                    width: 5,
                    alignItems: 'stretch',
                    transform: [{translate: [-2.5, 0, -5]}],
                }}>

                <SampleButton text="Click" onClickSound={sound}/>

                <SampleButton text="LongClick" onLongClickSound={sound}/>

                <SampleButton text="Enter" onEnterSound={sound}/>

                <SampleButton text="Exit" onExitSound={sound}/>

            </View>
        </View>
        );
    }
}

export class SampleButton extends React.Component {

    static propTypes = {
        color: React.PropTypes.string,
        text: React.PropTypes.string,
        onClickSound: React.PropTypes.object,
        onLongClickSound: React.PropTypes.object,
        onEnterSound: React.PropTypes.object,
        onExitSound: React.PropTypes.object
    };


    static defaultProps = {
        color: 'coral',
        text: '',
        onClickSound: null,
        onLongClickSound: null,
        onEnterSound: null,
        onExitSound: null
    };

    constructor(props){
        super(props);
    }

    render(){
        return(

            <VrButton
                onLongClick={()=>{}}
                onClickSound={this.props.onClickSound}
                onLongClickSound={this.props.onLongClickSound}
                onEnterSound={this.props.onEnterSound}
                onExitSound={this.props.onExitSound}
                style={{flex: 1}}>
                    <Text style={{fontSize: 0.2, textAlign: 'center', margin: 0.1, height: 0.3, backgroundColor: this.props.color}}>{this.props.text}</Text>
            </VrButton>
        );
    }
}

AppRegistry.registerComponent('ButtonSample', () => ButtonSample);

onLongClickSoundの再生にはonLongClickの指定が必要

仕様なのか、バグなのか、onLongClickSoundのみ、音源を再生するには対応するイベントの定義が必要です。
onLongClickで何もしないで音だけ鳴らしたいということはほとんどないかもしれませんが、そうしたい場合は上のコードのようにonLongClick={()=>{}}を指定する必要があります。

VrButtonの詳しい実装についてはこちらのソースをお読みください。 github.com

ひとこと

cluster.でReact VRとかA-Frameとかの勉強会ってやってないのでしょうか。
おうちでのんびり聴講したいです。

React VRで頭の動きに追従するカーソルを表示する

VR初心者のちらです。

今回はVRでよくある、頭の動きに追従するカーソルの表示方法についてのお話です。

まずReact VRのカーソルシステムについてのドキュメントはこちら。

facebook.github.io

カーソルの実装

カーソルはRayCasterを利用して実装します。

学生時代のおぼろげな記憶で説明をするのも良くないので、レイキャストについての詳細は割愛しますが、ちゃんと調べたい人はレイキャストとかレイキャスティングで検索をかけるといいと思います。

RayCasterで実装が必要なメソッドは以下の3つになります。

  • getType - raycasterを一意に特定できるような文字列を指定します。
  • getRayOrigin - カメラを基準にしたときの光線の原点を3次元座標で指定します。
  • getRayDirection - カメラの向きをもとに光線の方向を3次元ベクトルで指定します。

さらにオプションで2つのメソッドが用意されています。

  • frame - アプリケーションのフレーム毎に呼び出されるメソッドです。
  • drawCursor - 目に見えるカーソルを描画するかどうか決定するメソッドです。

カーソル用に各メソッドを実装するのであれば、  

  • getType : 任意の文字列
  • getRayOrigin : [0, 0, 0] カメラの原点
  • getRayDirection : [0, 0, -1] カメラの正面方向
  • frame : 実装しない
  • drawCursor : true

とするのがいいと思います。

カーソルの可視性の制御

カーソルの可視性はcursorVisibilityというオプションで制御できます。
このオプションはclient.jsやindex.htmlでの初期化の際に設定することができます。

設定できる値は以下の3つです。

  • hidden - カーソルを常に非表示にします。デフォルトではhiddenになっています。
  • visible - カーソルを常に表示します。
  • auto - 入力イベントハンドラを持つコンポーネント(など)に視線がぶつかっているときにカーソルを表示します。

autoを設定する場合も、デバッグ中はvisibleにしておくのがよさそうです。

実装

今回はアプリケーション全体でカーソルを可視化する体でclient.jsに可視化処理を実装しました。
初期化時のオプションのraycastersにカーソル用のRayCasterを実装し、同じくオプションのcursorVisibilityをvisibleにします。

// Auto-generated content.
// This file contains the boilerplate to set up your React app.
// If you want to modify your application, start in "index.vr.js"

// Auto-generated content.
import {VRInstance} from 'react-vr-web';

function init(bundle, parent, options) {

  const vr = new VRInstance(bundle, 'ReactVrSample', parent, {
     // Pass in the custom font as an initialization property
     raycasters: [
          {
            getType: () => "mycursor",
            getRayOrigin: () => [0, 0, 0],
            getRayDirection: () => [0, 0, -1],
            drawsCursor: () => true
          } 
     ],
     cursorVisibility: 'visible',
     ...options,
  });

  vr.render = function() {
  // Any custom behavior you want to perform on each frame goes here
  };
  // Begin the animation loop
  vr.start();
  return vr;
};

window.ReactVR = {init};

せっかくなのでカーソルの当たり判定も確認できるようにします。
index.vr.jsに画面上にピンクの文字列を表示して、文字列にカーソルが当たっているときには色を赤に変える処理を書きます。

import React from 'react';
import { AppRegistry, asset, Pano, Text, View } from 'react-vr';


export default class ReactVrSample extends React.Component {
 constructor() {
    super();

    this.state = {textColor: 'pink'};
  }

  render() {
    return (
        <View>
        <Pano source={asset('chess-world.jpg')}/>
      <Text
        style={{color: this.state.textColor, 
            fontSize: 0.3,
            fontWeight: '400',
            layoutOrigin: [0.5, 0.5],
            paddingLeft: 0.2,
            paddingRight: 0.2,
            textAlign: 'center',
            textAlignVertical: 'center',
            transform: [{translate: [0, 0, -3]}],
          }}
        onEnter={() => this.setState({textColor: 'red'})}
        onExit={() => this.setState({textColor: 'pink'})}>
        This text will turn red when you look at it.
      </Text>
      </View>
    );
  }  
};

AppRegistry.registerComponent('ReactVrSample', () => ReactVrSample);

確認

白い丸がカーソルです。GIFはありませんが、VRモードにして頭を動かすとちゃんと追従します。

f:id:chira_pym:20170528234908p:plain

文字の方を見ると、カーソルが文字に当たって文字の色が変わります。 f:id:chira_pym:20170528234940p:plain

ひとこと

A-Frameのtracked controllersのサンプルみたいにコントローラーの位置をトラッキングして手を表示したいのですが、公式サンプルはまだですか。