シェーダーとして深度情報にアクセスして表示する
通常、ARアプリケーションでカメラフレームを扱う際には、カメラに合わせた変換マトリックス(表示マトリックス)を使用して、アスペクト比がアプリケーションのビューポートに一致するようにする必要があります。 深度画像とAR背景画像のアスペクト比が一致している場合、AR Foundationでは表示マトリックスを AROcclusionManager.environmentDepth に適用し、適切な解像度の深度マップテクスチャを出力することができます。 Lightshipでは、 LightshipOcclusionExtension でこれらのプロパティを分離することで、 DepthTexture(テクスチャ自体) と DepthTransform(表示マトリックス) の両方を使用できます。 このように変換マトリックスを個別に保存することで、デバイスから深度情報が取得できなかったカメラフレームに対しても、補正が可能なワーピングデータを追加することができます。
この入門ガイドでは、以下の内容について説明します。
- フルスクリーン画像を表示するためのUIとシェーダーリソースを設定する。
- 深度テクスチャにアクセスする。
- テクスチャを画面に合わせる画像変換マトリックスを取得する。
- 深度テクスチャとその画像変換マトリックスをレンダリングリソースに適用する。
- メトリック深度を色スケールに数値変換する(深度情報の活用例)。
前提条件
ARDKがインストールされたUnityプロジェクトと、セットアップされた基本的なARシーンが必要です。 詳しくは、Lightship ARDKのセットアップと 基本的なARシーンのセットアップを参照してください。 また、Niantic はUnityエディターでテストできるように、 プレイバックを設定する ことも推奨しています。
ARオクルージョンマネージャーを追加する
ARFoundationでは、 AR Occlusion Manager のMonoBehaviourを使用することで、深度バッファにアクセスできます。 プロジェクトに AR Occlusion Manager を追加するには、次の手順を行います。
- メインカメラ の GameObjectにAROcclusionManagerを追加します。- Hierarchyで、 XROriginと Camera Offsetを展開し、 Main Camera オブジェクトを選択します。 次に、 Inspectorで、 Add Component をクリックし、AROcclusionManagerを追加します。
 
- Hierarchyで、 
 
Lightship Occlusion Extensionの追加
拡張機能を追加するには、以下の手順を行います。
- LightshipOcclusionExtensionをメインカメラ- GameObjectに追加します:- Hierarchy で、 XROriginを展開し、 Main Camera を選択します。 次に、 インスペクタ で、 コンポーネントの追加 をクリックし、Lightship Occlusion Extensionを追加します。
 
- Hierarchy で、 
AR Foundation Occlusion Managerを使用して深度テクスチャにアクセスする
AR Foundationから深度テクスチャを取得するには、次の手順を行います。
- Project ウィンドウで Assets フォルダを開き、その中で右クリックして Create メニューを開き、 C# Script を選択します。 Depth_HowToという名前を付けます。
- Depth_HowTo.csをファイルエディターで開き、以下のスニペットからコードを追加して保存します。
- Hierarchy で Main Camera を選択します。 Assets フォルダから Depth_HowTo.csを Inspector の下部にドラッグし、コンポーネントとして追加します。 **Occlusion Manager **の横の丸をクリックし、Main Cameraを選択します。
 
深度の入門ガイド コードを表示するにはクリックしてください。
    using UnityEngine;
    using UnityEngine.XR.ARFoundation;
    using Niantic.Lightship.AR.Utilities;
    public class Depth_HowTo : MonoBehaviour
    {
        public AROcclusionManager _occlusionManager;
        void Update()
        {
            if (!_occlusionManager.subsystem.running)
            {
                return;
            }
            // AR Foundationから深度テクスチャを取得
            // 背景画像とアスペクト比が一致している
            var depthTexture = _occlusionManager.environmentDepthTexture;
            // カメラの表示マトリックスのレイアウトはプラットフォームによって異なるため、保証されません
            // そのため、CameraMathライブラリを使用して独自に計算します
            var displayMatrix = CameraMath.CalculateDisplayMatrix
            (
                depthTexture.width,
                depthTexture.height,
                Screen.width,
                Screen.height,
                XRDisplayContext.GetScreenOrientation()
            );
            // テクスチャを使って何かの処理を行う
            // ...
        }
    }
Lightship Occlusion Extensionを使用して深度テクスチャにアクセスする
Lightshipから深度テクスチャを取得するには、次の手順を行います。
- Depth_HowTo.csを再度開き、コードを以下のスニペットに置き換えます。
- スクリプトの準備ができたら、 Hierarchy から Main Camera を選択し、 Inspector でそれをコンポーネントとして追加します。 Occlusion Extension フィールドの横にある丸をクリックし、 Main Camera を選択します。
 
深度の入門ガイド コードを表示するにはクリックしてください。
    using UnityEngine;
    using UnityEngine.XR.ARFoundation;
    using Niantic.Lightship.AR.Utilities;
    using Niantic.Lightship.AR.Occlusion;
    public class Depth_HowTo : MonoBehaviour
    {
        public LightshipOcclusionExtension _occlusionExtension;
        void Update()
        {   
            // 拡張機能から深度テクスチャと表示マトリックスを取得         
            var depthTexture = _occlusionExtension.DepthTexture;
            var displayMatrix = _occlusionExtension.DepthTransform;
            // テクスチャを使って何かの処理を行う
            // ...
        }
    }
