OculusQuest2 3D空間上にあるスクリーンのレイキャスト判定(Unity)

WRITER: k.okawa
2022/01/11

今回の内容


Unityクライアントエンジニアの大川です。

前回の記事に引き続き今回の記事では3D空間に配置しているスクリーン内の世界に対してのレイキャスト判定の仕方を書いていきます。

サンプル動画


サンプルプロジェクト


今回作成したプロジェクトはこちらです。

https://github.com/k-okawa/QuestSandBox/tree/master/Assets/Sample/HitTest

 

実装手順


前回の記事との変更点


前回の記事ではUICanvasをWorld空間に配置してRawImageで別世界のスクリーンを映し出すようにしていましたが、座標計算が複雑になるため3DObjectのQuadで映すように変更しました。

手順も一応載せておきます。

 

構成

Screen空オブジェクトの中にQuadのLeftScreenとRightScreenが入っています。

 

スクリーンの解像度が1920*1080のためXのスケールを1.777778にしています。

こちらのスケールは固定して親オブジェクトのScreenで位置とスケールを調整します。

 

マテリアル

左目・右目用どちらも同じ設定のマテリアルを用意していて、

テクスチャのみ設定を変えています。

 

スクリーン座標の求め方


最初にワールド空間上の点の座標を求める必要があります。

計算を簡単にするため、スクリーンはXY平面上にあって、Z軸は回転しないことを前提にしています。

必要な点はスクリーンの角P0~P3,レイが当たっている点Pになります。

 

P0~P3の求め方

QuadMeshのローカル頂点座標をまず知る必要があります。

MeshFilterComponentのmesh.verticesから取得できます。

配列格納順はP0~P3の順番と一致しています。

 

次にローカル座標からワールド座標に変換する必要があります。

実際行列の計算が必要ですがUnityにメソッドが用意されているのでこちらを使います。

こちらのふたつを合わせてLinqでまとめて配列を作成します。

_meshVertices[0] = P0

_meshVertices[1] = P1

_meshVertices[2] = P2

_meshVertices[3] = P3

 

P0~P3のワールド座標が求まりました!

 

Pの求め方

P0~P3の値は固定で変わりませんが、Pはレイが飛んでいる方向によって変わります。

平面と線分の交点を求めることで導き出せますが、Unityではすでにこちらの計算が用意されているので利用して求めます。

計算の詳細はこちらのサイトが分かりやすいです。

http://marupeke296.com/COL_main.html

 

LeftScreenまたはRightScreenにアタッチされているMeshColliderの関数を利用することで求められます。

 

Pが求まりました!

 

ワールド座標Pをスクリーン座標に変換する

Pはワールド座標なのでスクリーン上の座標に変換する必要があります。

スクリーンの角のスクリーン座標をSP0~SP3,Pのスクリーン座標をSP(x,y)とすると以下のようになります。

 

ここまでわかれば後はワールド座標の比率からスクリーン座標を求めることができます。

(XY平面上・Z軸回転なしの前提がなければz座標も考慮してしっかりベクトルの計算で求める必要があります)

 

プログラムの計算だと以下のようになります。

 

スクリーン空間のオブジェクトとレイキャスト判定


スクリーン座標からレイに変換

スクリーン座標が分かれば、カメラからのレイを求めることができます。

 

余談ですがこちらの計算がどうなっているのか気になり調べてみたところ、

Cocos2dxのソースコードに近い計算が書いてあったのでこちらを読み解けば分かりそうです。

https://github.com/cocos2d/cocos2d-x/blob/v4/cocos/2d/CCCamera.cpp#L314

Cocos2dxでのScreenPointをRayに変換する方法が書かれたフォーラム

https://discuss.cocos2d-x.org/t/raycast-from-touch-location-to-3d-space/34775

 

スクリーンを映しているカメラからレイを飛ばして交点を求める

最後に別空間のカメラからレイを飛ばして最初に交わるGameObjectとその点が求まれば今回やりたいことは達成です。

 

サンプルコード


最後に


今回は簡易的な計算で実装してみました。

また、サンプルでは右スクリーン・右カメラでレイキャスト判定を行っていますが、効き目が左の場合は左スクリーン・左カメラで判定した方が良いです。

スクリーン内のオブジェクトにレイが当たったことが重要で当たった位置が重要な情報でない場合は左右で2回判定しても良いかもしれません。

3Dスクリーンを使えば効き目を調べることにも使えそうですね。

次回はVRの話ではなくなりますが、別空間のカメラを移動させる方法について書いていきたいと考えています。