Diamond-Squareアルゴリズムに基づく惑星テクスチャの手順生成、パート1

画像



良い一日。 偶然にも、自分にとってある種の難しい質問を見つけたらすぐに、誰にでも解決策を伝えたいと思います。 そのため、手続き型生成などの興味深いトピックに関する2つの記事のシリーズを書くことにしました。 具体的には、惑星テクスチャの生成について説明します。 今回はより徹底的に準備し、前回の記事「Unityのシンプルイベントシステム」よりも内容を改善しようとします(ちなみに、回答の投稿に感謝します)。 続行する前に、いくつかの点に注意を向けたいと思います。



1)このジェネレーターは現実的であるとは主張していません。画面の10%を占有し、雲で覆われている何百もの小さなボールに独自のテクスチャを生成するために作成しました。

2)純粋に技術的な瞬間:Unity3dでC#で記述しているため、言語やプラットフォームごとに独自の方法で、許容可能な速度で画像に出力する方法を考える必要があります。







ダイヤモンド広場



むかしむかし、Gavin SP Millerは、2Dノイズ生成アルゴリズムについて説明しました。 そして4年前、もう1人の優れた人物deNULLが 、この方法と他の生成方法に関する詳細な記事を書きました。 先に進む前に、読むことを強くお勧めします。アルゴリズムは非常によく説明されています。 しかし、コードを書き始めたとき、いくつかの技術的な問題が発生しました。 問題は何だったのか、どのように解決されたのかについては、この記事で説明します。 一般的なアクションスキームから始めましょう。



画像



最初に、アルゴリズムを使用して、「高さマップ」を生成します。これは、2番目の図に見られるものとほぼ同じです。 Unityの色成分の値は0から1の小数であるように見えるため、アルゴリズムの私の実装はこの範囲の値でフィールドを埋めますが、異なる範囲のアルゴリズムを作り直すことは難しくありません。 次に、特定の比率で、この値が対応するピクセルの色のr、g、b値に入力されます。 最初のポイントに進みます。



高さのマップを生成します



この時点で、ダイヤモンドスクエア自体の一般的な原理をすでに想像していることを願っています。そうでない場合でも、上記の記事を読んでください。 実装のみを説明します。 まず、2次元配列を作成します。他のサイズでは、測定値は2 ^ n + 1に等しくなるはずです)。 2049x1025を取りました(2:1の比率は、真空中の球状惑星に最適です)。 SquareメソッドとDiamondメソッドを書きましょう。 最初は、正方形の左下隅と右上隅の座標を取得し、値をその中心に書き込みます。 2番目は、計算するポイントの値(つまり、この正方形の辺の中央)を取り、正方形の隣接する角、その中心、および隣接する正方形の中心の値に基づいて計算します。 この場所には興味深い障害がありますが、最初にメソッド自体があります。



public static int ysize = 1025, xsize = ysize * 2 - 1; public static float[,] heighmap = new float[xsize, ysize]; public static float roughness = 2f; //  ,  ,      public static void Square(int lx, int ly, int rx, int ry) { int l = (rx - lx) / 2; float a = heighmap[lx, ly]; // B--------C float b = heighmap[lx, ry]; // | | float c = heighmap[rx, ry]; // | ce | float d = heighmap[rx, ly]; // | | int cex = lx + l; // A--------D int cey = ly + l; heighmap[cex, cey] = (a + b + c + d) / 4 + Random.Range(-l * 2 * roughness / ysize, l * 2 * roughness / ysize); }
      
      





重要な注意:Random.Range(float min、float max)は、指定された範囲の擬似乱数を返します。 それは高速ですが、Unityでのみです。 System.Randomがどうなるかわかりません。 つまり、自分で擬似乱数ジェネレータを作成する必要があるかもしれません。



