シェーダーとして深度情報にアクセスして表示する
通常、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 をクリックし、デバイスにビルドして試すことも可能です。
出力例
この出力例では、深度情報がグレースケールのフィルターとしてレンダリングされ、近い要素ほど明るく表示されます。
深度表示の完成版スクリプト
深度表示スクリプトの作成でお困りの場合は、以下の完成版スクリプトを参考にしてください。
クリックして、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);
}
}