フラクタル颚景を構築するためのダむダモンド正方圢アルゎリズム

Cartographで䜜成されたMinecraftマップ 倚くの人が非垞に珍しいMinecraftゲヌムに粟通しおいるず思いたす右はその䞭で生成されたマップの䟋です。プレむダヌは地球のほが無限の衚面にいお、最小限の制限で圌の呚りの䞖界を探玢できたす。



ゲヌムの䜜者であるノッチは、どのようにしお圌のランダムな「䞖界」を地球の広倧さに䌌せたものにしたのでしょうか このトピックでは、この皮の人工的なランドスケヌプを構築する方法の1぀を怜蚎したた、他のいく぀かの方法を簡単に説明したす、パフォヌマンスを著しく䜎䞋させるこずなくランドスケヌプのサむズを倧幅に増加できるこのアルゎリズムの小さな改善に぀いおも説明したす



内郚には、いく぀かのスキヌムず矎しい写真、かなりの数の文字、アルゎリズムの実装䟋ぞのリンクがありたす。





行動蚈画



䞀般的にランドスケヌプ生成ずはどういう意味ですか 実際にMinecraftなどのコンピュヌタヌゲヌムのレベルの䜜成ほがリアルタむムに぀いお話す堎合、このプロセスは次のポむントで構成されたす。

  1. 高さマップを䜜成したす 。 最初に、平坊な2次元グリッドを䜜成し、各セルに特定の高さを割り圓おたす。 どうやっお これに぀いおは埌で説明したす。 ちなみに、グリッドは長方圢である必芁はありたせん。たずえば、䞉角圢で構成されるグリッドの同様のアルゎリズムをここで説明したす 。 ただし、ほずんどの堎合、正方圢のピクセルセルで構成されるグリッドの方が䟿利です。 高さマップずずもに、氎で芆われた゚リアも定矩されたす-少なくずも海ず海通垞、特定のレベルより䞋にあるセルは氎になりたす。
  2. 高さず湿床によるバむオヌム分垃 バむオヌム分垃 。 ここでは、地理の知識が必芁になりたすただし、ランドスケヌプを䜜成するプロセス党䜓でこれが必芁です。 ツンドラがどこにあるべきか、砂挠がどこにあるか、そしお熱垯雚林がどこにあるかを決定するために、すでに䜜成された高さマップず、䟋えば、氎空間たたはいく぀かの所定のポむント赀道/極たでの距離が圹立ちたす。 次に、バむオヌムは他の倚くのパラメヌタヌを蚭定したす-䟋えば、草地、岩堎の数、怍物、川や湖の数など。
  3. 地球、海掋、海も䜜成し、地理的ゟヌンを分散させたした。 䜕を忘れたしたか もちろん、川をしたしょう  山から流れお海に流れ蟌む、たたは空掞に湖を圢成する氎自䜓に加えお、地衚面ぞの圱響を゚ミュレヌトする必芁がありたす-川底が圢成され、砂や柔らかい土壌が河川に沿っお運ばれ、湖や他の氎域のビヌチが圢成されたす。 残念ながら、他のタむプの䟵食ず同様に、 氎䟵食ず呌ばれるこのプロセスは、この蚘事の範囲倖にする必芁がありたす。 それでも、参考文献のリストには、このトピックに関する非垞に優れた資料ぞのリンクがいく぀かありたす。
  4. 远加のアクション 。 実際、景芳はすでに䜜成されおいたすが、改善できるものはただ倚くありたす。たずえば、Minecraftでは、地䞋空間が完党に自然の掞窟で芆われ、倚くの暹朚が衚面に生えおいたす。 さらに、もちろん、バむオヌムによっおは、動怍物を倚様化するこずが可胜です-目暙が珟実に起きおいるこずにできるだけ近づくこずである堎合。


最埌に、生成されたランドスケヌプを描画するだけで枈みたす。 ここで詳现を1぀だけ確認したす。䞊蚘の䞉角圢のグリッドは、3次元マップの埓来の芖芚化の堎合に圹立ちたすが、 ボクセルの䜿甚は正方圢のセルには非垞に圹立ちたす。 ただし、これはたったく別の話です。



高さマップを䜜成する方法



それでは、ランドスケヌプを構築する䞊で最も重芁な段階を芋おみたしょう。地衚の各ポむントの高さを決定したす。 最も䞀般的なアむデアは、䞡方の座暙を調べおマップ[x] [y] =ランダムにするこずです。奇劙なこずに、蚱容できる結果が埗られないため、もっずsomethingなものを䜿甚する必芁がありたす。



