本文へスキップ

シェーダーとして深度情報にアクセスして表示する

通常、ARアプリケーションでカメラフレームを扱う際には、カメラに合わせた変換マトリックス(表示マトリックス)を使用して、アスペクト比がアプリケーションのビューポートに一致するようにする必要があります。 深度画像とAR背景画像のアスペクト比が一致している場合、AR Foundationでは表示マトリックスを AROcclusionManager.environmentDepth に適用し、適切な解像度の深度マップテクスチャを出力することができます。 Lightshipでは、 LightshipOcclusionExtension でこれらのプロパティを分離することで、 DepthTexture(テクスチャ自体)DepthTransform(表示マトリックス) の両方を使用できます。 このように変換マトリックスを個別に保存することで、デバイスから深度情報が取得できなかったカメラフレームに対しても、補正が可能なワーピングデータを追加することができます。

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

  1. フルスクリーン画像を表示するためのUIとシェーダーリソースを設定する。
  2. 深度テクスチャにアクセスする。
  3. テクスチャを画面に合わせる画像変換マトリックスを取得する。
  4. 深度テクスチャとその画像変換マトリックスをレンダリングリソースに適用する。
  5. メトリック深度を色スケールに数値変換する(深度情報の活用例)。

前提条件

ARDKがインストールされたUnityプロジェクトと、セットアップされた基本的なARシーンが必要です。 詳しくは、Lightship ARDKのセットアップと 基本的なARシーンのセットアップを参照してください。 また、Niantic はUnityエディターでテストできるように、 プレイバックを設定する ことも推奨しています。

ARオクルージョンマネージャーを追加する

ARFoundationでは、 AR Occlusion Manager のMonoBehaviourを使用することで、深度バッファにアクセスできます。 プロジェクトに AR Occlusion Manager を追加するには、次の手順を行います。

  1. メインカメラGameObjectAROcclusionManager を追加します。
    1. Hierarchyで、 XROriginCamera Offsetを展開し、 Main Camera オブジェクトを選択します。 次に、 Inspectorで、 Add Component をクリックし、 AROcclusionManagerを追加します。
AROcclusionManager

Lightship Occlusion Extensionの追加

拡張機能を追加するには、以下の手順を行います。

  1. LightshipOcclusionExtension をメインカメラ GameObject に追加します:
    1. Hierarchy で、 XROrigin を展開し、 Main Camera を選択します。 次に、 インスペクタ で、 コンポーネントの追加 をクリックし、 Lightship Occlusion Extension を追加します。

AR Foundation Occlusion Managerを使用して深度テクスチャにアクセスする

AR Foundationから深度テクスチャを取得するには、次の手順を行います。

  1. Project ウィンドウで Assets フォルダを開き、その中で右クリックして Create メニューを開き、 C# Script を選択します。 Depth_HowTo という名前を付けます。
  2. Depth_HowTo.cs をファイルエディターで開き、以下のスニペットからコードを追加して保存します。
  3. HierarchyMain Camera を選択します。 Assets フォルダから Depth_HowTo.csInspector の下部にドラッグし、コンポーネントとして追加します。 **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から深度テクスチャを取得するには、次の手順を行います。

  1. Depth_HowTo.cs を再度開き、コードを以下のスニペットに置き換えます。
  2. スクリプトの準備ができたら、 Hierarchy から Main Camera を選択し、 Inspector でそれをコンポーネントとして追加します。 Occlusion Extension フィールドの横にある丸をクリックし、 Main Camera を選択します。
Lightship Occlusion Extensionの設定
深度の入門ガイド コードを表示するにはクリックしてください。

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を作成し、深度バッファを変換してカメラ出力の上にオーバーレイ表示するマテリアルをアタッチします。

リアルタイムの深度表示を設定するには、次の手順を行います。

  1. Hierarchy で右クリックし、 UI メニューを開いて Raw Image を選択します。 新しいRaw Imageに DepthImage という名前を付けます。
  2. 画像変換ツールを使って Raw Image を画面中央に配置し、後で見やすいように画面全体を覆うように広げます(例は下の画像を参照)。
    1. すべてのパラメーター(左、上、Z、右、下)を0に設定します。
ストレッチ変換

マテリアルとシェーダーを追加する

マテリアルとシェーダーを作成するには、次のように行います。

  1. Project ウィンドウで Assets フォルダを開き、その中で右クリックします。 Create メニューを開き、 Material を選択します。 新しいマテリアルに DepthMaterial という名前を付けます。
  2. この作業を繰り返しますが、Shaderメニュー開き、Unlit Shaderを選択します。 新しいシェーダーに DisplayDepth という名前を付けます。
  3. シェーダーをマテリアルにドラッグして、それらを接続します。
  4. シェーダーをダブルクリックして開き、次のセクションの 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 を次のように修正します。

  1. スクリプトの using ステートメントを更新し、 RawImageMaterial を参照するための変数を追加します。
using UnityEngine.UI;

public class Depth_HowTo : MonoBehaviour
{
public RawImage _rawImage;
public Material _material;

// スクリプトの続きは以下
  1. その Material に対して、深度テクスチャと表示変換行列を設定します。
  2. 深度情報と変換情報をシェーダーに渡すために、 SetTextureSetMatrix をスクリプトに追加します。
  3. Main Camera を選択し、 Inspector で、先ほど作成したRaw ImageとMaterialを設定します。
Depth_HowToスクリプトコンポーネントと、割り当てられた変数
クリックして、修正後の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 をクリックし、デバイスにビルドして試すことも可能です。

出力例

この出力例では、深度情報がグレースケールのフィルターとしてレンダリングされ、近い要素ほど明るく表示されます。

グレースケールでレンダリングされた深度シェーダーの出力例

深度表示の完成版スクリプト

深度表示スクリプトの作成でお困りの場合は、以下の完成版スクリプトを参考にしてください。

クリックして、Depth_HowTo.csの完成版スクリプトを表示
using Niantic.Lightship.AR.Occlusion;
using UnityEngine;
using UnityEngine.XR.ARFoundation;
using UnityEngine.UI;

public class Depth_HowTo: MonoBehaviour
{
public AROcclusionManager _occlusionManager;
public RawImage _rawImage;
public Material _material;
public LightshipOcclusionExtension _occlusionExtension;

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

//マテリアルをRaw Imageに設定
_rawImage.material = _material;

// 深度テクスチャと表示マトリックスを取得
var depthTexture = _occlusionExtension.DepthTexture;
var displayMatrix = _occlusionExtension.DepthTransform;

//シェーダー用の変数を設定
//注意: 深度テクスチャの更新はUpdate()関数内で行う必要があります
_rawImage.material.SetTexture("_DepthTex", depthTexture);
_rawImage.material.SetMatrix("_DepthTransform", displayMatrix);
}
}