シェーダーとして深度情報にアクセスして表示する
通常、ARアプリケーションでカメラフレームを扱う場合、ディスプレイマトリックスと呼ばれるカメラに合わせた変換マトリックスを使用して、アスペクト比がアプリケーションのビューポートと一致するようにする必要があります。 深度画像とAR背景画像のアスペクト比が一致している場合、AR FoundationはディスプレイマトリックスをAROcclusionManager.environmentDepthに
適用し、適切な解像度で深度マップテクスチャを出力することができます。 Lightshipでは、LightshipOcclusionExtensionで
これらのプロパティを分離し、DepthTexture
(テクスチャ自体)とDepthTransform
(表示マトリックス)の両方を提供しています。 変換行列を個別に保存することで、デバイスから深度情報が提供されなかったカメラフレームを補正するためのワーピングデータを追加することができます。
このHow-Toは以下の内容をカバーしている:
- フルスクリーン画像を表示するための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の追加
拡張機能を追加するには、以下の手順を行います。
- Main Camera
GameObjectに
LightshipOcclusionExtensionを
追加します:- Hierarchy で、
XROrigin
を展開し、 Main Camera を選択します。 次に、 インスペクタ で、 コンポーネントの追加 をクリックし、Lightship Occlusion Extension
を追加します。
- Hierarchy で、
AR Foundationオクルージョンマネージャを使用して深度テクスチャにアクセスする
ARファンデーションからデプス・テクスチャーを取得する:
プロジェクト・ウィンドウでAssetsフォルダを開き、その中で右クリックしてCreateメニューを開き、C#スクリプトを選択します。
Depth_HowTo
という名前を付けます。ファイルエディターで
Depth_HowTo.csを
開き、以下のスニペットのコードを追加して保存します。階層から メインカメラを選択します。 Assetsフォルダから
Depth_HowTo.csを
Inspectorの一番下にドラッグし、Componentとして追加します。 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()
);
// テクスチャで何かする
// ...
}
}
ライトシップオクルージョンエクステンションを使った深度テクスチャへのアクセス
ライトシップから深度のテクスチャーを得る:
Depth_HowTo.csを
もう一度開き、そのコードを以下のスニペットで置き換える。スクリプトの準備ができたら、 Hierarchy から Main Camera を選択し、 Inspector でそれをコンポーネントとして追加します。 オクルージョン拡張]フィールドの横にある円をクリックし、メインカメラを選択します。
深度の入門ガイド コードを表示するにはクリックしてください。
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画像を作成し、深度バッファを変換してカメラ出力の上に重ねるマテリアルを添付します。
リアルタイムの深度表示を設定するには、次の手順を行います。
Hierarchy で右クリックし、 UI メニューを開いて Raw Image を選択します。 新しいRaw画像に
DepthImageと
名前を付ける。画像変換ツールを使って、Raw画像を中央に配置し、後で見えるように画面全体に引き伸ばす(例は下の画像を参照)。
- すべてのパラメーター(左、上、Z、右、下)を0に設定します。
マテリアルとシェーダーを追加する
マテリアルとシェーダーを作成するには、次のように行います。
- プロジェクトウィンドウで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に渡す
深度情報を生画像に渡すには、Depth_HowTo.csを
修正する:
- スクリプトの
using
文を更新し、RawImageと
Materialを
受け取る変数を追加します:
using UnityEngine.UI;
public class Depth_HowTo : MonoBehaviour
{
public RawImage _rawImage;
public Material _material;
// スクリプトの残りは以下の通りです。
その
Material
に対して、深度テクスチャと表示変換行列を設定します。深度情報と変換情報をシェーダーに渡すために、
SetTexture
とSetMatrix
をスクリプトに追加します。メインカメラを選択し、インスペクタでRaw ImageとMaterialを先ほど作成したものに設定します。
クリックすると、変更されたDepth_HowTo更新関数が表示されます。
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;
}.
//add our material to the raw image
_rawImage.material = _material;
// set the depth texture and display matrix
var depthTexture = _occlusionExtension.DepthTexture;
var displayMatrix = _occlusionExtension.DepthTransform;
//シェーダー用の変数を設定
//注意:深度テクスチャの更新はUpdate()関数内で行う必要があります
_rawImage.material.SetTexture("_DepthTex", depthTexture);
_rawImage.material.SetMatrix("_DepthTransform", displayMatrix);
}.
}