本文へスキップ
バージョン: 3.0

セマンティッククエリを実行して現実世界のオブジェクトを見つける

このHow-toガイドでは、以下の内容について説明します。

  • セマンティクスをクエリして、プレイヤーがタッチしたポイントに何が表示されているかを検出する。
  • 特定のセマンティック・チャンネルを視覚とテキストの両方で強調する。
  • セマンティック情報を照会するために利用可能なAPI。

前提条件

ARDK がインストールされたUnityプロジェクトと、セットアップされた基本的な AR シーンが必要です。 詳細については、 ARDK 3のインストール および ARシーンの設定を参照してください。

UI要素の追加

セマンティッククエリーを処理するスクリプトを実装する前に、ユーザーにセマンティック情報を表示するUI要素を準備する必要があります。 この例では、セマンティックチャンネル名を表示するテキストフィールドと、シェーダー出力を処理する RawImage を作成します。

UIエレメントを作成する:

  1. Hierarchyで、AR シーンを右クリックし、 UI にマウスオーバーして、 Text-TextMeshPro を選択して、テキストフィールドを追加します。
  2. このプロセスを繰り返しますが、 Text-TextMeshPro の代わりに Raw Image を選択し、 RawImage をシーンに追加します。

Semantic Segmentation Managerの追加

Semantic Segmentation Managerは、セマンティック・サブシステムへのアクセスを提供し、コードの他の部分がアクセスできるセマンティック予測を提供します。 (詳細については、 Semantics機能ページを参照)

シーンにSemantic Segmentation Managerを追加する:

  1. Hierarchy ウィンドウで右クリックし、 Create Empty を選択して、空の GameObject をシーンに追加します。 名前は Segmentation Manager
  2. 新しい GameObjectを選択し、 Inspector ウィンドウで、 Add Componentをクリックし、"AR Semantic Segmentation Manager" を検索して選択し、コンポーネントとして追加します。

Alignment Shaderの追加

意味情報が画面上で正しく配置されるようにするには、ディスプレイ・マトリクスを使い、カメラから返されたバッファを回転させる必要がある。 オーバーレイシェーダーを使って、 ARCameraManager frame update イベントからディスプレイのトランスフォームを取得し、それを使ってUVを正しいスクリーン空間にトランスフォームすることができます。 そして、セマンティック情報を先ほど作成した RawImage にレンダリングし、ユーザーに表示する。

シェーダーを作成する:

  1. シェーダーとマテリアルを作成します:
    1. Project ウィンドウで、 Assets ディレクトリを開きます。
    2. Assets ディレクトリで右クリックし、 Create にマウスオーバーし、 Shader メニューから Unlit Shader を選択します。 SemanticShaderと名前をつけます。
    3. この作業を繰り返しますが、 Create メニューから Material を選択し、新しいマテリアルを作成します。 SemanticMaterialと名付け、 SemanticShader をその上にドラッグ&ドロップして関連付けます。
  2. シェーダーのコードを追加する:
    1. Assets ディレクトリから SemanticShader を選択し、 Inspector ウィンドウで、 Open をクリックしてシェーダーコードを編集します。
    2. デフォルトのシェーダーをアライメントシェーダーのコードに置き換える。
クリックしてアライメントシェーダーのコードを展開する
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;
float2 texcoord : TEXCOORD1;
float4 vertex : SV_POSITION;

};

float4x4 _DisplayMat;

v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;

//画像を適切な回転とアスペクトに調整する必要があります。
o.texcoord = mul(float3(v.uv, 1.0f), _DisplayMat).xy;
return o;
}

sampler2D _MainTex;
sampler2D _SemanticTex;
fixed4 _Color;

fixed4 frag (v2f i) : SV_Target
{
float4 semanticCol = tex2D(_SemanticTex, i.texcoord);
return float4(_Color.r,_Color.g,_Color.b,semanticCol.r*_Color.a);
}.
ENDCG
}.
}
}
  1. 材料のプロパティを設定します:
    1. シェーダーコードを置き換えると、マテリアルにプロパティが入力されます。 これらにアクセスするには、 Assets ディレクトリから SemanticMaterial を選択し、 Inspector ウィンドウを見てください。
    2. マテリアルのカラーとアルファを設定するには、 InspectorColor プロパティの右にあるカラースウォッチをクリックします。 アルファ値を 128 (およそ50%)に設定し、セマンティックカラーフィルターがその下の現実世界のオブジェクトを見るのに十分な半透明になるようにします。 好きな色を選んでください。

