深度付き動画専用メディアプレイヤー Depth Video Player VR

# はじめに
Depth Video Player VRは深度付きの動画を視聴、確認する Windows用の簡易メディアプレイヤーです。デスクトップ アプリケーションですが、OpenXR対応なのでSteamVR等を使ってVRデバイスで使用することもできます。

* Depth Video Player VR

https://x.com/amarectv/status/1887654666403709136

* 深度付き動画
通常の2D動画の画像と 深度推定の結果を並べたもの

https://x.com/amarectv/status/1883543554670330059


:::note
深度付き動画作成ツール
https://qiita.com/amaman/items/811176c185856a913714
:::


# 動作条件
|項目|内容|
|-|-|
|OS|Windows 10 / 11|
|ビデオファイル再生|AVPro (Media Foundation)|
|VRモード|OpenXR|


* VRモードは以下の環境で動作確認しました

|項目|内容|
|-|-|
|OS|Windows 10 / 11|
|VRプラットフォーム|SteamVR (OpenXR)|
|VR機種|Meta Quest 2 / 3|
|そのほか|Meta Quest版の Virtual Desktop使用|


# ダウンロード
- [DepthVideoPlayerVR Ver1.2.0](http://www.amarectv.com/cgi-bin/kz_dl_counter.cgi?http://www.amarectv.com/download/DepthVideoPlayerVR120.zip)

- [深度付きサンプル動画 1](http://www.amarectv.com/cgi-bin/kz_dl_counter.cgi?http://www.amarectv.com/download/depth_sample.zip)
![depth_sample1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624096/0246271b-445b-4fd0-ba08-8047b14e2e79.png)

- [深度付きサンプル動画 2](http://www.amarectv.com/cgi-bin/kz_dl_counter.cgi?http://www.amarectv.com/download/depth_sample2.zip)
![depth_sample2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624096/3f8e77fd-32f9-457b-8a9f-78ca5faa4ad6.png)


# 使い方
インストール作業はありません。解凍してできる`DepthVideoPlayerVR.exe`を実行してください。
動画ファイルをエクスプローラーなどからドラッグ＆ドロップするか `playlist.txt`へ動画ファイルを列挙して使います。



## VRモード
`vr.bat`から起動するか、`V`キーを押すとVRモードで動作します。
右コントローラーで操作します。左は使えません。
|操作|内容|
|-|-|
|トリガー|シーク<br>シーク中にほかのボタンを押すとキャンセル|
|トリガー＋スティック|次のファイル|
|グリップ|スクリーン移動と回転|
|グリップ＋Bボタン|位置や向きをリセット|
|グリップ＋スティック|スケール調整|
|スティック左右|10秒送り|
|スティックを押す|一時停止|
|Aボタン|深度スケール調整<br>調整中にトリガーでリセット<br>調整中にグリップでキャンセル|
|Bボタン|縦にドラッグしてファイル選択<br>トリガーまたはグリップでキャンセル|
|トリガー＋グリップ＋Bボタン|アプリ終了|


* ボリューム調整はできません。キーボードで操作してください
* `F`キーを押すとフィルターの`ON` / `OFF`を切り替えます
* `D`キーを押すと 2D映像と並べて表示します

:::note alert
Meta QuestでVirtual Desktopを利用する場合ハンドトラッキングが(勝手に)切り替わって誤動作するので Virtual Desktopの入力設定からハンドトラッキングを無効にしてください。
![handtracking.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624096/83dea040-c23c-42f8-87e2-02aee3260c1e.png)
:::

# 更新履歴
|日付|バージョン|内容|
|-|-|-|
|2025.2.7|Ver1.0.0<br>[ダウンロード](http://www.amarectv.com/cgi-bin/kz_dl_counter.cgi?http://www.amarectv.com/download/DepthVideoPlayerVR100.zip)|- 初版|
|2025.2.25|Ver1.1.0<br>[ダウンロード](http://www.amarectv.com/cgi-bin/kz_dl_counter.cgi?http://www.amarectv.com/download/DepthVideoPlayerVR110.zip)|- デプススケール追加<br>- 輪郭処理追加<br>- ファイルリスト変更|
|2025.6.7|Ver1.2.0<br>[ダウンロード](http://www.amarectv.com/cgi-bin/kz_dl_counter.cgi?http://www.amarectv.com/download/DepthVideoPlayerVR120.zip)|- 深度付きVR動画へ対応<br>- 高画質化フィルタ<br>- VRコントローラーの操作変更|

# Ver1.1.0
## デプススケール
[Shift]を押しながら左ドラックで立体化の押し出し具合を調整します。
調整中に右を押してリセット。
VRではトリガーを押しながら左右。リセットはグリップを押しながらB。

## 輪郭処理
大きく深度が変化する部分(主に被写体の輪郭)を目立たないように処理します。
キーボードの`1`から`4`を押して選択します。
![depth_edge.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624096/7a5d82ec-344a-4fe2-9772-25b3986c3fe2.png)

- [1] 処理なし
そのまま表示します。トゲトゲになり目立ちます。

- [2] ぼかし
周囲の色でぼかします。
自然な表示になりますが被写体の色も含まれるため やや目立ちます。

- [3] 背景色
周囲の低い深度の色を背景色とみなし 背景色を使ってぼかします。
多くの場合で 最も目立たなくなります。

- [4] 処理領域
処理対象になっている個所を着色します。
顔とか 処理してほしくない箇所が対象になっていないか確認するのに使います。
青：処理対象領域
緑：処理対象だけど見た目にほぼ影響のない領域（無視していい）

## ファイルリスト
プレイリスト`playlist.txt`がフォルダに対応しました。
フォルダー（サブフォルダ含む）内のmp4ファイルを全てリストへ加えます。
ファイルをドロップしてもリストをクリアしないで追加するように変更。
リストをクリアするには`R`キーを押してください。
フォルダをドロップするとフォルダ内のmp4ファイルを全て追加します。
右を押しながらホイールで次のファイルを再生します。

# Ver1.2.0
## 深度付き動画へ対応
キーボードの`7`から`0`キーでスクリーンを選択できます。
初期状態で`0`の自動に設定されているので特別操作する必要はありません。

|キー|スクリーン形状|動作モード|
|:-:|-|-|
|7|2D用スクリーン|シアターモード|
|8|VR用スクリーン|シアターモード|
|9|VR用スクリーン|VR動画モード|
|0|自動|自動|

`0`キーの自動は動画の画像サイズによりVR動画、2D動画を判断し自動で設定します。

## シアターモード
離れた位置からスクリーンを視聴します。
立体感がよくわかる通常のモードです。

## VR動画モード
球状のスクリーンの中心から視聴します。
VR動画の中へ入り込んだ体験ができます。
通常のVR動画と異なりポジショントラッキングで（多少）移動することができます。
シアターモードと比べると立体感は感じられません。
グリップを押しながらBで位置をリセットします。（ホームボタンの長押し等と併用してください）
グリップムーブは移動のみで向きの調整はできません。
人物が巨人に見える場合はグリップを押しながらスティックの左右でスケールを調整してください。
デプススケールの調整は歪むので1.00のまま使って下さい。

## 対応フォーマット
VR180 equirectangular sbsを元にした次の深度付きVR動画へ対応します。

- 左の画像、右の画像、深度画像の3面構成
![depth_vr_sbs3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624096/cdd3c95b-1668-42ea-86f5-209465d111f6.png)


- 左の画像、深度画像の2面構成
![depth_vr_sbs2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624096/128d4ffe-477c-478f-a8ae-11f8db475678.png)


## 深度付きVR動画の作成
以下の2つで作成できます。

|ツール|補足|
|-|-|
|[dav2video](https://qiita.com/amaman/items/811176c185856a913714#%E6%B7%B1%E5%BA%A6%E4%BB%98%E3%81%8Dvr%E5%8B%95%E7%94%BB%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B%E6%9A%AB%E5%AE%9A%E7%89%88)|映像として安定しているけど<br>スケールが合わず巨人化する場面が多い<br>導入にはPythonのセットアップが必要で面倒|
|[Touchly Renderer](https://touchly.app/renderer/)|スケールが合うのでリアル感があるけど<br>映像として安定しない（Free版使用）<br>ダウンロードするだけで簡単に使える|

## 高画質化フィルタ
`F`キーを押すとフィルターの`ON` / `OFF`ができます。
初期状態でONになっています。
dav2videoで作成したデプス動画では`ON`で良い結果が得られます。

## 操作廃止
トリガーを押しながらスティックで1分送りを廃止しました。
グリップを押しながらスティックの上下でＺ座標の調整を廃止しました。

## 人物が巨人化する
dav2videoで作成したデプス動画はノンスケールのためVR動画モードだと（スケールが合わず）巨人化する場面が多々あります。
グリップを押しながらスティックの左右でスケールを小さ目に調整してください。
また、[Touchly Renderer](https://touchly.app/renderer/)で作成したデプス動画はスケールが合う（事が多い）ので多くの場面でリアルな印象を受けます。

## 視線が合わない
VR動画の左カメラの画像のみを使って処理するため、被写体の目線が合いません。
グリップを押して位置を少し動かして なんとな～くこっちを向いてるようにしてください。

根本的な解決には左カメラの画像と右カメラの画像から真ん中の画像を生成するAI処理が必要かもしれません。

## VR動画モードで視聴すると立体感を感じない
VR動画モードはリアルに近い映像体験となるため立体感を感じにくい性質があります。
VR動画の立体感を愉しむにはシアターモードを使って下さい。

:::note
VR動画およびVR動画の立体化は普段肉眼で見ている景色をできるだけ再現しようとするので ”イタズラに” 立体的だと感じる場面は少ないです。良くも悪くも ”普通” と感じられる体験（リアルな映像体験）がゴールとなります。

一方、3D動画および2D動画の立体化は 調整により立体感を ”イタズラに” 強調できるので そういった愉しみ方、演出が可能となっています。
なのでVR動画の立体感を愉しみたい場合は VR動画を2D動画のように扱うシアターモードをご利用ください。
:::

# 動作原理
方眼状メッシュの各頂点の位置を右側の深度画像の輝度を使って調整します。
そうやって凸凹になったメッシュへ 左側の画像を張り付ければ完成です。
この処理は下記シェーダープログラムで完結します。

* 方眼状のメッシュ
![mesh.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624096/ef9c26e8-e040-d4ce-0d87-aff2deff70e3.png)
Blender等を使って作るか、Unityのスクリプトで実行時に作成します

* 深度付き画像
![depth_image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624096/dbb433c8-5f05-52b2-5e2f-99ecc0a63bb9.png)
通常の2D画像(左)と Depth Anything等で作った深度画像(右)をくっつけたもの

* シェーダープログラム
```c++:depth.shader
Shader "AmaRecVR/depth"
{
	Properties
    {
    	_MainTex ("Base (RGB) Trans (A)", 2D) = "black" {}
    }
	
    SubShader
    {
		Tags { "RenderType"="Transparent" "IgnoreProjector"="True" "Queue"="Transparent" }
		LOD 100
		ZWrite On		// 手前のものに奥のものが隠れるように Zバッファを使う
		ZTest LEqual	// 既に書き込まれたZ値より手前の場合に 描画する（奥のものは描画しないので 隠れる）
		Blend One Zero
		Lighting Off
		Cull Off

		Pass
		{
			CGPROGRAM
            #pragma vertex vert
           	#pragma fragment frag
			#pragma target 3.0
           	#include "UnityCG.cginc"

           	struct appdata
           	{
               	float4 vertex : POSITION;
               	float2 uv     : TEXCOORD0;
           	};

           	struct v2f
           	{
               	float4 vertex      : SV_POSITION;
               	float2 uv          : TEXCOORD0;
           	};

			// 動画の画像
			uniform sampler2D _MainTex;
			
			// 頂点シェーダー
			v2f vert (appdata v)
			{
				v2f o;
				
				//頂点に該当する テクスチャの座標(uv)を計算 一般的な処理
				float2 uv  = (v.uv * float2(0.5, 1.0));		// 左半分だけ使う 横座標を半分にするだけ
				uv.y       = 1.0 - uv.y;					// 上下を反転させる
				o.uv       = uv;							// フラグメントシェーダーへ伝える
				
				// 画像の右側のピクセル（深度）を取得 uv座標へ0.5足して 画像からサンプリングする
				fixed4 col = tex2Dlod(_MainTex, float4(uv + float2(0.5, 0.0), 0, 0));

				// 赤の値を使って メッシュのZ座標を調整 押し出し具合が変わる
				v.vertex.z = -col.r/2;
				o.vertex   = UnityObjectToClipPos(v.vertex);	// 調整した頂点の座標を計算する 一般的な処理
				return o;
			}

			// フラグメントシェーダー 画像を張り付ける 一般的な処理
			fixed4 frag (v2f i, fixed facing : VFACE) : SV_Target
			{
				fixed4 col = tex2D(_MainTex, i.uv);
				col.rgb = (facing > 0 ) ? col.rgb: col.rgb / 4.0;	// 裏面を暗くする
				return col;
			}
			ENDCG
		}
	}
	Fallback "Diffuse"
}
```

頂点シェーダー v2f vert (appdata v)で上記の処理を行っています。