Friday, August 21, 2015

HLSLでフォトスフィアをLittle Planetに変換する

スマホで撮った近所の公園のフォトスフィアからLittle PlanetとかTiny Planetと呼ばれている絵ができました。



ソース画像となるフォトスフィアとはGoogleによる呼称で、「360度パノラマVR」と呼ばれたり「全方位パノラマ」と呼ばれたり呼称が統一されていない様です。最近は「HDRi」で検索すると海外の素材集がいっぱいでてきますね。投影法はEquirectangular Projection(正距円筒図法)といいます。ここではフォトスフィアで統一します。

また、結果物であるLittle Planetのような投影法をStereographic Projection(ステレオ投影)といいます。

今回の目的は、HLSLによってEquirectangular Projectionされた画像をソースとしてStereographic Projectionした画像を得ることです。 まず、簡単のためにキューブマップをソースにしてやってみます。



float4 mainPS(VsToPs inp) : SV_Target
{
float2 scale = float2(4.0f / 3.0f, 1) * 3; // scale & aspect ratio
float2 plane = inp.screenPos.xy * scale;
float3 dir = float3(plane.x * 2, plane.y * 2, -1 + dot(plane, plane)) / (1 + dot(plane, plane));
return texCube.Sample(samplerState, dir.xzy); // y is upper
}



scaleはアスペクト比と、Little Planetの大きさを決めます。ここは見た目が良くなるように適当に数値を決めればよいところです。
キューブマップのフェッチに使うdirはWikipediaのステレオ投影で紹介されている立体射影の逆写像の式そのままです。実際にフェッチの段階でyとzを入れ替えるのは、キューブマップのY方向が上下になっているからです。どうスィズルするかによってLittle Planetの中心をどこにするか決められます。

次に、フォトスフィアをソースにしてやってみます。冒頭の絵はこれで生成します。

float4 mainPS(VsToPs inp) : SV_Target
{
float2 scale = float2(4.0f / 3.0f, 1) * 3; // scale & aspect ratio
float2 plane = inp.screenPos.xy * scale;
float3 dir = float3(plane.x * 2, plane.y * 2, -1 + dot(plane, plane)) / (1 + dot(plane, plane));
dir = dir.xzy; // y is upper
float longitude = atan2(dir.x, dir.z) * (180 / 3.14159265f);
float latitude = asin(dir.y) * (180 / 3.14159265f);
return gTexture.Sample(samplerState, float2(longitude, latitude) / float2(360, -180) + 0.5);
}


前半部はキューブマップと全く同じで、dirを使ってフォトスフィアのどこからフェッチするかの計算が加わりました。まず、三次元ベクトルのdirを経度(longitude、-180~180)と緯度(latitude、-90~90)に変換します。そうすると経度と緯度がフォトスフィアのX方向とY方向に対応します。あとはテクスチャUVである0~1の範囲に変換すれば完成です。

No comments:

Post a Comment