2012/07/21

XNA Infinite Terrain アップ

実行時地形生成の実装を一段落させました。





ソース コードは以下にあります。
GitHub: TestXna / TiledTerrainDemo (tag 20120721)
※ソースコードを大幅に変更しました。下記ページを参照してください。
Blocks Project: XNA Infinite Terrain 改
ソース コードには、複数のノイズ生成および加工方法を組み合わせて height map を生成するためのテスト コードも色々と含まれており、とても見辛い状態であると思いますが、もう面倒なのでそのままにしています。

動画での設定は以下の通りです。
  • 解像度: 1280x720
  • 60fps (録画 30fps)
  • 各 height map: 1025x1025
  • Far plane distance: 300000.0f
  • フォグ範囲: 20000.0f - 30000.0f
  • 地形ロードに使用するスレッド数: 4
  • 地形生成方法: Improved Perlin noise + sum fractal (octave 7)

遠方の描画がちらつきますが、実際にはより手前でフォグをかけるのだと思います。しかし、あまり手前でフォグをかけてしまうと、遠方で地形が動的に生成される様を見せることができなくなるため、ちらつきが多いまま録画しています。恐らく、通常ならば far plane distance を 300000.0f
に設定することはないであろうと思います。

以下、ひとまず簡単な解説です。もう少し詳しく説明しようと思ってはいたのですが、今のところ非常に実装で疲れてしまい、「興味がある人はソースコード見ればいいじゃん!そもそも興味ある人いないかもしれないし!」へ傾きつつあります。

地形の動的ロードの枠組み

本題となる地形の動的ロードですが、やっていることは極めて簡単なものです。
まず、空間をロードの単位とするための仮想的な区画で区切ります。そして、カメラ位置からどの程度の距離まで近づいたらロードするか、および、どの程度の距離まで離れたらアンロードするかを定め、その距離に含まれる区画の位置を算出します。こうして、ロード範囲にある区画について、それが未ロードならばロードします。また、アンロード範囲から外れた区画について、ロード済ならばアンロードします。

基本はこれだけです。より効率の良い方法がありそうですが、他には特に思いつきませんでした。

Improved Perlin noise の採用

先日、Perlin noise が遅いため Simplex noise を実装しましたが (Simplex noise 実装と計測)、この動画では Improved Perlin noise で height map を生成させました。これは、Simplex noise は確かに処理が高速なものの、生成されるノイズがあまり滑らかではない印象を受けたからです。

原点から大きく離れたカメラ位置での問題

数日、カメラ位置が原点から極めて離れた場合 (例えば x = 100000.0f くらい) に、根本的に描画がちらつく問題に手こずっていました。
どうも、カメラ位置の値があまり大きくなると、頂点を View * Projection で変換させた時に精度が大きく落ちるようで、最終的には各地形の位置を原点とした空間を考え、そこからの相対位置で演算することで解決しました。

基本的に、今回の CDLOD に関わるコードでは、地形の位置を原点とした地形空間で頂点を計算するように統一して修正しています。

しかしながら、カメラ~地形間の距離が離れすぎると結局は同じ問題が発生し、遠方にある地形の描画がちらつきます。これを直そうと思ったのですが、そう簡単に解決できる問題でもないと考え始め、放置することにしました。


地形間の繋ぎ目の問題

地形間の繋ぎ目の問題ですが、これは height map 上でのデータ表現と頂点データ表現とのギャップが原因です。

例えば、height map の右端の値は右端の頂点の高さになりますが、その隣の height map の左端は左端の頂点の高さになります。つまり、height map から生成される頂点データを並べる際には、右端の値は、その右に並ぶ height map の左端の値と一致していなければならないということです。これは、上下の並びについても言えることです。

僕のコードではノイズから height map を生成しているので、値の出力範囲を調整することで、上下左右が隣接する height map と重なるようにしています。

ただし、それだけでは、動的な height map 生成ではまだ問題が発生します。ある頂点の法線情報は隣接する頂点の高さから算出しますが、上下左右の境界上では隣接する頂点の一部を得ることができないため法線を正しく計算できず、ライティングを行うと陰影による繋ぎ目が発生します。

僕のコードでは、height map のサイズを上下左右を更に 1 つ分増やし、隣接する height map と 1 つ分だけ重なるように値を生成し、境界上にある法線の算出においても必ず隣接する頂点を得られるようにしています。
しかしながら、完全に繋ぎ目を消すことはできていません。CDLOD の設定次第では、非常にわずかな繋ぎ目が表れる場合があります。

※改善版では CPU 上で normal map を作成し、ロード状況に応じて境界上の法線の再計算を行なっています。
Blocks Project: XNA Infinite Terrain 改

0 件のコメント:

コメントを投稿

libgdx いじり

Google が提供している Java 版の Tango Examples は Rajawali をベースにしているため、自分が仕事で開発する Tango アプリも Rajawali ベースとしていましたが、最近は libGDX への移行を進めています。一応、要点については移行が...