「手動で」䞘を䜜成する



かなり単玔なアルゎリズム最初から、すべおのポむントは同じレベルにあり、任意の堎所高さの異なる䞘に䜕らかの「バルゞ」を远加し始めるず考えおいたす。 慎重にこれらの䞘を互いの䞊に敷蚭した結果および、䞊蚘の少しのノむズが远加された可胜性がありたす、すでに真実に䌌たものを埗るこずができたす。 ただし、以䞋にリストしたアルゎリズムを䜿甚するず、はるかに珟実的なランドスケヌプを実珟できたす。そのため、「䞘」方匏に぀いおは説明したせん。



ボロノむ図に基づく颚景



実際、最近たで、次のアプロヌチは私にはたったく知られおいたせんでしたが、非垞に印象的な結果を出すこずができたこずに非垞に驚きたした。



ボロノむ図 それはすべお、誀っおマップ䞊にポむントを投げるこずから始たりたす。 次に、これらの点からボロノむ図 および、それに応じおDelaunay䞉角圢分割 が構築され、ロむドの緩和のいく぀かの反埩が実行されお、小さすぎるポリゎンが削陀されたす。



前の段萜が明確でない堎合-怖くない、その本質は右の図のようなグリッドに぀いお䜜成するこずになりたす。 その䞻な特性は䞍芏則性です。 これにより、基瀎に基づいお構築されたランドスケヌプが「正方圢」に芋えすぎないようにできたす。



結果ずしお生じる颚景 それ以䞊のアクションは簡単です。氎で満たすポリゎンをランダムに遞択し、残りのポリゎンに属するポむントの高さをsea-okiyanaたでの最短距離に等しくしたす。 同じノむズ高床自䜓ずポリゎンの境界の䞡方を远加するために残りたす-そしお、非垞に玠晎らしい島たたは芏暡に応じお本土を取埗したす。



私はこのアルゎリズムの実装を扱っおいたせんでした。そしお、蚘事を簡単にするために、いく぀かの詳现ず䞭間のものを省略したした。 詳现な蚘事写真ずほずんどの情報の出兞はここ 英語にありたす。たた、プロセスのすべおの段階を非垞に明確に瀺す玠晎らしいswfデモもありたす。



ダむアモンドスク゚アアルゎリズム



最も䞀般的で最も珟実的な結果をもたらすのは、1次元䞭点倉䜍アルゎリズムを2次元平面に拡匵したダむアモンドスク゚アたたはスク゚アダむアモンドアルゎリズムです。 その助けを借りお埗られた颚景は、原則ずしおフラクタルず呌ばれたすが、確かに、実際にはそれほど自己類䌌ではありたせん-反察に、以䞋で芋るように、あたり快適ではない特性は、倧芏暡に比范的滑らかになるこずです、小さなものは䞀皮のサンドペヌパヌに倉わりたす。



真倜䞭の倉䜍倜の地平線䞭点倉䜍アルゎリズム より単玔な䞭点倉䜍アルゎリズムから始めたしょう。 既に述べたように、2次元の平面ではなく、1次元のセグメント䞊で機胜したすしたがっお、たずえば、それを䜿甚しお氎平線を䜜成できたす。



このアルゎリズムがフラクタルに関連しおいるずいう事実は、その再垰的な動䜜です。 最初に、セグメントの䞡端の高さを䜕らかの方法で蚭定し、䞭倮のポむントで2぀のサブセグメントに分割したす。 このポむントをランダムな倀でシフトし、取埗したサブセグメントごずにパヌティションずシフトを繰り返したす。 など-セグメントの長さが1ピクセルになるたで。 これがアルゎリズム党䜓です右の図を参照。 ああ、はい-重芁な泚意ランダムな倉䜍は、パヌティションが䜜成されるセグメントの長さに比䟋する必芁がありたす。 たずえば、長さlのセグメントを分割したす-䞭倮のポむントは高さを持぀必芁がありたす

h = h L + h R / 2 +ランダム -R * l 、 R * l 

 h Lずh Rはセグメントの巊端ず右端の高さで、定数Rは結果のポリラむンの「粗さ」を決定し、このアルゎリズムの䞻芁なパラメヌタヌです。



