スマホで撮った近所の公園のフォトスフィアからLittle PlanetとかTiny Planetと呼ばれている絵ができました。
ソース画像となるフォトスフィアとはGoogleによる呼称で、「360度パノラマVR」と呼ばれたり「全方位パノラマ」と呼ばれたり呼称が統一されていない様です。最近は「HDRi」で検索すると海外の素材集がいっぱいでてきますね。投影法はEquirectangular Projection(正距円筒図法)といいます。ここではフォトスフィアで統一します。
また、結果物であるLittle Planetのような投影法をStereographic Projection(ステレオ投影)といいます。
今回の目的は、HLSLによってEquirectangular Projectionされた画像をソースとしてStereographic Projectionした画像を得ることです。
まず、簡単のためにキューブマップをソースにしてやってみます。
scaleはアスペクト比と、Little Planetの大きさを決めます。ここは見た目が良くなるように適当に数値を決めればよいところです。
キューブマップのフェッチに使うdirはWikipediaのステレオ投影で紹介されている立体射影の逆写像の式そのままです。実際にフェッチの段階でyとzを入れ替えるのは、キューブマップのY方向が上下になっているからです。どうスィズルするかによってLittle Planetの中心をどこにするか決められます。
次に、フォトスフィアをソースにしてやってみます。冒頭の絵はこれで生成します。
前半部はキューブマップと全く同じで、dirを使ってフォトスフィアのどこからフェッチするかの計算が加わりました。まず、三次元ベクトルのdirを経度(longitude、-180~180)と緯度(latitude、-90~90)に変換します。そうすると経度と緯度がフォトスフィアのX方向とY方向に対応します。あとはテクスチャUVである0~1の範囲に変換すれば完成です。
Showing posts with label Photo Sphere. Show all posts
Showing posts with label Photo Sphere. Show all posts
Friday, August 21, 2015
Sunday, August 9, 2015
[slide]프로그래머를 위한 360VR
로드뷰, 360VR의 기반 기술인 Equirectangular Projection(이퀴렉탱귤러/등장방형도법)을 이해해, DirectX11과 HLSL로 화면 출력, 큐브맵과 상호 변환 방법을 공부합니다.
Saturday, August 1, 2015
HLSLでフォトスフィアビューアを作る
ストリートビューなどで使われ、最近はAndroidのカメラなどでも誰でも手軽に作れるようになったフォトスフィアをDirectX11とHLSLでレンダリングしてみます。全方向の色を記録するためEquirectangular projectionという投影方法で長方形になっています。世界地図などでは正距円筒図法と呼ばれるようです。

これにPhoto Sphere XMP Metadataと呼ばれるメタデータが埋まることで、対応アプリから認識されます。非対応アプリからは普通のJPGファイルです。
全方向画像と言えば、ゲーム開発で主に使われるのはcube mapです。実際、cube mapで遠景を描いているゲームも多いと思いますが、フォトスフィアビューアでやる事とほとんど同じです。各シェーダの全文の例を掲示します。何をしているかというと、各ピクセルの視線ベクトルを求め、その方向の色をcubemap又はPhoto Sphereから読み取っています。見ての通り頂点シェーダ(mainVS)からピクセルシェーダ(mainPS)の第一行目まで全く同じです。
C++からは頂点は4つ出力しています。頂点バッファをバインドしない代わりに頂点シェーダで画面の四隅の位置を設定して全画面を塗りつぶします。また、pos2をラスタライザに渡してピクセルシェーダからピクセルの画面上の位置を取得できるようにします。ピクセルシェーダの一行目で画面上の位置にinvVPを掛けることで画角とカメラの方向が反映された視線方向を求められます。invVPはView行列とProjection行列を掛けたものの逆行列です。
ここから先はcube mapとphotosphereで処理が変わりますが、キューブマップの場合は視線ベクトルから色を求めるのはたったの1行です。キューブマップは元々三次元ベクトルから色を取得するものなので当然ですね。それに対し、Photo Sphereは三次元ベクトルをEquirectangularで投影した二次元テクスチャの座標に変換する作業が間に入ります。
このプログラムの三次元の座標系は、右が+x、上が+y、奥が+zと定義しています。フォトスフィア上の二次元の座標系coordX, coordYは、フォトスフィアの中心を(0,0)とし、左上を(-1,-1) 右下を(+1,+1)と定義します。便宜上の定義であって実際のテクスチャの座標ではないことに注意してください。

