テレワークならECナビ Yahoo 楽天 LINEがデータ消費ゼロで月額500円〜!
無料ホームページ 無料のクレジットカード 海外格安航空券 海外旅行保険が無料! 海外ホテル


log

2014.08.13 C++:Procedural sky

ファイル 448-1.jpg

今までは手書きテクスチャによる空の表現だったのですが、あまりクオリティがよろしく
無かった事と動的なシーン変化に対応しづらい事もあり今回はプロシージャル生成に
よる空表現を実装してみました。

またAtmospherical Scatteringについても今回改良を加えたので各時間帯の
スクリーンショットを撮ってみました。夕方(一番下)以外はそれなりに上手く出来た
ように思います。

夕方が不自然なのはライトの色が白色のままなのが原因なのですがキーライトの
色をどう求めるかで現在試行錯誤中だったりします苦笑。

2014.07.19 C++:The Last of Us的なAOその3

TLoUAO第3弾。今回はメリット、デメリットについてまとめますが、とりあえず正式名称
が分からず面倒なのでTLoUAOと省略します笑。

TLoUAOのメリット
・ざっくり挙げるならば"綺麗"。
・SSAOのようにサンプリング距離の制限が無い。

TLoUAOのデメリット
・良くも悪くもDraw数はAOSphereの数に比例する。
・Self AO(自身のAOSphereによる間違ったAO)を除去するのが難しい。

Self AOについてですが、自身のジオメトリをすっぽりと覆うようにAOSphereを生成
した場合、単純にTLoUAOを計算すると全てAOとして判定されるので真っ黒になって
しまいます。これを防ぐための手法としては自分が思いつく限り挙げると以下の通りです。

・自身のSelfAO(+隣接のSelfAO)を考慮しないようにする。
・AO計算時のSample座標を球中心からSample方向に半径サイズ分ずらす。
・AO半径をジオメトリを覆わないように調節。

1つ目は自分がまず始めに思いついた手法ですが、この場合"自身のSelfAOかをどう
やって判断するか"が必要となるためDeffered Renderingに向かないデメリット
があります。そこでVolumeAOを元にして考えたのが2つ目の手法となります。
3つ目も後で思いつきましたが、3は根本的にSelfAOが弱くなるだけで除去ではないので
Biasパラメータ的な位置づけです。

シーンにもよるので一概に言えないですが、概ねのケースでは2つ目が正解でいい
気がします。ただしTLoUのスライドにはこの辺りの実装がまったく載ってないため、
この辺の改善案については完全にオリジナルです。「実装したけどうまくいかねーゾ!」
っていう苦情を出したところで自分からの案はこれ以上でないのであしからずです笑。

2014.07.06 C++:The Last of Us的なAOその2

ファイル 446-1.jpgファイル 446-2.jpg

前回は球によるAOしか対応できませんでしたが今回はちゃんとTLoUと同じように
スケーリングされた球に対応してみました。

具体的な実装方法は以下のような感じにしています
・ジオメトリと最も近い線分の点を用いてAmbient Term
・ライトベクトル方向と最も近い線分の点を用いてDirectional Term

ようするに点と線分の最短距離、ラインと線分の最短距離を求め、その点をAO中心と
して球の時と同じように計算するといった感じになります。
正確にはスケーリングされた球ではなくカプセルのAOになるためTLoUとは実装が
違うと思われます(おそらくTLoUの方が簡略化されている、はず)。

それと正確な手法ではなく、最短距離の点以外からのAOが考慮できていないため、
画像をよく見ると分かりますがAmbient TermとDirectional Termの
つなぎ目部分が若干おかしいです。最短距離の点以外のAOも考慮するために
最短距離の点が線分のどの位置にあたるか調べ、中心になるほど球の半径を大きく
することでそれっぽくなる気がしますが…まだ試していないのでそれっぽくならないかも
しれません笑。

追記:
つなぎ目のズレはDirectional Termの計算が若干おかしいことが原因でした。
ということで、ちゃんと計算した画像も追加(2枚目)。

2014.07.01 C++:The Last of Us的なAO

ファイル 445-1.jpgファイル 445-2.jpg

前からThe Last of UsのAOが綺麗でいいなぁと思っていたので、今回はThe Last of Us
の手法を自分なりに真似て実装してみました。