二次元の䞭点倉䜍 このアルゎリズムを2次元の高さマップに䞀般化しおみたしょう。 たず、マップ党䜓の4぀の角にランダムな高さを割り圓お、それを䟿宜䞊、正方圢のマップで䜜業し、その蟺は2のべき乗であるず仮定しお4぀の等しい正方圢に分割したす。 それらのそれぞれで、角床の1぀の意味が知られおいたす。 残りはどこで入手できたすか 二次元の䞭点倉䜍の結果

1次元の䞭点倉䜍の堎合ず同様に、すべお同じ補間-䞭心の点は、4぀のコヌナヌ点すべおの高さを平均しお埗られ、倧きな正方圢の偎面の各䞭点は、察応する蟺の端にある1組の点を平均したす。 正方圢の蟺に比䟋する範囲内で䞭心点をランダムに䞊䞋にシフトするために、少しノむズを導入し、結果のサブ正方圢に察しお再垰的にアクションを繰り返すこずができたす。 それだけですか これですべおですが、すべおではありたせん。



これはただダむアモンドスク゚アではありたせん-このアルゎリズムは、原則ずしお、䞭点倉䜍アルゎリズムずも呌ばれたす。比范的蚱容できる結果が埗られるずいう事実にもかかわらず、結果の画像でその「たっすぐな」性質に簡単に気付くこずができたす。



ダむアモンドスク゚アアルゎリズムの進捗 ダむアモンドスク゚アアルゎリズム「実際の」フラクタルランドスケヌプを取埗できるアルゎリズムは、2぀のステップで構成されるずいう点で2次元の䞭点倉䜍ずは異なりたす。 最初は、いわゆるです。 「Square」-同様に、角床を平均し、実際の倉䜍を加算するこずにより、正方圢の䞭心点を決定したす 'a-ランダム偏差。 2番目のステップ「ダむダモンド」は、蟺の䞭倮にあるポむントの高さを決定するように蚭蚈されおいたす。 ここでは、2぀のポむント、぀たり「䞊」ず「䞋」垂盎偎のポむントに぀いお説明したすではなく、「巊」ず「右」のポむントのペア、぀たり「正方圢」のステップで埗られた2぀の䞭心点を平均したす。 前のステップで取埗したこれらの2぀の高さはすでにカりントされおいるこずに泚意するこずが重芁です。したがっお、蚈算は「レむダヌ」で実行し、最初にすべおの正方圢に察しお「正方圢」ステップを実行し、次にすべおの菱圢に察しお「ダむダモンド」ステップを実行し、より小さな正方圢に。



説明はわかりにくいかもしれたせんので、添付のスキヌムを泚意深く怜蚎するこずをお勧めしたす。各スキヌムでポむントの高さがどのように蚈算されるか、より明確にする必芁がありたす。



深さを歩くのではなく幅を歩く必芁があるこずに加えお、別の埮劙な点がありたす-颚景の端の状況です。 実際、ダむダモンドの段階では、アルゎリズムは珟圚の正方圢の倖偎にあるポむントの高さを䜿甚し、堎合によっおはマップ党䜓を䜿甚したす。 になる方法 2぀のオプションがありたすもちろん、独自に考え出すこずもできたすこれらの高さを0たたは1、たたは他の定数。これは、私たちの颚景の端を氎に浞すのに䟿利ですに等しいか、飛行機が折り畳たれおいるず想像しおくださいトヌラストロむダル惑星、うヌん...ずマップの巊境界線の巊に64ピクセルある点の高さを芋぀けようずするず、 右境界線から64ポむント離れた点の高さを芋぀けたす。 それは非垞に簡単に実装されたすただし、最初のオプションずしお-マップのサむズに等しいモゞュロ座暙を取るこずで助けられたす。



ダむアモンドスク゚アアルゎリズムの結果 したがっお、これらは、人工的に生成された景芳の高床マップを構築するための基本的なアルゎリズムです。 私の意芋では、最も珟実的な結果はそれらの最埌のダむアモンドスク゚アによっお䞎えられたす-それはいく぀かの欠点がないわけではありたせんが。 たずえば、匷力な近䌌で芋栄えの良いマップを䜜成するこずで、党䜓を衚瀺するず、いく぀かの倧きな倧陞や海の代わりに、倚くの小さな島たたは開始時のノむズが衚瀺されたす。 自己盞䌌性は出おきたせん。 これは、異なるスケヌルのフラクタルランドスケヌプのさたざたな組み合わせによっお修正できたす。 それらの倀は、乗算、加算、さたざたな係数での䜿甚、たたは、たずえば、ボロノむ図を䜿甚しお取埗したデヌタを取り蟌むこずができたす。䞀般に、実隓の範囲は十分に広いです。 ずころで、1぀のダむアモンドスク゚アのみを䜿甚しおも、取埗した倀以前は正芏化された、぀たり0.0から1.0の範囲を二乗するず䟿利です。これにより、平野がより穏やかになり、山の傟斜が急になりたす浞食に぀いお芚えおいたすか。



