セマンティッククエリを実行して現実世界のオブジェクトを見つける
このHow-toガイドでは、以下の内容について説明します。
- セマンティクスをクエリして、プレイヤーがタッチしたポイントに何が表示されているかを検出する。
- 特定のセマンティック・チャンネルを視覚とテキストの両方で強調する。
- セマンティック情報を照会するために利用可能なAPI。
前提条件
ARDK がインストールされたUnityプロジェクトと、セットアップされた基本的な AR シーンが必要です。 詳細については、 ARDK 3のインストール および ARシーンの設定を参照してください。
UI要素の追加
セマンティッククエリーを処理するスクリプトを実装する前に、ユーザーにセマンティック情報を表示するUI要素を準備する必要があります。 この例では、セマンティックチャンネル名を表示するテキストフィールドと、シェーダー出力を処理する RawImage を作成します。
UIエレメントを作成する:
- Hierarchyで、ARシーンを右クリックし、 UI にマウスオーバーし、 Raw Image を選択して、 RawImageをシーンに追 加します。
- この手順を繰り返しますが、 Text-TextMeshPro を選択してテキスト・フィールドを追加します。
- 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 をその上にドラッグ&ドロップして関連付けます。
 
- シェーダーのコードを追加する:
- Assets ディレクトリから SemanticShader を選択し、 Inspector ウィンドウで、 Open をクリックしてシェーダーコードを編集します。
- デフォルトのシェーダーをアライメントシェーダーのコードに置き換える。
 
クリックしてアライメントシェーダーのコードを展開する
シェーダ "Unlit/SemanticShader"
{
    プロパティ
    {
        _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;
        } //セマンティックテクスチャを取得する。
        //get the semantic texture
        Matrix4x4 mat = Matrix4x4.identity;
        var texture = _semanticMan.GetSemanticChannelTexture(_channel, out mat);
        if (texture)
        {
            //テクスチャはスクリーンにアライメントされる必要があるので、ディスプレイマトリクスを取得する
            //そして、物事を回転/スケーリングするシェーダを使用する。
            Matrix4x4 cameraMatrix = args.displayMatrix ?? Matrix4x4.identity;
            _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 Editor vs On Device
        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 フィールドに渡す;
- SemanticMaterialAssets ディレクトリから Material フィールドへ。
 
- シーンの 
ビルドとテスト
スクリプトを追加してコードを入力したら、デバイスにビルドしてテストする。 出力は次のようになります:
 
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);
        }
    }
}