詳しく説明すると、以下のような感じです。
(The Last of Usと同じかどうかは分かりませんのでご了承下さい)
・SSAOのようにスクリーンスペース深度から求めるのではなく、ジオメトリを球に近似し
その球からの遮蔽率を元にAOを計算。
・遮蔽率はサンプルから見た立体角を考慮して計算。
・遮蔽計算は半球全体の遮蔽(Ambient Term)とライト方向の遮蔽(Directional
 Term)の2つに分かれます。

こんな感じの実装となります。画像はこの手法のAO以外にSSAOとデカールによる
シャドウもごっちゃに表示されているので分かりにくいですが、キャラクター付近に
斜めに謎の影が落ちているのがこの技法によるAOになります。

ちなみにThe Last of Usと違いスケーリングされた球(楕円)にはまだ対応していま
せんが、これはネットで調べても情報があまり載っておらず、球の実装だけで四苦八苦
してしまったためです笑。

ある程度見ただけでソースコードの理解が瞬時にできるようになりたいなあ。

追記:
45度付近からのライトを想定して作成しているのですが、明らかにDirectional
TermのAOが伸びすぎだったため修正(2枚目の画像)。TLoUのスライドを
ちょっとマネして各AO項で色分けしてみました。
Directional Termによる影が弱い(ボケすぎている)のでちょっとまだ正確さが
足りないと思われますが、これである程度はTLoUに近くなりました。

2014.06.28 考察:メジャーなリアルタイム手法まとめ

メジャーなリアルタイム手法を自分なりにまとめてみました。
(ただし自分があまり詳しくないマイナーな手法等は除いています)

[Diffuse]
・Lambert
・Burley
・Ashikhmin-Shirley
・Oren-Nayar

[NDF]
・Blinn-Phong
・Beckmann
・GGX

[Anisotropic]
・Ward
・Ashikhmin-Shirley
・GGX Anisotropic
・Kajiya-Kay
・Merchner

[Frenel]
・Schlick Frenel
・CookTorrance Frenel

[GI]
・SH2次orMore(頂点orテクスチャベイク)
・SH1次(ボクセル)
・AmbientCube
・HemiShpereLighting
・RadiosityNormalMap
・LightMap

[HDR]
・RGBE
・RGBD
・RGBM
・LogLUV

GeometricShadowingについては数が多い上にどれが普及しているのかよく分かって
いないので明記しませんでした。というより、多くのゲームではGeometricShadowing=1
で計算していると思われます。

ちなみに何故こんなまとめを行ったのかというと、自分が最近になってようやくこれらを
実装(というより勉強)したからなのですが。理論については半分も分かっていない
かもしれません苦笑。色々一度に調べすぎて、、頭がこんがらがりそうです。

1個使えば後は必要ないといわれそうですが、これらの知識が理解できることで
昔読んで理解できなかった記事等が理解できるようになったり、現在のシーンでの
最適な手法を選択できたりするので覚えておいて損は無いなと思いました。

2014.06.27 C++:法線マップの仕込み

ファイル 443-1.jpg

見栄えをよくするために、ようやくですが法線マップを仕込中です。

ちなみにスカルプトから法線マップを生成するのではなく、昔ながらのバンプマップを
生成しそこから法線マップを生成するといった感じで作っています苦笑。

実は今まで修正すれどもすれども"これでよし"な感じにならないでいたラビなのですが
法線マップを入れることで何だかいい感じになった気がします。
リニアワークフローにしてもそうですが、やっぱり色々な角度から試さないと分からない
もんだなと思いました。

2014.06.25 その他:スパム対策を行った結果…

この日誌ではずっと前から何故か海外からのコメントスパムが絶えないので
その応急処置として古い記事にはコメントを書き込めないようにしたり、コメントの
連続書き込み禁止時間を長めに設定したり、まぁ色々と工夫してようやく平和な
感じになったのはいいのですが。。
どうやら管理人以外からは古い記事のコメントが見えなくなってるということに
今更気づきました苦笑。うわぁ。

なので新しめの記事と今後の記事はコメントが見えるように設定し直すことにします。
後はプロクシ経由のコメントは禁止等、設定を見直しました。
コメント書き込んだのに禁止になるぞゴルァという方がいましたらすみませんです。

というよりスパムが悪いんですよ、止めろよホント。Botだろうし言ってもしょうがないけど。

2014.06.23 C++:リニアワークフロー

ファイル 441-1.jpg

今回はリニアワークフローの実装を行ってみました。
リニアワークフローについてはここで説明するほどのことでは無いかもしれないですが
とりあえず簡単にだけ説明します。

