深度テクスチャからピクセルのワールド空間位置を再構成する
この例の Unity シェーダーは、深度テクスチャとスクリーンスペース UV 座標を使用して、ピクセルのワールド空間位置を再構成します。シェーダーでメッシュ上にチェッカーボードパターンを描画することにより、位置を可視化します。
以下の画像は、最終的な結果を示しています。
このページは、以下のセクションで構成されています。
サンプルシーンの作成
サンプルシーンを作成して、このセクションのステップに従います。
URP を既存の Unity プロジェクトにインストールするか、ユニバーサルプロジェクトテンプレート を使用して新しいプロジェクトを作成します。
サンプルシーンで、平面のゲームオブジェクトを作成し、いくつかのゲームオブジェクトを覆うように配置します。
新しいマテリアルを作成して、平面に割り当てます。
新しいシェーダーを作成して、マテリアルに割り当てます。Unity シェーダーのソースコードを URP の基本的な Unlit シェーダー ページからコピーして貼り付けます。
URP アセットを選択します。URP のテンプレートを使用してプロジェクトを作成した場合、URP アセットパスは
Assets/Settings/UniversalRP-HighQuality
です。URP アセットの General セクションで、
Depth Texture
を有効にします。ステップ 4 で作成したシェーダーを開きます。
ShaderLab コードの編集
このセクションは、URP の基本的な Unlit シェーダー ページからソースコードをコピーしたことを前提としています。
ShaderLab コードに以下の変更を加えます。
HLSLPROGRAM
ブロックで、深度テクスチャのシェーダーヘッダー用に include 宣言を追加します。例えば、Core.hlsl
用の既存の include 宣言の下に配置します。#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" // DeclareDepthTexture.hlsl ファイルにはカメラ深度テクスチャのサンプリング用の // ユーティリティが含まれています。 #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareDepthTexture.hlsl"
DeclareDepthTexture.hlsl
ファイルにはカメラ深度テクスチャのサンプリング用の関数が含まれています。この例では、ピクセルの Z 座標のサンプリングにSampleSceneDepth
関数を使用しています。フラグメントシェーダーの定義で、
Varyings IN
を入力として追加します。half4 frag(Varyings IN) : SV_Target
この例では、フラグメントシェーダーで
Varyings
構造体のpositionHCS
プロパティを使用してピクセルの位置を取得しています。フラグメントシェーダー内で、深度バッファのサンプリング用の UV 座標を算出するために、ピクセル位置をレンダーターゲットの解像度である
_ScaledScreenParams
で除算します。_ScaledScreenParams.xy
プロパティでは、動的解像度などのレンダーターゲットのあらゆるスケーリングが考慮されます。float2 UV = IN.positionHCS.xy / _ScaledScreenParams.xy;
フラグメントシェーダー内で、
SampleSceneDepth
関数を使用して深度バッファをサンプリングします。#if UNITY_REVERSED_Z real depth = SampleSceneDepth(UV); #else // z を OpenGL の NDC に一致するよう調整 real depth = lerp(UNITY_NEAR_CLIP_VALUE, 1, SampleSceneDepth(UV)); #endif
SampleSceneDepth
関数はDeclareDepthTexture.hlsl
ファイル内の関数です。[0, 1]
の範囲内で Z 値を返します。再構成関数 (
ComputeWorldSpacePosition
) を機能させるには、深度値が正規化デバイス座標 (NDC) 空間内になければなりません。D3D での Z の範囲は[0, 1]
、OpenGL での Z の範囲は[-1, 1]
です。この例では、
UNITY_REVERSED_Z
定数を使用してプラットフォームを決め、Z 値の範囲を調整しています。詳細な説明については、この例のステップ 6 を参照してください。UNITY_NEAR_CLIP_VALUE
変数は、プラットフォームに依存しないクリップスペースのニアクリップ面値です。詳細については、プラットフォーム固有のレンダリングの違い を参照してください。
ピクセルの UV および Z 座標からワールド空間位置を再構成します。
float3 worldPos = ComputeWorldSpacePosition(UV, depth, UNITY_MATRIX_I_VP);
ComputeWorldSpacePosition
は、UV 値と深度 (Z) 値からワールド空間位置を計算するユーティリティ関数です。この関数は、SRP Core パッケージのCommon.hlsl
ファイル内に定義されています。UNITY_MATRIX_I_VP
は、クリップスペースからワールド空間へと点を変換する反転ビュー射影行列です。ピクセルのワールド空間位置を可視化するには、チェックボードエフェクトを作成します。
uint scale = 10; uint3 worldIntPos = uint3(abs(worldPos.xyz * scale)); bool white = (worldIntPos.x & 1) ^ (worldIntPos.y & 1) ^ (worldIntPos.z & 1); half4 color = white ? half4(1,1,1,1) : half4(0,0,0,1);
scale
は、チェックボードパターンサイズの逆スケールです。abs
関数は、パターンを負側の座標にミラーリングしています。worldIntPos
変数のuint3
宣言では、座標位置を整数にスナップしています。式
<integer value> & 1
のAND
演算子は、値が偶数 (0) か奇数 (1) かをチェックしています。この式によってコードでサーフェスを正方形に分割できます。式
<integer value> ^ <integer value>
のXOR
演算子は、正方形の色を反転させています。ジオメトリがレンダリングされない領域では、深度バッファに有効な値がない場合があります。以下のコードでは、そのような領域に黒色を描画します。
#if UNITY_REVERSED_Z if(depth < 0.0001) return half4(0,0,0,1); #else if(depth > 0.9999) return half4(0,0,0,1); #endif
プラットフォームによってファークリップ面に使用される Z 値は異なります (0 == far または 1 == far)。
UNITY_REVERSED_Z
定数によってコードですべてのプラットフォームに正しく対処できます。シェーダーのコードを保存すれば、例をいつでも実行できます。
以下の画像は、最終的な結果を示しています。
完成した ShaderLab コード
以下は、この例の完成した ShaderLab コードです。
// この Unity シェーダーは、深度テクスチャとスクリーンスペース UV 座標を使用して
// ピクセルのワールド空間位置を再構成します。シェーダーでメッシュ上にチェッカーボードパターンを
// 描画することにより、位置を可視化します。
Shader "Example/URPReconstructWorldPos"
{
Properties
{ }
// シェーダーのコードが含まれる SubShader ブロック。
SubShader
{
// SubShader Tags では SubShader ブロックまたはパスが実行されるタイミングと条件を
// 定義します。
Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" }
Pass
{
HLSLPROGRAM
// この行では頂点シェーダーの名前を定義します。
#pragma vertex vert
// この行ではフラグメントシェーダーの名前を定義します。
#pragma fragment frag
// Core.hlsl ファイルには、よく使用される HLSL マクロおよび関数の
// 定義が含まれ、その他の HLSL ファイル (Common.hlsl、
// SpaceTransforms.hlsl など) への #include 参照も含まれています。
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
// DeclareDepthTexture.hlsl ファイルにはカメラ深度テクスチャのサンプリング用の
// ユーティリティが含まれています。
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareDepthTexture.hlsl"
// この例では、Attributes 構造体を頂点シェーダーの入力構造体として
// 使用しています。
struct Attributes
{
// positionOS 変数にはオブジェクト空間内での頂点位置が
// 含まれます。
float4 positionOS : POSITION;
};
struct Varyings
{
// この構造体内の位置には SV_POSITION セマンティックが必要です。
float4 positionHCS : SV_POSITION;
};
// Varyings 構造体内に定義されたプロパティを含む頂点シェーダーの
// 定義。vert 関数の型は戻り値の型 (構造体) に一致させる
// 必要があります。
Varyings vert(Attributes IN)
{
// Varyings 構造体での出力オブジェクト (OUT) の宣言。
Varyings OUT;
// TransformObjectToHClip 関数は頂点位置をオブジェクト空間から
// 同種のクリップスペースに変換します。
OUT.positionHCS = TransformObjectToHClip(IN.positionOS.xyz);
// 出力を返します。
return OUT;
}
// フラグメントシェーダーの定義。
// Varyings 入力構造体には頂点シェーダーからの補間された値が
// 含まれます。フラグメントシェーダーで `Varyings` 構造体の `positionHCS` プロパティを
// 使用してピクセルの位置を取得しています。
half4 frag(Varyings IN) : SV_Target
{
// 深度バッファのサンプリング用の UV 座標を算出するために
// ピクセル位置をレンダーターゲットの解像度である
// _ScaledScreenParams で除算します。
float2 UV = IN.positionHCS.xy / _ScaledScreenParams.xy;
// カメラ深度テクスチャから深度をサンプリングします。
#if UNITY_REVERSED_Z
real depth = SampleSceneDepth(UV);
#else
// Z を OpenGL の NDC ([-1, 1]) に一致するよう調整
real depth = lerp(UNITY_NEAR_CLIP_VALUE, 1, SampleSceneDepth(UV));
#endif
// ワールド空間位置を再構成します。
float3 worldPos = ComputeWorldSpacePosition(UV, depth, UNITY_MATRIX_I_VP);
// 以下の部分ではチェッカーボードエフェクトを作成します。
// scale は正方形の逆サイズです。
uint scale = 10;
// 座標のスケーリング、ミラーリング、スナップを行います。
uint3 worldIntPos = uint3(abs(worldPos.xyz * scale));
// サーフェスを正方形に分割します。色の ID 値を計算します。
bool white = ((worldIntPos.x) & 1) ^ (worldIntPos.y & 1) ^ (worldIntPos.z & 1);
// ID 値 (black または white) に応じて正方形に色を塗ります。
half4 color = white ? half4(1,1,1,1) : half4(0,0,0,1);
// ファークリップ面の付近の色を黒に
// 設定します。
#if UNITY_REVERSED_Z
// D3D などの REVERSED_Z があるプラットフォームの場合。
if(depth < 0.0001)
return half4(0,0,0,1);
#else
// Case for platforms without REVERSED_Z, such as OpenGL.
if(depth > 0.9999)
return half4(0,0,0,1);
#endif
return color;
}
ENDHLSL
}
}
}