以前にマーチングキューブ/四面体を実装して、IsoSurfaceをレンダリングしました。これは機能しましたが(YouTube)、ビューの距離に基づいて可変の詳細レベルを実装する(または古い、遠いチャンクを削除する)ことを回避できなかったため、パフォーマンスは悲惨でした。
今回はもう一度やってみることにしました。Build()
が呼び出されたときに次のように機能するOctreeNodeを作成することから始めました。
- チャンクが小さすぎてビルドできない場合は、すぐに戻ります。
- サーフェスがこのチャンクのボリュームを通過する場合は、ワークアウトしてください。
- その場合は、LODを上げるかどうかを決定します(カメラが近いため)。
- その場合、8つの子を生成し、同じプロセスを呼び出します
- そうでない場合は、現在のノードの寸法を使用してメッシュを構築します
いくつかのPseudoCode:
OctNode Build() {
if(this.ChunkSize < minChunkSize) {
return null;
}
densityRange = densitySource¹.GetDensityRange(this.bounds);
if(densityRange.min < surface < densityRange.max) {
if(loDProvider.DesiredLod(bounds)² > currentLoD) {
for(i 1 to 8) {
if(children[i] == null) {
children[i] = new OctNode(...)
}
children[i] = children[i].Build();
}
} else {
BuildMesh();
}
return this;
}
}
densityある時点での密度を返すだけでなく、密度ソースは、特定のボリュームの可能な密度範囲を決定できます。
²LoDプロバイダーは、境界ボックスを取り、カメラの位置/錐台、ユーザー設定などに基づいて、最大の望ましいLoDを返します。
だから...これはかなりうまくいきます。単純な球を密度ソースとして使用し、すべてのノードを表示します。
そして葉だけ:
ただし、いくつかの問題があります。
- 初期バウンディングボリュームを定義する必要があります(それが大きいほど、実行する必要のある処理が多くなります)。
- ツリーのルートでは、葉の深さがどれほどかわからないので、LoD番号付けは最低品質(ルート)から始まり、チャンクが小さくなるにつれて増加します。LoDは最初のボリュームに対して相対的になっているので、特定のサイズ/品質で物事をやりたいときはあまり役に立ちません。
私はいくつかのオプションを考えましたが、どちらにも欠陥があるようです:
- オクトリーのコレクションを維持し、距離に応じて追加/削除します。どのようにうまくメッシュするかがわかりません¹さらに、特に任意の3Dサーフェスが必要な場合(空のボリュームが繰り返し計算されるのを避けるため)には、既知の空ノードのリストが必要になります。
- 親ノードを現在のルートに追加してから、元のノードに7つの兄弟を追加します。これは機能し、オンデマンドですが、プレイヤーがランドスケープを移動するときに、慎重に縮小するのは複雑に見えます。また、LoD番号の意味がさらに減ります。
¹[以下のQについて説明します]現在、ツリー内の2つの物理的に隣接するノードが異なるLODにある場合、メッシュが生成されるときに継ぎ目がないように頂点を強制するコードがあります。周囲の複数のノードの密度を知ることでこれを行うことができます。2つの独立したオクツリーが並んでいるシナリオでは、この情報を取得する簡単な方法がないため、継ぎ目が生じます。
これに取り組むための最適な方法は何ですか?