さらに進みます:



 bool lrflag = false; public static void Diamond(int tgx, int tgy, int l) { float a, b, c, d; if (tgy - l >= 0) a = heighmap[tgx, tgy - l]; // C-------- else // | | a = heighmap[tgx, ysize - l]; // B---t g----D | // | | // A-------- if (tgx - l >= 0) b = heighmap[tgx - l, tgy]; else if (lrflag) b = heighmap[xsize - l, tgy]; else b = heighmap[ysize - l, tgy]; if (tgy + l < ysize) c = heighmap[tgx, tgy + l]; else c = heighmap[tgx, l]; if (lrflag) if (tgx + l < xsize) d = heighmap[tgx + l, tgy]; else<source lang="cs"> d = heighmap[l, tgy]; else if (tgx + l < ysize) d = heighmap[tgx + l, tgy]; else d = heighmap[l, tgy]; heighmap[tgx, tgy] = (a + b + c + d) / 4 + Random.Range(-l * 2 * roughness / ysize, l * 2 * roughness / ysize); }
      
      





ここでさらに詳しく説明します。 ご覧のように、各ポイントでチェックを実行します。配列の境界を越えていますか。 もしそうなら、反対の値をそれに割り当てます。 つまり たとえば、左側で菱形を計算する場合、その左側の頂点の横座標はゼロ未満であり、代わりに対称な点の値を使用します。 xsize-l(lは正方形の半分の辺) したがって、球に適用すると、シームレスなテクスチャが得られます。 さらに、右の境界を超える可能性のある座標については、追加のチェックが実行されます。 実際、菱形は正方形に対してのみ有効です。 辺2:1の長方形を作成し、2つの正方形として数えます。 したがって、私たちがどの部分で行動しているかを決定するフラグを導入し、それに応じて、右側の境界を1025または2049(ysizeまたはxsize)のいずれかと見なしました。 非常にエレガントなソリューションではありませんが、安価で信頼性が高く実用的です。他の比率はまだ必要ないので、そのままにしました。



すべての座標を再確認してください! ダイヤモンドパラメーターの1つの間違いのため、この数学的に興味深いものの前で終日困惑しましたが、写真の主題ではまったくありませんでした。

ネタバレ
画像



私たちのツールはほぼ準備ができており、最後の小さな方法が残り、それらを1つの正方形に結合します。



 public static void DiamondSquare(int lx, int ly, int rx, int ry) { int l = (rx - lx) / 2; Square(lx, ly, rx, ry); Diamond(lx, ly + l, l); Diamond(rx, ry - l, l); Diamond(rx - l, ry, l); Diamond(lx + l, ly, l); }
      
      





このメソッドを再帰的に呼び出さないことに注意してください。その理由を説明します。 この写真を見てください:



画像



サブスクエアを再帰的にダイアモンドスクエアと呼ぶとどうなるかを、段階的に描きました。 最初の正方形は正常と見なされます、なぜなら ダイヤモンドの頂点は配列の境界を越えており、代わりに正方形の中心が使用されます。 しかし、ここではその中の正方形はすでに間違っていると考えられています 正方形の真ん中-隣人はまだ数えられていません。 その結果、良いことは何も起こりません。 したがって、その記事に書かれているように、レイヤーを考慮する必要があり、この場合の再帰は必要ありません。 この事実に気付いてしばらくして、レイヤーごとにこの計算を思いついた。



 public static void Generate() { heighmap[0, 0] = Random.Range(0.3f, 0.6f); heighmap[0, ysize - 1] = Random.Range(0.3f, 0.6f); heighmap[xsize - 1, ysize - 1] = Random.Range(0.3f, 0.6f); heighmap[xsize - 1, 0] = Random.Range(0.3f, 0.6f); heighmap[ysize - 1, ysize - 1] = Random.Range(0.3f, 0.6f); heighmap[ysize - 1, 0] = Random.Range(0.3f, 0.6f); for (int l = (ysize - 1) / 2; l > 0; l /= 2) for (int x = 0; x < xsize - 1; x += l) { if (x >= ysize - l) lrflag = true; else lrflag = false; for (int y = 0; y < ysize - 1; y += l) DiamondSquare(x, y, x + l, y + l); } }
      
      





私たちは正方形の辺のすべての長さを調べます(注意深い人は検索が半分の長さですぐに始まることに気付くかもしれません。正直に言うと、私は理由がわかりませんが、一度あなたが完全な辺をとると、大陸を取得します-マップ全体の過成長)この「レイヤー」の長さの正方形のすべての左下隅。 したがって、すべての正方形は常に隣人の中心を「認識」し、正しい画像が取得されます。 ちなみに、コードからわかるように、画像の4つの角(長方形の場合、4つの角と大きな辺の中央)を指定する必要があります。これはアルゴリズムへの入力です。



出来上がり!



画像



さて、今最も興味深いのは色です。



免責事項 :以下は私の自転車です。 そして正直なところ、最高ではありません。 それは最も現実的な図からはほど遠い、そのコードはかさばるので、改善のためのアイデアを喜んで聞きます。 すべて、あなたは警告され、惑星形成のプロセスに関するエッセイを書くためにキーボードを準備します、私は学校でよく聞きませんでした、そして何も覚えていませんでした。



私は惑星全体を3つのゾーンに分割することにしました:雪、緑ゾーン、砂漠ゾーン。 地理の教科書に写っている写真を覚えていますか? 今、それらを行います。 このために、別のクラスを作成しました。 それは小さいです、私はすぐに広がりました:



 public static class TemperatureCurves_class { static int xsize = Heighmap_class.xsize; public static int snowEdge = Heighmap_class.ysize / 10; public static int greenEdge = Heighmap_class.ysize / 3; public static int[] northGreen = new int[xsize]; public static int[] southGreen = new int[xsize]; public static int[] northSnow = new int[xsize]; public static int[] southSnow = new int[xsize]; static float snowRoughness = 0.03f; static float greenRoughness = 0.15f; static void MidPointDisplacement1D(ref int[] curve, int l, int r, float roughness) { if (r - l > 1) { curve[(l + r) / 2] = (curve[l] + curve[r]) / 2 + (int)Random.Range(-(r - l) * roughness, (r - l) * roughness); MidPointDisplacement1D(ref curve, l, (l + r) / 2, roughness); MidPointDisplacement1D(ref curve, (l + r) / 2, r, roughness); } } public static void Generate() { northSnow[0] = northSnow[xsize - 1] = Heighmap_class.ysize - snowEdge; southSnow[0] = southSnow[xsize - 1] = snowEdge; northGreen[0] = northGreen[xsize - 1] = Heighmap_class.ysize - greenEdge; southGreen[0] = southGreen[xsize - 1] = greenEdge MidPointDisplacement1D(ref northGreen, 0, xsize - 1, greenRoughness); MidPointDisplacement1D(ref southGreen, 0, xsize - 1, greenRoughness); MidPointDisplacement1D(ref northSnow, 0, xsize - 1, snowRoughness); MidPointDisplacement1D(ref southSnow, 0, xsize - 1, snowRoughness); } }
      
      





そのため、名前が示すように、気候帯の境界はこのクラスにあります。 それらはアレイの形をしています-南に2つ、北に2つ。 そして、祖先のダイアモンドスクエア:中間点変位を使用して生成します。 この場合、直線で使用します。 特にdeNULLが私のためにこれをすでに行っているので、私はダイアモンドスクエアの後のその動作の原理を説明する必要がないと思います。 実際、x座標にマッピングされたy座標のセットがあり、ゾーンのインターフェースを示しています。 ベルトが閉じられるように(これは後で球体上ですべて引っ張ります)、エッジを等しくします。 そして、雪の中のリアリズムと緑のストリップのために、異なる粗さ値を作成して、極円が円(テクスチャ上で直線)になり、ベルトが最も予測不可能な経路に沿ってうごめくことができます(ただし、遠くに行かないことが重要です、雪の境界上の砂漠は奇妙に見えます)。 ただし、砂漠はまだ奇妙に見えます。 それらは、赤道に近いだけでなく、山、風、その他の要因によっても決定されます。



色を扱う

コードの最も面倒で自転車の部分は準備してください。

まず、色の配列を作成します(ユニットには、そのような配列をすばやく読み取り、その要素をテクスチャピクセルに書き込むメソッドがあります。これは、SetPixel()などよりもはるかに高速です)。



 public Texture2D tex; //  ,     public static Color[] colors = new Color[Heighmap_class.xsize * Heighmap_class.ysize]; float waterLevel = 0.2f;
      
      





ちなみに、 deNULLが書いたように、高さマップの値を2乗すると便利です。これにより、ビットマップが少し滑らかになり、実際の値により似たものになります。



 void SqrHeighmap() { for (int x = 0; x < Heighmap_class.xsize; x++) for (int y = 0; y < Heighmap_class.ysize; y++) Heighmap_class.heighmap[x, y] *= Heighmap_class.heighmap[x, y]; }
      
      





さらに、完成したテクスチャに次の効果を適用します。



 void SmoothImg() { for (int i = 0; i < Heighmap_class.xsize * Heighmap_class.ysize - 2; i++) { Color clr1 = colors[i]; Color clr2 = colors[i + 1]; colors[i] = (2 * clr1 + clr2) / 3; colors[i + 1] = (clr1 + 2 * clr2) / 3; } }
      
      





効果は小さいですが、速度には特に影響しないようです。

そして今、私たちはさまざまな地形の色を設定するメソッドを書きます:



 void SetSnow(int counter, float heigh) { if (heigh < waterLevel + Random.Range(-0.04f, 0.04f)) colors[counter] = new Color(Random.Range(0.8f, 0.85f), Random.Range(0.8f, 0.85f), Random.Range(0.85f, 0.9f)); else { colors[counter].r = 0.005f / heigh + Random.Range(0.8f, 0.85f); colors[counter].g = 0.005f / heigh + Random.Range(0.8f, 0.85f); colors[counter].b = 0.01f / heigh + Random.Range(0.8f, 0.85f); } }
      
      





冬が来ています。 水が分布下にある場合は、均一な雪の地殻で覆い、さもなければ、標高の領域でわずかに暗くなる大陸の弱い輪郭を持つ雪の地殻で覆います。



 void SetOcean(int counter, float heigh) { colors[counter].r = heigh / 5; colors[counter].g = heigh / 5; colors[counter].b = 0.2f + heigh / 2 + Random.Range(-0.02f, 0.02f); }
      
      





まあ、すべてが明確です、彼は海であり、他の惑星では海です。



 void SetGreen(int counter, float heigh) { colors[counter].g = 0.1f / heigh + 0.05f + Random.Range(-0.04f, 0.04f); if (heigh < waterLevel + 0.1f) colors[counter].g -= (waterLevel + 0.1f - heigh); if (colors[counter].g > 0.5f) colors[counter].g = 0.5f / heigh + 0.05f + Random.Range(-0.04f, 0.04f); colors[counter].r = 0; colors[counter].b = 0; }
      
      





色を高さと逆に変更します-高くなるほど暗くなります。 さらに、大陸の端が露出しないように、水の端でわずかに減らします。



 void SetDesert(int counter, float heigh) { colors[counter].r = Random.Range(0.6f, 0.75f) + heigh / 2 - 0.35f; colors[counter].g = Random.Range(0.6f, 0.75f) + heigh / 2 - 0.35f; colors[counter].b = Random.Range(0.2f, 0.3f) + heigh / 2 - 0.35f; }
      
      





ここではすべてが明確であり、別のタイプの地形:



 void SetMountains(int counter, float heigh) { float rnd = Random.Range(-0.03f, 0.03f); if (heigh > 1.1f) heigh = 1.1f + Random.Range(-0.05f, 0.05f); colors[counter].r = heigh * heigh / 2 + rnd - 0.1f; colors[counter].g = heigh * heigh / 2 + rnd - 0.1f; colors[counter].b = heigh * heigh / 2 + rnd - 0.05f; }
      
      





同時に、山を避けます(別の質問です。なぜ1.0f以上の値が表示されるのでしょうか。しかし、ユニットはそれに対して何も持っていないようです)。



そして、最小値と最大値の間の値をチェックするユーティリティメソッドを使用して、ポイントがベルトに属しているかどうかを判断します。



 bool ChechInRange(int value, int min, int max, int rand) { return ((value > min + rand) && (value < max + rand)); }
      
      





そして今、肉そのもの:



 void Awake() { Heighmap_class.Generate(); TemperatureCurves_class.Generate(); tex.Resize(Heighmap_class.xsize, Heighmap_class.ysize); tex.Apply(); SqrHeighmap(); int counter = 0; for (int y = 0; y < Heighmap_class.ysize; y++) for (int x = 0; x < Heighmap_class.xsize; x++) { float heigh = Heighmap_class.heighmap[x, y]; if (heigh < waterLevel) SetOcean(counter, heigh); else { if (ChechInRange(y, TemperatureCurves_class.southSnow[x], TemperatureCurves_class.northSnow[x], Random.Range(-10, 10))) if (ChechInRange(y, TemperatureCurves_class.southGreen[x], TemperatureCurves_class.northGreen[x], Random.Range(-10, 10))) { SetDesert(counter, heigh); if (heigh < waterLevel + 0.1f + Random.Range(-0.05f, 0.05f)) SetGreen(counter, heigh); } else SetGreen(counter, heigh); if (heigh > 0.82f + Random.Range(-0.05f, 0.05f)) SetMountains(counter, heigh); } if ((y < TemperatureCurves_class.southSnow[x] + Random.Range(-10, 10)) || (y > TemperatureCurves_class.northSnow[x] + Random.Range(-10, 10))) SetSnow(counter, heigh); counter++; } SmoothImg(); tex.SetPixels(colors); tex.Apply(); }
      
      





言葉で試してみましょう:

高さのマップを生成し、ゾーンの境界を生成し、テクスチャを初期化し、正方形にします。

ループ内:

高さが海面より低い場合、ピクセルを海で塗りつぶします。

それ以外の場合は、緑の帯に属しているかどうかを確認し、内部で砂漠を確認し、そうであれば砂漠を作成し、そうでなければ砂漠を作成します。

急に高さが高くなった場合、このすべての上に山を置きます。

さらに、北極圏に到達すると、 白い歩行者を雪と呼びます

さて、サイクルの後、画像を滑らかにしてテクスチャにロードします。



ここに私が手に入れた発電機があります。 あなたは何ですか?

画像



PSソース:

デモ

Unityプロジェクト

テキスト
 using UnityEngine; using System.Collections; public class Heighmap_class { public static int ysize = 1025, xsize = ysize * 2 - 1; public static float[,] heighmap = new float[xsize, ysize]; public static float roughness = 2f; //  ,  ,      public static void Square(int lx, int ly, int rx, int ry) { int l = (rx - lx) / 2; float a = heighmap[lx, ly]; // B--------C float b = heighmap[lx, ry]; // | | float c = heighmap[rx, ry]; // | ce | float d = heighmap[rx, ly]; // | | int cex = lx + l; // A--------D int cey = ly + l; heighmap[cex, cey] = (a + b + c + d) / 4 + Random.Range(-l * 2 * roughness / ysize, l * 2 * roughness / ysize); } static bool lrflag = false; public static void Diamond(int tgx, int tgy, int l) { float a, b, c, d; if (tgy - l >= 0) a = heighmap[tgx, tgy - l]; // C-------- else // | | a = heighmap[tgx, ysize - l]; // B---t g----D | // | | // A-------- if (tgx - l >= 0) b = heighmap[tgx - l, tgy]; else if (lrflag) b = heighmap[xsize - l, tgy]; else b = heighmap[ysize - l, tgy]; if (tgy + l < ysize) c = heighmap[tgx, tgy + l]; else c = heighmap[tgx, l]; if (lrflag) if (tgx + l < xsize) d = heighmap[tgx + l, tgy]; else d = heighmap[l, tgy]; else if (tgx + l < ysize) d = heighmap[tgx + l, tgy]; else d = heighmap[l, tgy]; heighmap[tgx, tgy] = (a + b + c + d) / 4 + Random.Range(-l * 2 * roughness / ysize, l * 2 * roughness / ysize); } public static void DiamondSquare(int lx, int ly, int rx, int ry) { int l = (rx - lx) / 2; Square(lx, ly, rx, ry); Diamond(lx, ly + l, l); Diamond(rx, ry - l, l); Diamond(rx - l, ry, l); Diamond(lx + l, ly, l); } public static void MidPointDisplacement(int lx, int ly, int rx, int ry) { int l = (rx - lx) / 2; if (l > 0) { float a = heighmap[lx, ly]; // B--------C float b = heighmap[lx, ry]; // | | float c = heighmap[rx, ry]; // | ce | float d = heighmap[rx, ly]; // | | // A--------D int cex = lx + l; int cey = ly + l; heighmap[cex, cey] = (a + b + c + d) / 4 + Random.Range(-l * 2 * roughness / xsize, l * 2 * roughness / xsize); heighmap[lx, cey] = (a + b) / 2 + Random.Range(-l * 2 * roughness / xsize, l * 2 * roughness / xsize); heighmap[rx, cey] = (c + d) / 2 + Random.Range(-l * 2 * roughness / xsize, l * 2 * roughness / xsize); heighmap[cex, ly] = (a + d) / 2 + Random.Range(-l * 2 * roughness / xsize, l * 2 * roughness / xsize); heighmap[cex, ry] = (b + c) / 2 + Random.Range(-l * 2 * roughness / xsize, l * 2 * roughness / xsize); MidPointDisplacement(lx, ly, cex, cey); MidPointDisplacement(lx, ly + l, lx + l, ry); MidPointDisplacement(cex, cey, rx, ry); MidPointDisplacement(lx + l, ly, rx, cey); } } public static void Generate() { heighmap[0, 0] = Random.Range(0.3f, 0.6f); heighmap[0, ysize - 1] = Random.Range(0.3f, 0.6f); heighmap[xsize - 1, ysize - 1] = Random.Range(0.3f, 0.6f); heighmap[xsize - 1, 0] = Random.Range(0.3f, 0.6f); heighmap[ysize - 1, ysize - 1] = Random.Range(0.3f, 0.6f); heighmap[ysize - 1, 0] = Random.Range(0.3f, 0.6f); for (int l = (ysize - 1) / 2; l > 0; l /= 2) for (int x = 0; x < xsize - 1; x += l) { if (x >= ysize - l) lrflag = true; else lrflag = false; for (int y = 0; y < ysize - 1; y += l) DiamondSquare(x, y, x + l, y + l); } } }
      
      





 using UnityEngine; using System.Collections; public static class TemperatureCurves_class { static int xsize = Heighmap_class.xsize; public static int snowEdge = Heighmap_class.ysize / 10; public static int greenEdge = Heighmap_class.ysize / 3; public static int[] northGreen = new int[xsize]; public static int[] southGreen = new int[xsize]; public static int[] northSnow = new int[xsize]; public static int[] southSnow = new int[xsize]; static float snowRoughness = 0.03f; static float greenRoughness = 0.15f; static void MidPointDisplacement1D(ref int[] curve, int l, int r, float roughness) { if (r - l > 1) { curve[(l + r) / 2] = (curve[l] + curve[r]) / 2 + (int)Random.Range(-(r - l) * roughness, (r - l) * roughness); MidPointDisplacement1D(ref curve, l, (l + r) / 2, roughness); MidPointDisplacement1D(ref curve, (l + r) / 2, r, roughness); } } public static void Generate() { northSnow[0] = northSnow[xsize - 1] = Heighmap_class.ysize - snowEdge; southSnow[0] = southSnow[xsize - 1] = snowEdge; northGreen[0] = northGreen[xsize - 1] = Heighmap_class.ysize - greenEdge; southGreen[0] = southGreen[xsize - 1] = greenEdge + Random.Range(-100, 100); MidPointDisplacement1D(ref northGreen, 0, xsize - 1, greenRoughness); MidPointDisplacement1D(ref southGreen, 0, xsize - 1, greenRoughness); MidPointDisplacement1D(ref northSnow, 0, xsize - 1, snowRoughness); MidPointDisplacement1D(ref southSnow, 0, xsize - 1, snowRoughness); } }
      
      





 using UnityEngine; using System.Collections; public class Renderer_script : MonoBehaviour { public Texture2D tex; public static Color[] colors = new Color[Heighmap_class.xsize * Heighmap_class.ysize]; float waterLevel = 0.2f; void SqrHeighmap() { for (int x = 0; x < Heighmap_class.xsize; x++) for (int y = 0; y < Heighmap_class.ysize; y++) Heighmap_class.heighmap[x, y] *= Heighmap_class.heighmap[x, y]; } void SetSnow(int counter, float heigh) { if (heigh < waterLevel + Random.Range(-0.04f, 0.04f)) colors[counter] = new Color(Random.Range(0.8f, 0.85f), Random.Range(0.8f, 0.85f), Random.Range(0.85f, 0.9f)); else { colors[counter].r = 0.005f / heigh + Random.Range(0.8f, 0.85f); colors[counter].g = 0.005f / heigh + Random.Range(0.8f, 0.85f); colors[counter].b = 0.01f / heigh + Random.Range(0.8f, 0.85f); } } void SetOcean(int counter, float heigh) { colors[counter].r = heigh / 5; colors[counter].g = heigh / 5; colors[counter].b = 0.2f + heigh / 2 + Random.Range(-0.02f, 0.02f); } void SetGreen(int counter, float heigh) { colors[counter].g = 0.1f / heigh + 0.05f + Random.Range(-0.04f, 0.04f); if (heigh < waterLevel + 0.1f) colors[counter].g -= (waterLevel + 0.1f - heigh); if (colors[counter].g > 0.5f) colors[counter].g = 0.5f / heigh + 0.05f + Random.Range(-0.04f, 0.04f); colors[counter].r = 0; colors[counter].b = 0; } void SetDesert(int counter, float heigh) { colors[counter].r = Random.Range(0.6f, 0.75f) + heigh / 2 - 0.35f; colors[counter].g = Random.Range(0.6f, 0.75f) + heigh / 2 - 0.35f; colors[counter].b = Random.Range(0.2f, 0.3f) + heigh / 2 - 0.35f; } void SetMountains(int counter, float heigh) { float rnd = Random.Range(-0.03f, 0.03f); if (heigh > 1.1f) heigh = 1.1f + Random.Range(-0.05f, 0.05f); colors[counter].r = heigh * heigh / 2 + rnd - 0.1f; colors[counter].g = heigh * heigh / 2 + rnd - 0.1f; colors[counter].b = heigh * heigh / 2 + rnd - 0.05f; } void SmoothImg() { for (int i = 0; i < Heighmap_class.xsize * Heighmap_class.ysize - 2; i++) { Color clr1 = colors[i]; Color clr2 = colors[i + 1]; colors[i] = (2 * clr1 + clr2) / 3; colors[i + 1] = (clr1 + 2 * clr2) / 3; } } bool ChechInRange(int value, int min, int max, int rand) { return ((value > min + rand) && (value < max + rand)); } void Awake() { Heighmap_class.Generate(); TemperatureCurves_class.Generate(); tex.Resize(Heighmap_class.xsize, Heighmap_class.ysize); tex.Apply(); SqrHeighmap(); int counter = 0; for (int y = 0; y < Heighmap_class.ysize; y++) for (int x = 0; x < Heighmap_class.xsize; x++) { float heigh = Heighmap_class.heighmap[x, y]; if (heigh < waterLevel) SetOcean(counter, heigh); else { if (ChechInRange(y, TemperatureCurves_class.southSnow[x], TemperatureCurves_class.northSnow[x], Random.Range(-10, 10))) if (ChechInRange(y, TemperatureCurves_class.southGreen[x], TemperatureCurves_class.northGreen[x], Random.Range(-10, 10))) { SetDesert(counter, heigh); if (heigh < waterLevel + 0.1f + Random.Range(-0.05f, 0.05f)) SetGreen(counter, heigh); } else SetGreen(counter, heigh); if (heigh > 0.82f + Random.Range(-0.05f, 0.05f)) SetMountains(counter, heigh); } if ((y < TemperatureCurves_class.southSnow[x] + Random.Range(-10, 10)) || (y > TemperatureCurves_class.northSnow[x] + Random.Range(-10, 10))) SetSnow(counter, heigh); counter++; } SmoothImg(); tex.SetPixels(colors); tex.Apply(); } }
      
      








All Articles