本文へスキップ

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

結果

拡張現実アプリケーションでカメラに関連する画像を扱うには、通常、画像と対になった変換行列を扱うことが含まれる。 その理由は、カメラ画像のアスペクト比がアプリケーションのビューポートのアスペクト比と一致することが保証されていないからです。 ほとんどの場合、ビューポートはアプリケーションを実行するデバイスの画面全体をカバーする。 画像が歪むのを避けるため、表示する前にトリミング、パディング、あるいは回転させる必要がある。

AR背景画像の場合、上記の変換は表示行列を介して行われる。 このマトリクスは、深度画像とAR背景画像の縦横比が同じであれば、深度画像の表示にも使用できる。 AR Foundationを使って深度画像を取得する標準的な方法は、AROcclusionManagerenvironmentDepthTextureプロパティにアクセスすることです。 このプロパティは、AR背景画像と同じ縦横比のテクスチャを提供する。

Lightshipを使用する場合、さらに LightshipOcclusionExtensionDepthTexture プロパティを提供します。 この方法は、画像を元のアスペクト比のまま得られるため、上述の方法よりも性能が高い。 しかし、AR背景画像とは縦横比が異なるため、別の変換行列を使用する必要がある。 この行列には LightshipOcclusionExtensionDepthTransform プロパティでアクセスできる。 このアプローチを使用する大きな利点は、この変換が深度画像をワープさせ、デバイスから深度情報を受信していないフレームを埋めることである。

このハウツーでは、フルスクリーンで深度テクスチャにアクセスして表示する従来の方法とLightshipのアプローチの両方を紹介します。

このハウツーでは、次のことをカバーする:

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

前提条件

ARDKがインストールされたUnityプロジェクトと、セットアップされた基本的なARシーンが必要です。 詳細については、 ARDK 3のインストール および 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. LightshipOcclusionExtension をメインカメラ GameObject に追加します。
    1. Hierarchy で、 XROrigin を展開し、 Main Camera を選択します。 次に、 インスペクタ で、 コンポーネントの追加 をクリックし、 Lightship Occlusion Extension を追加します。

Occlusion Manager から深度テクスチャにアクセスする(アプローチA)

コードで深度テクスチャにアクセスするには、カメラとOcclusion Manager をポーリングするスクリプトが必要です:

  1. プロジェクトウィンドウで、AssetsフォルダにC#スクリプトファイルを作成します。

  2. Depth_HowToと名付ける。

  3. ファイルエディターでDepth_HowTo.csを開き、以下の手順の後にスニペットのコードを追加します。

  4. スクリプトの準備ができたら、Hierarchy からMain Cameraを選択し、Inspector に Component として追加します。 Occlusion Manager の横の丸をクリックし、Main Cameraを選択します。

    デプス_ハウツー
深度のHow-to コードを表示するにはクリックしてください。

using UnityEngine;
using UnityEngine.XR.ARFoundation;

public class Depth_HowTo :MonoBehaviour
{
public AROcclusionManager _occlusionManager;

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

var texture = _occlusionManager.environmentDepthTexture;

var displayMatrix = CameraMath.CalculateDisplayMatrix
(
texture.width,
texture.height,
Screen.width,
Screen.height,
XRDisplayContext.GetScreenOrientation()
);

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

}
}

スクリプト・チュートリアル

  1. この例では、AR FoundationのOcclusion Managerから深度画像を取得します。 この画像はAR背景画像と同じ縦横比を持ちます。
  2. Lightshipの数学ライブラリを使って、深度画像をスクリーンにフィットさせる表示マトリックスを計算します。 カメラマネージャのフレーム更新コールバックで提供される表示マトリックスを使うこともできますが、そのマトリックスのレイアウトはプラットフォームによって異なります。

Lightship Occlusion Extension から深度テクスチャにアクセスする(アプローチB)

コードで深度テクスチャにアクセスするには、カメラとOcclusion Managerをポーリングするスクリプトが必要です:

  1. プロジェクトウィンドウで、AssetsフォルダにC#スクリプトファイルを作成します。

  2. Depth_HowToと名付ける。

  3. ファイルエディターでDepth_HowTo.csを開き、以下の手順の後にスニペットのコードを追加します。

  4. スクリプトの準備ができたら、Hierarchy からMain Cameraを選択し、Inspector に Component として追加します。 Camera Extension)フィールドの横の丸をクリックし、Main Cameraを選択します。

    デプス_ハウツー
デプスのHow-Toコードを表示するにはクリックしてください。

using UnityEngine;
using UnityEngine.XR.ARFoundation;

public class Depth_HowTo :MonoBehaviour
{
public LightshipOcclusionExtension _occlusionExtension;

void Update()
{
var texture = _occlusionExtension.DepthTexture;

var displayMatrix = _occlusionExtension.DepthTransform;

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

}
}

スクリプト・チュートリアル

  1. この例では、LightshipのARオクルージョン拡張機能から深度画像を取得する。 この画像の縦横比はAR背景画像と一致することを保証するものではありません。
  2. 深度テクスチャのほかに、オクルージョン拡張機能は、深度を画面に表示するための適切な変換行列も提供する。 このマトリックスは従来のディスプレイマトリックスとは異なり、欠落したフレームを補正するためのワーピングも含まれている。

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

シーンに生画像を追加し、深度バッファを変換してスクリーンと位置合わせするマテリアルを添付することで、ライブの深度情報を表示できます。

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

  1. 階層内で右クリックし、UIメニューを開いてRaw Imageを選択します。

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

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

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

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

  1. Assets(アセット)ウィンドウで右クリックし、 Create(作成) の上にカーソルを合わせ、 Material(マテリアル) を選択します。 DepthMaterial という名前を付けます。
  2. この作業を繰り返しますが、Shaderメニュー開き、Unlit Shaderを選択します。 新しいシェーダーにDisplayDepth という名前を付ける。
  3. シェーダーをマテリアルにドラッグして、それらを接続します。
  4. DisplayDepth Shader Code のコードを 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
}.
}
}

深度を生画像に渡す

スクリプトで生画像に深度情報を渡す:

  1. RawImageとMaterial`を取り込むようにスクリプトを更新する。

  2. そのマテリアルの深度テクスチャと表示トランスフォームを設定します。

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

  4. UnityのスクリプトにMaterialと RawImageを接続します。

    デプス_ハウツー
クリックすると、更新されたDepthDisplayシェーダーのコードが表示されます。
using UnityEngine.UI;

public class Depth_HowTo :MonoBehaviour
{
public RawImage _rawImage;

public Material _material;

...


void Update()
{
if (!_occlusionManager.subsystem.running)
{
return;
} //マテリアルを生画像に追加します。

//raw imageにマテリアルを追加
_rawImage.material = _material;

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

}

セットアップのテストとデバイスへのビルド

テスト用にすべての部品を接続して試すには、次の手順を行います。

  1. インスペクタでメインカメラに オクルージョンマネージャと デプススクリプトマテリアルと RawImageが設定されている)がアタッチされていることを確認します。 これでUnity EditorでPlaybackを使ったテストができるはずです。 また、Build Settingsを開き、Build and Runをクリックしてデバイスにビルドして試すこともできる。
最終セットアップ