Query Scriptの作成

プレーヤーがスクリーンにタッチしたときにセマンティック情報を得るには、セマンティックセグメンテーションマネージャーに問い合わせ、プレーヤーがエリアにタッチしたときに情報を表示するスクリプトが必要です。

Query Scriptを作成する:

  1. スクリプトファイルを作成し、Segmentation Managerに追加する:
    1. Project ウィンドウで、 Assets ディレクトリを選択し、ウィンドウ内で右クリックし、 Createにマウスオーバーし、 C# Scriptを選択する。 新しいスクリプトを SemanticQueryingと名前をつけます。
    2. Hierarchyで、 Segmentation Manager GameObjectを選択し、 Inspector ウィンドウで、 Add Componentをクリックします。 script」を検索し、 New Script を選択し、 SemanticQuerying スクリプトを選択します。
  2. スクリプトにコードを追加する:
    1. 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";
private float _timer = 0.0f;

void OnEnable()
{
_cameraMan.frameReceived += OnCameraFrameUpdate;
}

private void OnDisable()
{
_cameraMan.frameReceived -= OnCameraFrameUpdate;
}

private void OnCameraFrameUpdate(ARCameraFrameEventArgs args)
{
if (!_semanticMan.subsystem.running)
{
return;
}

//get the semantic texture
Matrix4x4 mat = Matrix4x4.identity;
var texture = _semanticMan.GetSemanticChannelTexture(_channel, out mat);

//the texture needs to be aligned to the screen so get the display matrix
//and use a shader that will rotate/scale things.
Matrix4x4 cameraMatrix = args.displayMatrix ?? Matrix4x4.identity;
_image.material = _material;
_image.material.SetTexture("_SemanticTex", texture);
_image.material.SetMatrix("_DisplayMat", cameraMatrix);
}

void Update()
{
if (!_semanticMan.subsystem.running)
{
return;
}.

Vector3 pos = default;
if (Application.isEditor)
{
if (Input.GetMouseButtonDown(0))
{
pos = Input.mousePosition;
}.
}
else
{
if (Input.touches.Length > 0)
{
pos = Input.touches[0].position;
}.
}

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 = "Unknown semantic channel!";
}.
_timer = 0.0f;
}.
}
}
}
}

Script Variablesの割り当て

スクリプトを実行する前に、スクリプトとUIエレメントが互いに会話できるように、Unityで変数を割り当てる必要があります。

変数を割り当てる:

  1. Hierarchyで、 Segmentation Manager GameObjectを選択します。
  2. Inspector ウィンドウで、 SemanticQuerying スクリプトコンポーネント内の変数を、各項目をそれぞれのフィールドにドラッグアンドドロップして割り当てます:
    1. シーンの MainCamera から Camera Man フィールドへ;
    2. SegmentationManager オブジェクトを Segmentation Man フィールドに渡す;
    3. Text-TMP オブジェクトから Text フィールドへ;
    4. RawImage オブジェクトを Image フィールドに渡す;
    5. SemanticMaterial Assets ディレクトリから Material フィールドへ。

ビルドとテスト

スクリプトを追加してコードを入力したら、デバイスにビルドしてテストする。 出力は次のようになります:

ARアプリでセマンティック情報を見る

Appendix: クエリースクリプトの仕組み(How Does the Query Script Work?)

スクリーンには何が映っていますか?

クエリースクリプトのコアは、毎フレーム、画面の中心をチェックする。 (Screen.width/2, Screen.height/2)ARSemanticSegmentationManager.GetChannelNamesAt 、クエリーターゲットを中心点として定義し、出力をUIテキスト要素に渡します。

チャンネル名のスニペットを表示するにはここをクリック
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;
}

var list = _semanticMan.GetChannelNamesAt(Screen.width / 2, Screen.height / 2);
_text.text="";
foreach (var i in list)
_text.text += i;
}
}

セマンティック・クラスのハイライト

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;

//this will highlight the class it found
if (list.Count > 0)
{
//just show the first one.
_image.texture = _semanticMan.GetSemanticChannelTexture(list[0], out mat);
}
}
}