惑星を生成します
惑星を生成する最も簡単な方法の1つは、ノイズを使用することです。 選択する場合、いくつかの選択肢があります。 それぞれを見て、最良のものを決定しましょう。
- Perlin Noiseが最も簡単なオプションです。 パーリンノイズは1983年にケンパーリンによって開発されましたが、いくつかの欠点があります。視覚的なアーティファクトと、他のオプションに比べて大きな画像を生成するときの速度です。
- シンプレックスノイズ(シンプレックスノイズ) -パーリンノイズの欠陥を排除するために2001年にケンパーリンによって開発されました。 これは非常に価値のある迅速なソリューションですが、重大な欠点があります。3次元シンプレックスノイズの使用は特許によって保護されているため、非常に高価になります。
- オープンシンプレックスノイズ -KDotJPGは1つの単純な目標で開発されました。シンプレックスノイズの比較的高速で歪みのない最新の無料バージョンを作成することです。
これら3つのうち、個人的なプロジェクトで使用するOpen Simplex Noiseを個人的に好みます。 現在のOpenSimplexNoise実装では、スケール、オクターブ、ジェネレーターに簡単にアクセスするには追加の作業が必要になることに注意してください。 これらの各要素が何をするかについて、インターネット上には多くの情報がありますので、勉強することを強くお勧めします。 ただし、私の記事ではこれについては説明しません。
以下は、16オクターブのOpen Simplex Noiseの外観です。
シームレスノイズ
ノイズは無限です。つまり、 等間隔の投影を得るためにアスペクト比2:1のキャンバスを作成した場合、球に重ねてもループしません(この素晴らしいWebサイトに感謝を表します)。大きな違い。
継ぎ目なしで作成されたノイズ。
ノイズが球に適用されたときに現れる巨大な縫い目に注意してください。
これを修正するには多くの方法があります。 たとえば、 Red Blob Gamesのこの優れた投稿 [Habréの翻訳 ]では、中心までの距離を変数として受け取り、縁の高さ0を設定して継ぎ目を最小限に抑える関数を使用して島を生成するだけで十分でした。
ただし、これは必要なものではありません。 北極と南極が存在する可能性のある惑星を生成したいため、より複雑な数学的計算が必要になります。
球面オーバーレイ
球面惑星を生成できる方法は、キャンバスのデカルト座標を球面座標に変換し、これらの座標に基づいてノイズを生成し、次にノイズをデカルト座標に変換してキャンバスに適用することです。
ただし、この実装には制限があり、その原因はRon Valstarによる驚くべき投稿に示されています。 最も重要なことは、この場合の大陸の形式は非常に奇妙で歪んでいるように見えるため、このオプションを使用しないことです。
ノイズの球面重ね合わせ。 奇妙な形と歪みにより、大陸はかなりいものになります。
しかし、少なくとも継ぎ目はありません。
キュービックオーバーレイ
その結果、Ron Valstarの投稿とacko Making Worldsの一連の記事から取った2番目の方法を使用しました。 彼らは、球の形の例になるまで、まるで風船であるかのように、立方体とその「インフレーション」の生成を通じて地球の生成を説明します。
acko.netからの画像。 立方体マップの概念を、視覚化された簡単な方法で説明します。
ここで、6つの面を生成する必要がありますが、これは非常に簡単です。これを行うには多くの方法があります。
最終的に、配列を作成し、データを入力することにしました。 キャンバスの2D座標を立方体の3D座標に変換し、これらの3D座標ごとにノイズを生成して、対応する2D座標値に保存しました。
//Z STATIC for(int y = 0; y < cubeFaceSize; y++) { for(int x = 0; x < cubeFaceSize * 2; x++) { //Generates FRONT if(x < cubeFaceSize) { cubeMap[cubeFaceSize+x][cubeFaceSize+y] = noise.noise3D(x, y, 0); } //Generates BACK else { cubeMap[cubeFaceSize*3+(x-cubeFaceSize)][cubeFaceSize+y] = noise.noise3D(cubeFaceSize-(x-cubeFaceSize), y, cubeFaceSize); } } } //X STATIC for(int y = 0; y < cubeFaceSize; y++) { for(int x = 0; x < cubeFaceSize * 2; x++) { //Generates LEFT if(x < cubeFaceSize) { cubeMap[x][cubeFaceSize+y] = noise.noise3D(0, y, cubeFaceSize-x); } //Generates RIGHT else { cubeMap[cubeFaceSize*2+(x-cubeFaceSize)][cubeFaceSize+y] = noise.noise3D(cubeFaceSize, y, x-cubeFaceSize); } } } //Y STATIC for(int y = 0; y < cubeFaceSize * 2; y++) { for(int x = 0; x < cubeFaceSize; x++) { //Generates TOP if(y < cubeFaceSize) { cubeMap[cubeFaceSize+x][y] = noise.noise3D(x, 0, cubeFaceSize-y); } //Generates BOTTOM else { cubeMap[cubeFaceSize+x][cubeFaceSize*2+(y-cubeFaceSize)] = noise.noise3D(x, cubeFaceSize, y-cubeFaceSize); } } }
このようにして、 Bartoszによって作成されたすばらしいコードを使用して、等間隔の投影に簡単に変換できる3次マップを作成できます。
アルゴリズムによって生成されたキュービックマップ。
3次マップの等距離変換。
maptoglobe.comでレンダリングされた立方体マップグローブ。
ご覧のように、等距離マップははるかに美しい形状をしており、球体に重ね合わせると、球体の重ね合わせに似た結果を作成しますが、すべての欠点はありません。 ちなみに、等距離射影は、さまざまなプログラム( NASA G.Projectorなど)を使用して 、ほぼすべてのタイプのカードに簡単に変換できます。
結論として
惑星全体を生成するのは困難な作業のように思えます。正しく使用するとノイズは非常に強力なツールですが、何世紀にもわたって直面している問題があります。たとえば、歪みを最小限に抑えて2Dキャンバスに地球を重ねます
私が提案した解決策は、構造プレート、川、島の鎖、さらには山さえも考慮に入れない非常に大雑把に生成された惑星を作成するため、より複雑なシミュレーションの基礎としてのみ使用できます。
実際、特定の値の範囲で値のマトリックスのみを作成します。 グレースケール画像の場合、これは0〜255です。 次に、値は、グレースケールの最初の画像に似た画像を作成するピクセル、または実際の高さの違いをシミュレートするために-11000から8000の範囲の画像に変換され、その後、ピクセルは高さの間隔(たとえば、0からの値5は海岸をシミュレートするために砂の色で塗られています) 。
宇宙の構築において、神はより高いレベルの数学を使用しました。
-ポール・ディラック