読者です 読者をやめる 読者になる 読者になる

VOYAGE GROUP VR室ブログ

VOYAGE GROUP VR室のブログです。コンテンツの紹介や制作方法、イベントレポートなどについて書きます。原則毎週水曜日更新。

A-Frameで壁に沿って移動する視線カーソルを実装する

A-Frame

こんにちは。買って2週間で壊れたBluetoothイヤホンがメーカー交換となり、新品を送ってもらったもののそれも1週間で壊れた jujunjun110 です。

きっと耳から怪電波が出てるんだと思います。

最近はA-Frameの勉強をしながら、3D空間・VR空間ではどういった表現が使いやすいのか研究する日々です。今回は、壁に沿う視線カーソルを作ってみたので、それについて書いていきます。

目次

壁に沿うカーソルとは

A-Frameのデフォルトの視線カーソルがこんな感じで画面中央に固定されているのに対し、

https://media.giphy.com/media/3oriNL2sQfZcltdQoo/source.gif

こんな感じでオブジェクトに沿って動くカーソルのことです。

https://media.giphy.com/media/3oz8xrdEdTQQ9GQqE8/source.gif

(ちなみに今回はこちらのフリーのモデルをお借りしました)

このタイプのカーソルは、オブジェクトに沿って動くことで、より細かい操作の際にどこをポインティングしているのかわかりやすいというメリットがあります。

また、全天球画像の裏側にモデルを配置することで、より立体感を高めるみたいなことが結構効果的なのではないかと思っていて、それもこれから研究していくつもりです。

今回は以下の2段階の手順で実装していきます。

  • cameraから出るraycasterが他のオブジェクトに交わった点とその面の法線を取得する
  • カーソルをその位置に移動し、法線の方向を向ける

さっそく見ていきましょう。

raycaster(視線)と他のオブジェクトの交点情報を取得する

まず、メインカメラにraycasterコンポーネントを付加します。

<a-entity camera look-controls wasd-controls raycaster my-ray="target: #crawling-cursor"></a-entity>

raycaster(視線)コンポーネントは他のオブジェクトと交差するたびに、

  • raycasterのついたオブジェクト側に raycaster-intersection
  • 交差したオブジェクト側に raycaster-intersected

というイベントを発火させるデキる子です。

今回はraycaster側でこのイベントをトリガーにカーソルを移動させていきます。

自作コンポーネントmy-rayをカメラに設定し、javascriptで実装していきます。


まず、取得できるraycaster-intersectionイベントの e.detail.intersectionsというところに、raycasterと交差した全ての面(Mesh)が手前から順に配列で入っているので、一番カメラに近いintersectionを取り出します。

こうして取り出したintersecrionの中の以下の値を利用します。

  • intersection.point ... 交差が発生した点の座標

  • intersection.face.normal ... 交差が発生したメッシュの法線ベクトル(=メッシュに垂直な単位ベクトル)

図にするとこんな感じですね。

f:id:jujunjun110:20161101170146j:plain

カーソルの位置と向きを適切な方向に揃える

こうして交差地点の座標と法線が取れたので、カーソルをそこに配置します。

通常、カーソル(a-cursor要素)はカメラオブジェクトの子要素として配置しますが、今回はルートに配置します。

これは、子要素の移動・回転が、親要素の回転・移動に影響されてしまうため、今回のように自分で制御する場合扱いが難しくなるからです。

<!-- カメラオブジェクト -->
<a-entity camera look-controls wasd-controls raycaster my-ray="target: #crawling-cursor"></a-entity>
        
<!-- カーソルオブジェクト -->
<a-cursor 
    id="crawling-cursor" 
    class="ignore-ray" 
    look-at="0 0 0" 
    geometry="primitive: ring; radius-outer:0.2; radius-inner:0.06;" 
    material="color:lightblue; opacity: 0.7"
></a-cursor>

カーソル向きを指定するのには、aframe-lookat-component を利用します。

これはその名の通り座標を指定することでオブジェクトをその方向に向かせることができるコンポーネントです。現在はA-Frame開発者 Kevin Ngoさんの便利コンポーネント集 kframe に統合されているので、これを読み込んで利用します。

cursorの位置

cursorの位置は交差地点の座標をそのまま指定すればよさそうなものですが、そうしてしまうと、カーソルの座標と対象オブジェクトの座標がかぶって表示がちらつくので、法線ベクトルの1/10ぶんだけ、法線ベクトル側の点を指定します。

// cursorの座標 = 交差地点の座標 + 法線ベクトル×0.1 (少しだけ交差地点から浮かせる)
var cursorPosition = new THREE.Vector3().addVectors(intersection.point, intersection.face.normal.multiplyScalar(0.1));
data.target.setAttribute("position", cursorPosition);

なお、A-Frameでは基本的に長さの単位はメートルなので、単位ベクトルの1/10は10cmを表します。つまり交差オブジェクトの表面から10cm浮いているということですね。

cursorの向き

次に、look-atコンポーネントを利用して向きを指定します。

付加されている要素からの相対的な向き(法線ベクトル)ではなく、グローバル空間の座標を指定する必要があるので、交差地点の座標に法線ベクトルを足し合わせ座標を計算します。

// look atの方向の絶対座標 = 交差地点の座標 + 法線ベクトル
var lookAtTarget = new THREE.Vector3().addVectors(intersection.point, intersection.face.normal);
data.target.setAttribute("look-at", lookAtTarget);

ここまでまとめるとこんな感じ!

f:id:jujunjun110:20161102100852j:plain

実物

こんな感じで、壁に沿って動くカーソルが実装できました。

↓ぐりぐり触ってみて下さい。

WASDキーで移動もできます。)

フルスクリーンでページを閲覧する

今回のソースコード(github)

イベントのお知らせ

さて、今回も使ったkframeの開発者であり、MozillaでバリバリA-Frameを開発しているKevin Ngo氏をアメリカからお招きして、A-Frameのイベントを開催します!

その名も TOKYO A-Framer Meetup

f:id:jujunjun110:20161101185724p:plain

  • 11月29日(火)
  • OPEN 19:00 START 19:30
  • 参加費無料(懇親会は1000円)
  • 株式会社ドリコム(目黒)

イベントページへ

LT登壇者も募集中です。僕も喋る予定です!

A-FrameやWeb VRをやっている方にとってはまたとない機会だと思いますので、みなさまふるってご参加下さい!

次回はDayBySay が、衝撃のあまり腰が抜けるほどのお役立ち情報を更新する予定ですのでお楽しみに!