・まず一般的に使われているモニタはsRGB(ガンマ2.2)です。これは以下のように
 入力された色データはガンマ補正されて出力されているということです。
 Out = pow( In, 2.2 );
・普段Photoshop等で画像を作成した場合、このガンマ2.2のモニタ上で見ながら
 作業を行うため、シェーダ上でそれらの画像を扱う際は当然同じ色味として扱わなけ
 ればなりません。つまり入力値0.5のピクセルは明るさpow(0.5,2.2) = 0.217..と
 して扱う必用があるということです。
・最終的にはモニタと同じガンマ値に戻す必要があるので以下の補正を加えます。
 Out = pow( Color, 1/2.2 );

以上のような感じです。
ただしsRGBで途中計算をしていたものと同じパラメータでリニアで計算するように変更
した場合、かなり画面が不自然になってしまうためパラメータも一部変える必要が
出てきます(自身の場合だと明るい部分をライト色になるように補正を加えているのですが
その補正がかなり効きすぎる感じになったので補正を弱めたりしました)。
これによってフレネルやスペキュラがいい感じに入るようになってくれたのはいいですが
現在は法線マップをほとんどのモデルで使っていないので不自然にフレネルが入ったり
スペキュラが入るべき位置に入っていなかったりしています。

さて、時間がかかるので後回しにしてましたが、法線マップとスペキュラマップを今後
用意しないとライティングの検証がしづらくなってきました。うーむ。

追記:
モニタには何故ガンマ特性があるのか
ttp://www.denjuku.org/old/hayakawa/gamma.html

"元をたどればリニア(直線的)な階調はリニアに感じられないという、人間の眼の感受
特性に端を発しているのですが、同様に各々の出力機器に応じた固有のガンマ特性
を考慮しないと、視覚的に正しい階調を得る事ができないのです。"(記事抜粋)

つまりPhotoShopでリニアのグラデーションの画像を作成してもそれはモニタが人の
目にリニアで見えるように勝手に補正してくれているだけであり、各ピクセル間の勾配は
"エネルギー的には"リニアでないため、物理学的に正しく光の計算をシェーダで行う前に
一旦リニアに戻す必要がある、とまぁそういう訳ですね。ようやくすんなりと理解できる
ようになりました。
これを踏まえるとOutputもリニアのままでも物理的には間違ってないような気がしますが
これだとテクスチャ作成時の見た目と実行時の見た目が結構変わってしまうため
デザイナーさんが困るとかそういうことなのだと思われます。

ようやく理解できてよかったですが、今までは「モニタがガンマ補正してるだけだから
データ的にはリニアなんじゃないの?」とか悩んだりしてたんですよね笑。

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つ必要。
・以外に使い勝手が良くて素晴らしい手法な気がします。

2014.04.20 C++:経路探索その2


前回の経路探索ではナビゲーションメッシュの各ポリゴンをノードとしたダイクストラ法に
よる手法でしたが、今回はスタート、ゴール、凸壁頂点をノードとしたA*による手法で
実装してみました。

以前はラビが妙な方向に進みながら対象方向に向かっていましたが、今回はちゃんと
最短経路を進むようになっています。…ただ、動画では経路探索でよく用いられる迷路
のような地形ではないため実際に最短経路を進んでる様子が分かりにくいですね苦笑。

他には高さを考慮した手法等が残っていますが、経路探索はこれで一旦終了にしようと
思います。それと前回記事を書いた時点では「他のキャラクターによって経路が遮られて
いる場合はどうするんだろ?」と疑問に思っていたのですがこの場合、キャラクターの
コリジョンサイズと同じかそれより大きいサイズのバウンディングボックスやConvex
Hullを考え、そのコリジョンの凸角を経路探索のノードとして追加することで解決できるな、
と思いました。…結局まだ試していないのであやしいですが。

ところで、GTAやMMO系のようなワールドサイズが広大なゲームの場合、経路探索の
コストがとんでもないことになりそうですが、どうやって最適化してるんだろう。。
ネットで調べてみると、エリアを「AABBで分割してうまく最適化してるぜ!」みたいな
ことが書かれていますが、うーむ分かりそうで…分からない苦笑。
そもそも離れすぎている対象に対しての正確な最短経路の移動は逆に不自然になるので
2ブロック以上離れたAABBエリアへの経路探索は行わないようにする、とかそういう
ことなのかなあ。

追記:
凸角をノードとして経路探索を行う手法はどうやら一般的にはJump Point Searchと
呼ぶようです。自分で思いついた手法なのですが、やはり先人がいました笑。