写真だと位置をイメージしにくいので、Equirectangular projectionされた世界地図を使って、実例で座標を指定してみます。
東経0度、北緯0度 =(0, 0)、ガーナ南方の赤道上の地点、ここは視線ベクトル(0, 0, 1)になります。
東経0度、北緯45度 =(0, 0.5)、フランスのボルドー、ここは視線ベクトルは(0, 0.7071, 0.7071)です。
西経90度、北緯45度 =(-0.5, 0.5) ウィスコンシン州、ここは視線ベクトルは(-0.7071, 0.7071, 0)です。
こうして数値を見比べてみると数式が見えてこないでしょうか。
coordXは視線ベクトルのうちy成分を無視して、xとzからatan2で角度を求めればそれがそのまま経度になります。
coordYは視線ベクトルy成分からのみ算出します。どちらも-1から1の範囲ですが、経度は球面上の距離なのでasinで変換します。
最後に、coordX, coordYは実際のテクスチャ座標系ではないので0から1の範囲に変換してテクスチャを読み込みます。

これにPhoto Sphere XMP Metadataと呼ばれるメタデータが埋まることで、対応アプリから認識されます。非対応アプリからは普通のJPGファイルです。
全方向画像と言えば、ゲーム開発で主に使われるのはcube mapです。実際、cube mapで遠景を描いているゲームも多いと思いますが、フォトスフィアビューアでやる事とほとんど同じです。各シェーダの全文の例を掲示します。何をしているかというと、各ピクセルの視線ベクトルを求め、その方向の色をcubemap又はPhoto Sphereから読み取っています。見ての通り頂点シェーダ(mainVS)からピクセルシェーダ(mainPS)の第一行目まで全く同じです。
C++からは頂点は4つ出力しています。頂点バッファをバインドしない代わりに頂点シェーダで画面の四隅の位置を設定して全画面を塗りつぶします。また、pos2をラスタライザに渡してピクセルシェーダからピクセルの画面上の位置を取得できるようにします。ピクセルシェーダの一行目で画面上の位置にinvVPを掛けることで画角とカメラの方向が反映された視線方向を求められます。invVPはView行列とProjection行列を掛けたものの逆行列です。
ここから先はcube mapとphotosphereで処理が変わりますが、キューブマップの場合は視線ベクトルから色を求めるのはたったの1行です。キューブマップは元々三次元ベクトルから色を取得するものなので当然ですね。それに対し、Photo Sphereは三次元ベクトルをEquirectangularで投影した二次元テクスチャの座標に変換する作業が間に入ります。
このプログラムの三次元の座標系は、右が+x、上が+y、奥が+zと定義しています。フォトスフィア上の二次元の座標系coordX, coordYは、フォトスフィアの中心を(0,0)とし、左上を(-1,-1) 右下を(+1,+1)と定義します。便宜上の定義であって実際のテクスチャの座標ではないことに注意してください。

写真だと位置をイメージしにくいので、Equirectangular projectionされた世界地図を使って、実例で座標を指定してみます。
東経0度、北緯0度 =(0, 0)、ガーナ南方の赤道上の地点、ここは視線ベクトル(0, 0, 1)になります。
東経0度、北緯45度 =(0, 0.5)、フランスのボルドー、ここは視線ベクトルは(0, 0.7071, 0.7071)です。
西経90度、北緯45度 =(-0.5, 0.5) ウィスコンシン州、ここは視線ベクトルは(-0.7071, 0.7071, 0)です。
こうして数値を見比べてみると数式が見えてこないでしょうか。
coordXは視線ベクトルのうちy成分を無視して、xとzからatan2で角度を求めればそれがそのまま経度になります。
coordYは視線ベクトルy成分からのみ算出します。どちらも-1から1の範囲ですが、経度は球面上の距離なのでasinで変換します。
最後に、coordX, coordYは実際のテクスチャ座標系ではないので0から1の範囲に変換してテクスチャを読み込みます。
Subscribe to:
Posts (Atom)