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の範囲に変換してテクスチャを読み込みます。

No comments:

Post a Comment