PC用眼鏡【管理人も使ってますがマジで疲れません】 解約手数料0円【あしたでんき】 Yahoo 楽天 NTT-X Store

無料ホームページ 無料のクレジットカード 海外格安航空券 ふるさと納税 海外旅行保険が無料! 海外ホテル

log

2014.06.15 C++:Parallax Occulusion Mapping

ファイル 440-1.pngファイル 440-2.pngファイル 440-3.png

視差マッピングと視差閉鎖マッピングの手法はネットで調べてみると
色々な手法があり、正直どれを使うのがベストなのか分からなかったので
今回は視差マッピングと視差遮蔽マッピングの各種手法の実装とそれらの
比較検証を行ってみました。

まずは視差マッピングの各種手法については以下の通りですが、これらの
違いはOffsetベクトルの求め方が若干違うだけであり、基本的には以下の
式で共通となっています。
UV = srcUV + Height * Offset;
・Parallax Mapping : Offset = E.xy / E.z;
・ParallaxMappingWithOffsetLimiting : Offset
 = normalize(E).xy;
・ParallaxMappingTakingIntoAccountTheSlope : Offset
 = normalize(E).xy * tex2D( s, srcUV ).z;
Eはタンジェントスペースの視線ベクトル(正規化前)でsは法線マップのサンプラとします。
品質はParallax Mapping < ParallaxMappingWithOffsetLimiting <= ParallaxMappingTakingIntoAccountTheSlopeの順。
個人的にはParallaxMappingTakingIntoAccountTheSlopeのtex2D()部分は
Heightと同じような意味合いなので逆に無駄なのではないかと思っていますが。
まぁ、詳しくは分かっていないです苦笑。

続いて視差遮蔽マッピングの手法についてですが、これは基本的には視差マッピングの
サンプリングをN回loopさせるという点では共通となっています(逆に言えば視差マッピング
は視差遮蔽マッピングの1回サンプリング版とも言えます)。
for() UV += Offset * Height;
・IterativeParallaxMapping : 前回のHeightと現在のHeightの差分による
 DeltaHeightを利用してUVを加算する手法。
・BinarySearch : InUV,OutUV,MinH(InUVのHeight),MaxH(OutUVのHeight)を
 用意し、MinH,MaxHの中点のハイトMHとサンプリングハイトHを比較。
 ( UV = lerp( InUV, OutUV, MH ) )
 HがMinHより小さい場合はMinHをHに更新、
 HがMaxHより大きい場合はMaxHをHに更新。
 これをN回繰り返してUVをずらす。
・LenearSearch : 単純に線形OffsetのRayMarch。
 ATIの視差閉鎖やネットのサンプルの多くはこれがベース。

画像は上からIterativeParallaxMapping,BinarySearch,LinearSearchの順です
(サンプリング数は全て8)。
BinarySearchの説明が長たらしくて分かりにくいかもしれませんが苦笑、次は
各手法の利点欠点を挙げたいと思います。
・IterativeParallaxMapping : 良くも悪くも隣接サンプリング位置の凹凸勾配に
 よって品質が左右される。画像ではブロック状の凹凸なのでLenearSearchとあまり
 違いは無いですが、滑らかな凹凸画像だと斜面部分の品質がLinearSearchより
 高くなります。
・BinarySearch : 画像を拡大すると分かりやすいですが斜面部分の品質が
 最も高くなります。これはサンプリングステップが1/2,1/4,1/16,...といった感じで
 どんどん細かくなっているからなのですが、最初のステップ数が1/2とかなり大きいため
 隣接の凸山部分が間違ってサンプリングされるケースが 他の手法よりかなり高いです。
 画像では凹部分が不自然に明るくなっている箇所がありますが、おそらくは
 これが原因と思われます。この問題はMaxHeightを小さくすることで解消できます。
・LinearSearch : 線形なので斜面のSliceもちょうどサンプリング数と同じとなる。
 品質の見積もりがしやすい反面、逆に言うとサンプリング数を上げないと他の
 手法より品質が下がって見えてしまう。

・Slice Quarity(斜面品質) : BinarySearch >>> IterativeParallaxMapping > LinearSearch
・Over Shift Problem(隣接凸をサンプリングする問題) : 同上
こんなところでしょうか。
結論からいうとMaxHeightが小さい場合はBinarySerchを使い、MaxHeightが
高くて滑らかな凹凸の場合はIterativeParallaxMappingを使い、MaxHeightが
高くて凹凸も激しい場合はLinearSearchを使うようにするのが理想かなと思いました。
(理論上の話であって、上の画像で比較すると凹凸が激しくてもIterativeParallax
Mappingの方が品質が上に見えるんですよね…うーむ。)

また、他にはSphereTracingやConeStepといった手法がありますが、これらは
Heightの他にDistanceFieldをテクスチャに格納して隣接ジオメトリまでの距離を
元に次のStepまでのサイズを決めるようになっています。
DistanceFieldが必要なのでまだこれらの手法は試していませんが、斜面の品質は
ConeStep>SphereTracing>BinarySearchの順になるのではないかなと
勝手に予測しています笑。
またDX11とかだと"細けえこたぁいいんだよ。男は黙ってTessellation+DisplacementMappingだ!!"ですかね。

追記:
説明が抜けていたため補足。画像の斜面部分が黒いものとそうでないものがありますが、
これは最終的なUVでHeightをサンプリングし、その高さに応じて画像を暗めに
設定しているためです(個人的な設定であり疑似AO用です)。

追記2:
参考資料のURLも載せないと分かりにくいと思われるので追加。
ttp://sirkan.iit.bme.hu/~szirmay/egdisfinal3.pdf
ttp://www.cescg.org/CESCG-2006/papers/TUBudapest-Premecz-Matyas.pdf

追記3:
Quadtree Displacement Mapping
ttp://www.drobot.org/pub/M_Drobot_Programming_Quadtree%20Displacement%20Mapping.pdf
・Distance Fieldによる視差遮蔽手法の一種(というより階層型POM?)。
・階層構造のHeight(MipMapに格納、ただしブロックの各Heightの内最も大きいもの
 を選択)を用いてMipLevel0から順にRayMarch。
・必然的にサンプリング数は用意したMipMap数と同じとなる。
・MipMap数が少ない場合はBlock状のHeightになるがSlice QuarityやOver Shiftに
 悩まされなくて済みます。
 ちなみに2048x2048の最大MipLevelは12。サンプリング数がこれよりも細かい
 視差閉鎖手法を使うくらいならこの手法を使った方が品質・速度ともこちらの方がよい。
・手法上、階層HeightはサンプリングフィルタをOFFにしなければならないと思われる。
 そのためサンプラは最終出力用と合わせて2つ必要。
・以外に使い勝手が良くて素晴らしい手法な気がします。