セマンティッククエリを実行してセマンティック・チャンネルをハイライトする
この入門ガイドでは、以下の内容について説明します。
- セマンティックをクエリして、プレイヤーがタッチしたポイントに何が表示されているかを検出する。
- プレイヤーが最後にタップしたポイントに基づき、特定のセマンティックチャンネルをハイライトする。
- セマンティック情報を照会するために利用可能なAPI。
前提条件
ARDKがインストールされたUnityプロジェクトと、セットアップされた基本的なARシーンが必要です。 詳細については、 ARDK 3のインストール および ARシーンの設定 をご覧ください。
手順
UI要素の追加
セマンティッククエリーを処理するスクリプトを実装する前に、ユーザーにセマンティック情報を表示するUI要素を準備する必要があります。 この例では、セマンティックチャンネル名を表示するテキストフィールドと、シェーダー出力を処理する RawImage
を作成します。
UIエレメントを作成する:
- Hierarchyで、ARシーンを右クリックし、 UI にマウスオーバーし、 Raw Image を選択して、
RawImage
をシーンに追加します。 - この手順を繰り返しますが、 Text-TextMeshPro を選択してテキスト・フィールドを追加します。 TMP Importerポップアップが表示されたら、Import TMP Essentialsをクリックして、テキス トフィールドの追加を完了します。
- Hierarchyでテキストフィールドを選択し、 Inspectorで、その位置を (0, 0, 0) に設定します。
- Inspectorの Rect Transform メニューで、左上隅の四角をクリックして、 Anchor Presets メニューを開きます。 Shiftを押しながらcenterオプションをクリックすると、テキストが画面の中央に固定されます。
RawImage
を選択し、 Anchor Presets メニューを再度開きます。 Optionキー(WindowsではAltキー)を押しながら右下の正方形を選択し、RawImage
、正しい位置に配置し、画面全体を覆うように引き伸ばす。
Semantic Segmentation Managerの追加
Semantic Segmentation Managerは、セマンティック・サブシステムへのアクセスを提供し、コードの他の部分がアクセスできるセマンティック予測を提供します。 (詳細については、 Semantics機能ページを参照)
シーンにSemantic Segmentation Managerを追加する:
- Hierarchy ウィンドウで右クリックし、 Create Empty を選択して、空の
GameObject
をシーンに追加します。 Segmentation Manager という名前を付けます。 - 新しい
GameObject
を選択し、 Inspector ウィンドウで、 Add Componentをクリックし、"AR Semantic Segmentation Manager" を検索して選択し、コンポーネントとして追加します。
Alignment Shaderの追加
意味情報が画面上で正しく配置されるようにするには、ディスプレイ・マトリクスを使い、カメラから返されたバッファを回転させる必要がある。 オーバーレイシェーダーを使って、 ARCameraManager frame update イベントからディスプレイのトランスフォームを取得し、それを使ってUVを正しいスクリーン空間にトランスフォームすることができます。 そして、セマンティック情報を先ほど作成した RawImage
にレンダリングし、ユーザーに表示する。
シェーダーを作成する:
- シェーダーとマテリアルを作成します:
- Project ウィンドウで、 Assets ディレクトリを開きます。
- Assets ディレクトリで右クリックし、 Create にマウスオーバーし、 Shader メニューから Unlit Shader を選択します。 SemanticShader という名前を付けます。
- この作業を繰り返しますが、 Create メニューから Material を選択し、新しいマテリアルを作成します。 SemanticMaterialと名付け、SemanticShaderを新しいMaterialにドラッグ&ドロップして関連付けます。
- シェーダーのコードを追加する:
- Assets ディレクトリから SemanticShader を選択し、 Inspector ウィンドウで、 Open をクリックしてシェーダーコードを編集します。
- デフォルトのシェーダーをアライメントシェーダーのコードに置き換えます。
クリックしてアライメントシェーダーのコードを展開する
Shader "Unlit/SemanticShader"
{
Properties
{
_MainTex ("_MainTex", 2D) = "white" {}
_SemanticTex ("_SemanticTex", 2D) = "red" {}
_Color ("_Color", Color) = (1,1,1,1)
}
SubShader
{
Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
Blend SrcAlpha OneMinusSrcAlpha
// カリングまたは深度はなし
Cull Off ZWrite Off ZTest Always
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float3 texcoord : TEXCOORD1;
float4 vertex : SV_POSITION;
};
float4x4 _SemanticMat;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
//画像を適切な回転とアスペクトに調整する必要があります。
o.texcoord = mul(_SemanticMat, float4(v.uv, 1.0f, 1.0f)).xyz;
return o;
}
sampler2D _MainTex;
sampler2D _SemanticTex;
fixed4 _Color;
fixed4 frag (v2f i) : SV_Target
{
//座標空間の変換
float2 semanticUV = float2(i.texcoord.x / i.texcoord.z, i.texcoord.y / i.texcoord.z);
float4 semanticCol = tex2D(_SemanticTex, semanticUV);
return float4(_Color.r,_Color.g,_Color.b,semanticCol.r*_Color.a);
}.
ENDCG
}.
}
}
- 材料のプロパティを設定します:
- シェーダーコードを置き換えると、マテリアルにプロパティが入力されます。 これらにアクセスするには、 Assets ディレクトリから SemanticMaterial を選択し、 Inspector ウィンドウを見てください。
- マテリアルのカラーとアルファを設定するには、 Inspector の Color プロパティの右にあるカラースウォッチをクリックします。 アルファ値を
128
(およそ50%)に設定し、セマンティックカラーフィルターがその下の現実世界のオブジェクトを見るのに十分な半透明になるようにします。 好きな色を選んでください。
Query Scriptの作成
プレーヤーがスクリーンにタッチしたときにセマンティック情報を得るには、セマンティック セグメンテーションマネージャーに問い合わせ、プレーヤーがエリアにタッチしたときに情報を表示するスクリプトが必要です。
Query Scriptを作成する:
- スクリプトファイルを作成し、Segmentation Managerに追加する:
- Project ウィンドウで、 Assets ディレクトリを選択し、ウィンドウ内で右クリックし、 Createにマウスオーバーし、 C# Scriptを選択する。 新しいスクリプトに SemanticQuerying という名前をつけます。
- Hierarchyで、 Segmentation Manager
GameObject
を選択し、 Inspector ウィンドウで、 Add Componentをクリックします。 script」を検索し、 New Script を選択し、 SemanticQuerying スクリプトを選択します。
- スクリプトにコードを追加する:
- Assets ディレクトリの SemanticQuerying スクリプトをダブルクリックしてテキストエディタで開き、次のスクリプトをコピーします。 (スクリプトの各部分の動作の詳細については、 Appendix を参照)。
クリックするとSemanticQueryingスクリプトが表示されます。
using Niantic.Lightship.AR.Semantics;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.XR.ARFoundation;
public class SemanticQuerying : MonoBehaviour
{
public ARCameraManager _cameraMan;
public ARSemanticSegmentationManager _semanticMan;
public TMP_Text _text;
public RawImage _image;
public Material _material;
private string _channel = "ground";
void OnEnable()
{
_cameraMan.frameReceived += OnCameraFrameUpdate;
}
private void OnDisable()
{
_cameraMan.frameReceived -= OnCameraFrameUpdate;
}
private void OnCameraFrameUpdate(ARCameraFrameEventArgs args)
{
if (!_semanticMan.subsystem.running)
{
return;
}
//セマンティックテクスチャを取得
Matrix4x4 mat = Matrix4x4.identity;
var texture = _semanticMan.GetSemanticChannelTexture(_channel, out mat);
if (texture)
{
//テクスチャを画面に合わせるために表示行列を取得
//シェーダーを使用して、テクスチャの回転やスケールを行う
_image.material = _material;
_image.material.SetTexture("_SemanticTex", texture);
_image.material.SetMatrix("_SemanticMat", mat);
}
}
private float _timer = 0.0f;
void Update()
{
if (!_semanticMan.subsystem.running)
{
return;
}
//Unityエディターとデバイス上での動作の違い
if (Input.GetMouseButtonDown(0) || (Input.touches.Length > 0))
{
var pos = Input.mousePosition;
if (pos.x > 0 && pos.x < Screen.width)
{
if (pos.y > 0 && pos.y < Screen.height)
{
_timer += Time.deltaTime;
if (_timer > 0.05f)
{
var list = _semanticMan.GetChannelNamesAt((int)pos.x, (int)pos.y);
if (list.Count > 0)
{
_channel = list[0];
_text.text = _channel;
}
else
{
_text.text = "?";
}
_timer = 0.0f;
}
}
}
}
}
}
Script Variablesの割り当て
スクリプトを実行する前に、スクリプトとUIエレメントが互いに会話できるように、Unityで変数を割り当てる必要があります。
変数を割り当てる:
- Hierarchyで、 Segmentation Manager
GameObject
を選択します。 - Inspector ウィンドウで、
SemanticQuerying
スクリプトコンポーネント内の変数を、各項目をそれぞれのフィールドにドラッグアンドドロップして割り当てます:- シーンの
MainCamera
から Camera Man フィールドへ; SegmentationManager
オブジェクトを Segmentation Man フィールドに渡す;Text-TMP
オブジェクトから Text フィールドへ;RawImage
オブジェクトを Image フィールドに渡す;SemanticMaterial
Assets ディレクトリから Material フィールドへ。
- シーンの
ビルドとテスト
スクリプトを追加してコードを設定したら、プレイバックのデータセットを使ってテストしてみましょう。 録 画したデータセットをUnityエディタで再生するための設定方法については、再生設定方法を参照してください。 Lightship GithubのPlaybackデータセットを使う場合、出力は次のようになるはずだ:
また、デバイスにビルドし、実際の環境でテストできるようになりました。
Appendix: クエリースクリプトの仕組み(How Does the Query Script Work?)
スクリーンには何が映っていますか?
クエリスクリプトで重要なのは、ユーザーがタップ、またはクリックしているかどうかをフレームごとにチェックすることです。 タップやクリックの位置とフレーム数を確認し、適切であることを確認した後、ユーザーが選択した地点でセマンティックチャンネルを取得し、結果をテキストボックスに表示します。
チャンネル名のスニペットを表示するにはここをクリック
using Niantic.Lightship.AR.ARFoundation;
using TMPro;
using UnityEngine;
public class SemanticQuerying : MonoBehaviour
{
public ARSemanticSegmentationManager _semanticMan;
public TMP_Text _text;
void Update()
{
if (!_semanticMan.subsystem.running)
{
return;
}
//Unityエディターと実際のデバイスでの入力処理
if (Input.GetMouseButtonDown(0) || (Input.touches.Length > 0))
{
var pos = Input.mousePosition;
if (pos.x > 0 && pos.x < Screen.width)
{
if (pos.y > 0 && pos.y < Screen.height)
{
_timer += Time.deltaTime;
if (_timer > 0.05f)
{
var list = _semanticMan.GetChannelNamesAt((int)pos.x, (int)pos.y);
if (list.Count > 0)
{
_channel = list[0];
_text.text = _channel;
}
else
{
_text.text = "?";
}
_timer = 0.0f;
}
}
}
}
}
}
セマンティック・クラスのハイライト
GetSemanticChannelTexture
を使って、見ているセマンティックチャンネルのテクスチャを取得し、結果を RawImage
UI要素に出力することができます。 この関数は Matrix4x4
テクスチャを出力するので、 アライメント・シェーダの追加で説明したように、シェーダで変換します。
クリックするとテクスチャー出力のスニペットが表示されます
using Niantic.Lightship.AR.ARFoundation;
using TMPro;
using UnityEngine;
public class SemanticQuerying : MonoBehaviour
{
public ARSemanticSegmentationManager _semanticMan;
public TMP_Text _text;
public RawImage _image;
void Update()
{
if (!_semanticMan.subsystem.running)
{
return;
}
var list = _semanticMan.GetChannelNamesAt(Screen.width / 2, Screen.height / 2);
_text.text="";
foreach (var i in list)
_text.text += i;
//これは見つけたクラスをハイライトする
if (list.Count > 0)
{
//最初の1つのみを表示する。
_image.texture = _semanticMan.GetSemanticChannelTexture(list[0], out mat);
}
}
}