Raw Imageを追加して深度バッファを表示する
リアルタイムの深度データにアクセスできるようになったので、それをカスタムUI要素に反映して画面上に表示します。 この例では、Raw Imageを作成し、深度バッファを変換してカメラ出力の上にオーバーレイ表示するマテリアルをアタッチします。
リアルタイムの深度表示を設定するには、次の手順を行います。
- Hierarchy で右クリックし、 UI メニューを開いて Raw Image を選択します。 新しいRaw Imageに DepthImageという名前を付けます。
- 画像変換ツールを使って Raw Image を画面中央に配置し、後で見やすいように画面全体を覆うように広げます(例は下の画像を参照)。
- すべてのパラメーター(左、上、Z、右、下)を0に設定します。
 
 
マテリアルとシェーダーを追加する
マテリアルとシェーダーを作成するには、次のように行います。
- Project ウィンドウで Assets フォルダを開き、その中で右  クリックします。 Create メニューを開き、 Material を選択します。 新しいマテリアルに DepthMaterialという名前を付けます。
- この作業を繰り返しますが、Shaderメニュー開き、Unlit Shaderを選択します。 新しいシェーダーに DisplayDepthという名前を付けます。
- シェーダーをマテリアルにドラッグして、それらを接続します。
- シェーダーをダブルクリックして開き、次のセクションの DisplayDepthシェーダーコードに貼り付けます。
DisplayDepthシェーダーのコード
深度表示は、vert/fragセクションを使用する標準的なフルスクリーンシェーダーです。 このシェーダーのvertセクションでは、ディスプレイ変換行列を使ってUV座標に乗算し、テクスチャをサンプリングします。 この操作により、深度を画面に合わせるための変換が行われます。
クリックしてDepthDisplayシェーダーを表示
Shader "Unlit/DisplayDepth"
{
    Properties
    {
        _DepthTex ("_DepthTex", 2D) = "green" {}
    }
    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
            {
                float3 texcoord : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };
            // 深度テクスチャのサンプラー
            sampler2D _DepthTex;
            // スクリーン空間から深度テクスチャ空間への変換
            float4x4 _DepthTransform;
            inline float ConvertDistanceToDepth(float d)
            {
                // 近クリップ平面よりも小さい距離をクリップし、その距離から深度値を計算します
                return (d < _ProjectionParams.y) ? 0.0f : ((1.0f / _ZBufferParams.z) * ((1.0f / d) - _ZBufferParams.w));
            }
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                // 画像変換をUV座標に適用
                o.texcoord = mul(_DepthTransform, float4(v.uv, 1.0f, 1.0f)).xyz;
                return o;
            }
            fixed4 frag (v2f i) : SV_Target
            {
                // 深度画像変換にはリプロジェクション(ワーピング)が含まれている可能性があるため、
                // 同次座標から直交座標にUV座標を変換する必要があります
                float2 depth_uv = float2(i.texcoord.x / i.texcoord.z, i.texcoord.y / i.texcoord.z);
                // 深度値は赤チャンネルをサンプリングして取得
                // テクスチャ内の値は、カメラからの距離を示すメトリックの視線深度(カメラからの距離)
                float eyeDepth = tex2D(_DepthTex, depth_uv).r;
                // 視線深度をZバッファ値に変換
                // Zバッファ値は[0, 1]の範囲の非線形値
                float depth = ConvertDistanceToDepth(eyeDepth);
                // Z値を色として使用
#ifdef UNITY_REVERSED_Z
              return fixed4(depth, depth, depth, 1.0f);
#else
              return fixed4(1.0f - depth, 1.0f - depth, 1.0f - depth, 1.0f);
#endif
            }
            ENDCG
        }
    }
}
深度情報をRaw Imageに渡す
深度情報をRaw Imageに渡すには、 Depth_HowTo.cs を次のように修正します。
- スクリプトの usingステートメントを更新し、RawImageとMaterialを参照するための変数を追加します。
using UnityEngine.UI;
public class Depth_HowTo : MonoBehaviour
{
    public RawImage _rawImage;
    public Material _material;
    // スクリプトの続きは以下
- その Materialに対して、深度テクスチャと表示変換行列を設定します。
- 深度情報と変換情報をシェーダーに渡すために、 SetTextureとSetMatrixをスクリプトに追加します。
- Main Camera を選択し、 Inspector で、先ほど作成したRaw ImageとMaterialを設定します。
 
クリックして、修正後のDepth_HowTo Update関数を表示
    void Update()
    {
        if (!_occlusionManager.subsystem.running)
        {
            return;
        } //マテリアルを生画像に追加します。
        //raw imageにマテリアルを追加する
        _rawImage.material = _material;
        //深度テクスチャと表示マトリクスを設定する
        var depthTexture = _occlusionExtension.DepthTexture;
        var displayMatrix = _occlusionExtension.DepthTransform; //シェーディング用の変数を設定する。DepthTransform;
        //シェーダー用の変数を設定
        //注意:深度テクスチャの更新はUpdate()関数内で行う必要があります
        _rawImage.material.SetTexture("_DepthTex", depthTexture);
        _rawImage.material.SetMatrix("_DepthTransform", displayMatrix);
}.
設定をテストし、デバイスにビルドする
これで、Unityエディターでプレイバックを使用してテストできるようになります。 また、 Build Settings を開いて Build and Run をクリックし、デバイスにビルドして試すことも可能です。
出力例
この出力例では、深度情報がグレースケールのフィルターとしてレンダリングされ、近い要素ほど明るく表示されます。