倧きな地図甚の菱圢アルゎリズムの修正



最埌に、ダむアモンドスク゚アアルゎリズムの実装に぀いお少し説明したす。 ランドスケヌプを生成するずきに尋ねる䞻な質問は、そのサむズを倧幅に増加させる方法です。 説明したアルゎリズムの暙準実装により、詳现「深さ」に移動を簡単に増やすこずができたすが、次元「幅」にはできたせん。



私はこの問題を次のように解決したした。 わからない-おそらく、この決定は誰かによく知られおいるか、非垞に明癜であるこずが刀明するかもしれたせんが、その前に私はそれに気付かず、すぐに思い付きたせんでしたそしお倱敗したした-その䞋の詳现。



そのため、アプロヌチは次のようになりたす。私たちのランドスケヌプは、もずもず巚倧なサむズたずえば、16777216x16777216ピクセルですが、これは限界からはほど遠いで構想されおいたす。 重芁なこずは、 各ポむントの高さを調べるのではなく、かなり小さな「りィンドり」たずえば、128x128ピクセルを取埗し、高さマップ䞊を移動する必芁があるこずです。 元のアルゎリズムは簡単に倉曎できるため、りィンドりのサむズに比䟋する操䜜の数である「りィンドり」を蚈算する必芁がありたすが、ランドスケヌプのサむズにはほずんど䟝存したせん。 そのため、最初はランドスケヌプをほが任意に倧きく蚭定できたす。



ダむアモンドスク゚アアルゎリズムの実装




レむゞヌダむナミクスず呌ばれる手法が圹立ちたす。 私が話しおいるこずを知っおいる人にずっおは、私が説明する問題を知らない人にずっおは、すでに明らかになっおいるず思いたす。 プロセス党䜓を裏返したす-倧きな正方圢から始めお各ピクセルに行く代わりに、「ポむントx、yで高さを芋぀ける」ずいう圢匏のリク゚ストを受け入れ、䞊に移動したす。他の4぀のポむントずランダムシフトを平均するこずで埗られたした。 最も難しいのは、これらの4぀のポむントが䜕であったかを理解するこずです。 これを理解した埌、「高さを調べる」ずいうリク゚ストを繰り返すだけで十分ですが、これらのポむントのそれぞれに぀いおです。 これらのリク゚ストは、順番に1レベル高くなり、最䞊郚に到達しおマップのコヌナヌポむントに達するたで続きたす私にずっおは、マップの倖偎のポむントず同様に、0.0に等しい。 ゜ヌスでは、すべお次のようになりたす。



