本文へスキップ

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

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

このHow-Toは以下の内容をカバーしている:

  1. フルスクリーン画像を表示するためのUIとシェーダーリソースの設定。
  2. デプス・テクスチャーへのアクセス。
  3. 画面上のテクスチャにフィットする画像変換行列を取得する。
  4. 深度テクスチャとその画像変換行列をレンダリングリソースに割り当てる。
  5. 深度情報を使って何をすべきかの例として、メートル深度を数学的にカラースケールに変換する。

前提条件

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

ARオクルージョンマネージャーの追加

ARFoundationでは、AR Occlusion ManagerMonoBehaviourが深度バッファへのアクセスを提供します。 プロジェクトにARオクルージョンマネージャーを追加する:

  1. メインカメラの GameObjectに AROcclusionManagerを追加します:

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

Lightship Occlusion Extensionの追加

拡張機能を追加する:

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

AR Foundationオクルージョンマネージャを使用して深度テクスチャにアクセスする

ARファンデーションからデプス・テクスチャーを取得する:

  1. プロジェクト・ウィンドウでAssetsフォルダを開き、その中で右クリックしてCreateメニューを開き、C#スクリプトを選択します。 Depth_HowToと名付ける。

  2. ファイルエディターでDepth_HowTo.csを開き、以下のスニペットのコードを追加して保存します。

  3. 階層から メインカメラを選択します。 AssetsフォルダからDepth_HowTo.csを Inspectorの一番下にドラッグし、Componentとして追加します。 Occlusion Manager の横の丸をクリックし、Main Cameraを選択します。

    オクルージョンマネージャーの設定
深度のHow-to コードを表示するにはクリックしてください。

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()
);

// テクスチャで何かする
// ...

}
}

ライトシップオクルージョンエクステンションを使った深度テクスチャへのアクセス

ライトシップから深度のテクスチャーを得る:

  1. Depth_HowTo.csをもう一度開き、そのコードを以下のスニペットで置き換える。

  2. スクリプトの準備ができたら、Hierarchy からMain Cameraを選択し、Inspector に Component として追加します。 オクルージョン拡張]フィールドの横にある円をクリックし、メインカメラを選択します。

    ライトシップ・オクルージョン・エクステンションの設定
デプスのHow-Toコードを表示するにはクリックしてください。

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;

// テクスチャで何かする
// ...

}
}

深度バッファを表示するための生画像の追加

ライブの深度データにアクセスできるようになったので、それをカスタムUIエレメントに供給して画面上に表示することができる。 この例では、Raw画像を作成し、深度バッファを変換してカメラ出力の上に重ねるマテリアルを添付します。

ライブ深度表示を設定するには

  1. 階層内で右クリックし、UIメニューを開いてRaw Imageを選択します。 新しいRaw画像にDepthImageと名前を付ける。

  2. 画像変換ツールを使って、Raw画像を中央に配置し、後で見えるように画面全体に引き伸ばす(例は下の画像を参照)。

    1. すべてのパラメータ(左、上、Z、右、下)を0に設定する。
    ストレッチ・トランスフォーム

マテリアルとシェーダーを追加します。

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

  1. プロジェクトウィンドウでAssetsフォルダを開き、その中で右クリックします。 Createメニューを開き、Materialを選択します。 新しいマテリアルに「DepthMaterial」という名前を付けます。
  2. この作業を繰り返しますが、Shaderメニュー開き、Unlit Shaderを選択します。 新しいシェーダーにDisplayDepth という名前を付ける。
  3. シェーダーをマテリアルにドラッグして、それらを接続します。
  4. シェーダをダブルクリックして開き、次のセクションでDisplayDepthシェーダのコードを貼り付けます。

DisplayDepth Shader Code

深度表示は、vert/frag セクションを使用する標準的なフルスクリーン・シェーダーです。 シェーダーのvertセクションで、UVにディスプレイ変換を掛けてテクスチャをサンプリングする。 これは、奥行きをスクリーンに合わせるトランスフォームを提供する。

クリックするとDepthDisplayシェーダが表示されます。
シェーダ "Unlit/DisplayDepth"
{
プロパティ
{
_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
}.
}
}

深度を生画像に渡す

深度情報を生画像に渡すには、Depth_HowTo.csを修正する:

  1. スクリプトのusing文を更新し、RawImageと Materialを受け取る変数を追加します:
using UnityEngine.UI;

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

// スクリプトの残りは以下の通りです。
  1. そのマテリアルの深度テクスチャと表示トランスフォームを設定します。

  2. スクリプトにSetTextureSetMatrixを追加して、深度とトランスフォーム情報をシェーダに渡します。

  3. メインカメラを選択し、インスペクタでRaw ImageとMaterialを先ほど作成したものに設定します。

    Depth_HowToスクリプトコンポーネントと割り当てられた変数
クリックすると、変更された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 EditorでPlaybackを使ったテストができるはずです。 また、Build Settingsを開き、Build and Runをクリックしてデバイスにビルドして試すこともできる。

出力例

この出力例では、奥行き情報をグレイスケールフィルタとしてレンダリングし、明るい要素が見る人に近くなるようにしている。

グレースケールでレンダリングされたデプス・シェーダー出力の例