_ReciprocalNearFar
については、あれは私が勝手に名付けたものです。あの名前に必然性はありませんので、お好きな名前にしていただいてかまいません。
「Cg/HLSL でシェーダープロパティを参照する - Unity マニュアル 」にありますように、通常はPropertiesブロックにプロパティを定義して、シェーダーコード本体にそれと対応するユニフォーム変数を宣言するスタイルになるかと思いますが(このようにするとマテリアルのインスペクター上に設定項目が現れ、そこから値を設定することもできるでしょう)、今回はPreserveRenderTexture
の
C#
1 // ★マテリアルにcameraのニア・ファープレーンの逆数を与えます
2 blitMaterial . SetVector ( "_ReciprocalNearFar" , new Vector4 ( 1.0f / camera . nearClipPlane , 1.0f / camera . farClipPlane ) ) ;
の部分でマテリアルにデータを送り込む方式にしており、マテリアル自体にプロパティを持たせる必要はないだろうと思いまして、プロパティは定義せずにユニフォーム変数の宣言のみを行う形にしました。
linearEyeDepth
の算出方法ですが、あの式はUnityが用意しているLinearEyeDepth
の式を拝借したものです。組み込みのシェーダーはダウンロードアーカイブ から入手でき、それに収録されているUnityCG.cgincによると
ShaderLab
1 // Z buffer to linear depth
2 inline float LinearEyeDepth( float z )
3 {
4 return 1.0 / (_ZBufferParams.z * z + _ZBufferParams.w);
5 }
となっていました。式の中で使われている_ZBufferParams
はUnity組み込みの変数で、「ビルトインのシェーダー変数 - Unity マニュアル 」によると
Z バッファ値をリニア化するために使用します。 x は (1-far/near)、 y は (far/near)、 z は (x/far)、 w は (y/far) です。
となっているそうです。LinearEyeDepth
を使わずにわざわざ自前で計算したのは、今回の深度画像出力のためのレンダリングは通常のシーン内オブジェクトのレンダリングとはタイミングが異なるためです。シーン内オブジェクト用のシェーダーであれば、レンダリングを行っているカメラのニア・ファープレーン情報をもとにUnityが自動的に_ZBufferParams
をセットしてくれるため気兼ねなくLinearEyeDepth
を使えるはずですが、今回は深度画像撮影用のカメラのニア・ファープレーンを使って計算しないと結果が狂ってしまうためあのようにしました。
ついでに、点群変換時に結果が狂ってしまう件について私もちょっと調べてみましたので報告いたします。
まず実験材料として、Unity側では下図のようなカラー画像・深度画像を撮影しました。床の赤青チェッカーパターンは、各タイルが1m四方のサイズになるようにしています。
このときのカメラ設定はField Of Viewが60、Clipping PlanesのNearが0.512、Farが8.192です。
ご質問者さんのスクリーンショットを拝見しますに、点群が妙に引き延ばされて配置されてしまったようですが、私の予想ですとUnity上のカメラ設定(特にField Of View)とOpen3D上での点群生成時のカメラ内部パラメーターがマッチしていないんじゃないかと思います。
また、カメラから遠い部分が消えてしまう現象についてですが、open3d.geometry.RGBDImage.create_from_color_and_depth によるとどうやらデフォルトではdepth_trunc
が3.0に設定されており、3mより遠くのデータは捨てられてしまうようでした。もしあそこがデフォルトのままでしたら、もっと大きくしてやると遠くの点も出力されるかもしれません。
私の場合は下記のようなコードで点群を生成してみたのですが、
Python
1 import open3d as o3d
2 import matplotlib . pyplot as plt
3 import math
4
5 color_raw = o3d . io . read_image ( "../../screenshot.png" )
6 depth_raw = o3d . io . read_image ( "../../DepthRenderTexture_0125.png" )
7
8 # 深度切り捨て距離を延長、ついでにグレースケール変換をオフ
9 rgbd_image = o3d . geometry . RGBDImage . create_from_color_and_depth (
10 color_raw ,
11 depth_raw ,
12 depth_trunc = 65.535 ,
13 convert_rgb_to_intensity = False )
14 print ( rgbd_image )
15
16 plt . subplot ( 1 , 2 , 1 )
17 plt . title ( 'Unitychan color image' )
18 plt . imshow ( rgbd_image . color )
19 plt . subplot ( 1 , 2 , 2 )
20 plt . title ( 'Unitychan depth image' )
21 plt . imshow ( rgbd_image . depth )
22 plt . show ( )
23
24 # Unity側のカメラの画角が60°で、撮影した画像の解像度が256ピクセル四方なら...
25 fieldOfView = 60.0
26 resolution = 256
27
28 # 画像解像度の半分(128ピクセル)を画角の半分(30°)のタンジェントで割れば、ピクセル単位の焦点距離が算出できるはず
29 halfExtent = resolution * 0.5
30 focalLength = halfExtent / math . tan ( math . radians ( fieldOfView * 0.5 ) )
31
32 pcd = o3d . geometry . PointCloud . create_from_rgbd_image (
33 rgbd_image ,
34 o3d . camera . PinholeCameraIntrinsic (
35 resolution , resolution ,
36 focalLength , focalLength ,
37 halfExtent , halfExtent ) )
38 pcd . transform ( [ [ 1 , 0 , 0 , 0 ] , [ 0 , - 1 , 0 , 0 ] , [ 0 , 0 , - 1 , 0 ] , [ 0 , 0 , 0 , 1 ] ] )
39
40 o3d . visualization . draw_geometries ( [ pcd ] )
下図のような結果が出力されました。見た感じではUnity上でオブジェクトを配置した時と比べて変に伸縮したりはしていない様子でした。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2024/01/29 04:30