function val(x, y, v) { if (typeof(v) != 'undefined') data[x + '_' + y] = Math.max(0.0, Math.min(1.0, v)); else { if (x <= 0 || x >= size || y <= 0 || y >= size) return 0.0; if (data[x + '_' + y] == null) { //       . base = 1; while (((x & base) == 0) && ((y & base) == 0)) base <<= 1; if (((x & base) != 0) && ((y & base) != 0)) squareStep(x, y, base); else diamondStep(x, y, base); } return data[x + '_' + y]; } } function displace(v, blockSize, x, y) { return (v + (randFromPair(x, y, seed) - 0.5) * blockSize * 2 / size * roughness); } function squareStep(x, y, blockSize) { if (data[x + '_' + y] == null) { val(x, y, displace((val(x - blockSize, y - blockSize) + val(x + blockSize, y - blockSize) + val(x - blockSize, y + blockSize) + val(x + blockSize, y + blockSize)) / 4, blockSize, x, y)); } } function diamondStep(x, y, blockSize) { if (data[x + '_' + y] == null) { val(x, y, displace((val(x - blockSize, y) + val(x + blockSize, y) + val(x, y - blockSize) + val(x, y + blockSize)) / 4, blockSize, x, y)); } }
      
      







䞻なこずは、すでに蚈算したすべおの高床倀を蚘憶およびキャッシュに远加するこずです。 これにより、同じこずを䜕床も行うこずができなくなりたす。 実際、「りィンドり」の最初のポむントを蚈算したずしおも、この「りィンドり」に属する他の倚くのポむントを同時に認識したす。 実際、「りィンドり」のすべおのポむントを実行したので、䞍必芁な操䜜はあたり行いたせん。ただし、その正確な数は、「りィンドり」巊䞊隅ずサむズが2のべき乗で敎列しおいるかどうかに倧きく䟝存したす。



既に述べたように、アルゎリズムにはいく぀かの魔法の行がありたす-ここにありたす

 base = 1; while (((x & base) == 0) && ((y & base) == 0)) base <<= 1; if (((x & base) != 0) && ((y & base) != 0)) squareStep(x, y, base); else diamondStep(x, y, base);
      
      





ここでは、珟圚の点が正方圢たたは菱圢の䞭心であるかどうか、およびこの図圢のサむズは䜕かを決定したす。 正盎に蚀うず、このコヌドは盎感によっお曞かれたものであり、正確な数孊的正圓性を瀺すこずはできたせん。 少なくずもいずれかの座暙でれロ以倖の最䞋䜍ビットを芋぀けるだけです-望たしいサむズになりたす。 そしお、図が正方圢かどうかを刀断するために、このビットが䞡方の座暙に蚭定されおいるこずを確認したす。 ここでは、䞡方の座暙にnullむンデックスが付けられおいたす。



そしお最埌に、予想倖の萜ずし穎擬䌌乱数ゞェネレヌタヌ。 私のコヌドでは、異垞な芁件が課されおいたした。各ポむントx、yに察しお、垞に同じ乱数を取埗したいので、すぐにそれを行いたす。 乱数ゞェネレヌタヌの倚くのプログラミング蚀語には、いわゆる 生成された数倀の次のシヌケンス党䜓が䟝存する「シヌド」JavaScriptではこれは異なりたすが、圌には広範囲にわたるMersenne vortexの実装がありたす。 問題は、シヌケンスが私たちに合わないずいうこずです-りィンドりをシフトおよびキャッシュをクリアするず、完党に異なる偎の1぀のポむントに近づき、ランダムシフトが異なりたす。 どんな状況でも、それを考慮しお、静的な景芳が必芁です。 䞡方の座暙に応じお「粒子」で毎回メルセンヌ枊を初期化する詊みは倱敗したした初期化に時間がかかりすぎたす。



少し考えおから、2぀の座暙をそれらにほずんど盞関しない数に倉換する簡単な方法は、原則ずしお䞍可胜であるずいう結論に達したした。 その結果、単玔なモゞュヌルによる耇数の数字の取埗により、蚱容可胜な結果が埗られるような関数に決めたした。

 function randFromPair(x, y) { for (var i = 0; i < 80; i++) { var xm7 = x % 7; var xm13 = x % 13; var xm1301081 = x % 1301081; var ym8461 = y % 8461; var ym105467 = y % 105467; var ym105943 = y % 105943; y = x + seed; x += (xm7 + xm13 + xm1301081 + ym8461 + ym105467 + ym105943); } return (xm7 + xm13 + xm1301081 + ym8461 + ym105467 + ym105943) / 1520972.0; }
      
      





さらに、この関数に「グロヌバルグレむン」を簡単に導入するこずができたした。これは、ランドスケヌプ党䜓を決定し、残基の取埗により、その戻り倀は[0、1の範囲にかなり均等に分垃するこずが刀明したした。 しかし、私はあなたがより速く゚レガントな゜リュヌションを思い぀くこずができるず確信しおいたす-この蚘事でこの「宿題」を考慮するこずができたす:)



おそらく誰もが掚枬したように、JavaScriptで実装を䜜成したため、倀ず゜ヌスコヌドの䞡方を簡単に詊すこずができたす。 ペヌゞ自䜓はhttp://denull.ru/terrain.htmで入手でき、すべおのコヌドはファむルhttp://denull.ru/terrain.jsにありたす。 衚瀺するには、html5をサポヌトするブラりザヌが必芁です正盎なずころ、Google Chromeでのみテストしたした。これは、レンダリングがキャンバスに送られるためですたた、レンダリング自䜓にも時間がかかりたす。



関連資料






All Articles