【Unity】パーリンノイズを組み合わせてもうちょっと地形に変化をつける
以前パーリンノイズを使って地形の高さを取得し、なだらかに変化するフィールドを作成しました。
地形として使う場合、単純に0から1までの値となると、山の高さが一定になったりするため、もう少し変化をつけるべくパーリンノイズを何回か生成して重ね合わせる形で地形の高さを設定したいと思います。
うまくいけばよりメリハリのある地形になる……はず。
環境
macOS 11.1 Big Sur
Unity2019.4.4f1
パーリンノイズとは
パーリンノイズは主にテクスチャを作成する際に使われるノイズで、完全にランダムではなく隣り合う値が近いものになっているノイズです。
例えばRandomクラスを使って値を取得すると、以下のように分散グラフのような見た目になります。
パーリンノイズを使って値を取得すると、以下のように波のような見た目になります。急激に変化するのではなく、徐々に変化していく値を取得することができるので、自然な揺れ(カメラの手振れ)などを表現するのに向いています。
パーリンノイズについては以下の記事で解説を行っています。簡単なサンプルコードもあるのでよかったらご覧くださいな。
このページの前提となるもの
パーリンノイズを使って地形の高さを調整するサンプルコードを以下の記事で紹介しています。こちらのコードを使って動的に地形となるオブジェクトを生成します。今回は下の記事で紹介している『FieldGenerator』のスクリプトを継承して処理を追加するため、もし良かったら先にこちらからスクリプトをコピペして作成してくださいな。
メッシュの結合については以下の記事で扱っています。マテリアルも考慮して結合するようにしているので、見た目が崩れないようにしています。
パーリンノイズの重ね合わせ
UnityのMathfクラスでは、2次元のパーリンノイズを扱うことができます。例えばパーリンノイズから取得した値をグレースケールに変換してテクスチャを作成すると、以下のように濃い部分、薄い部分が現れます。
このテクスチャを見るとランダムなパターンになっています。パーリンノイズでは0から1の値(はみ出ることもあります)を取得していて、座標を指定することで値を取得する位置を変更できます。
地形として使うことを考えると、0から1の値でも悪くないのですが、もう少しメリハリが欲しいところ。
高い山、それなりの山、低い丘、といった形で変化が生まれるとより地形っぽく感じます。
というわけで、何パターンかのパーリンノイズを取得し、それを足し合わせて地形を生成してみましょう。イメージとしては以下の画像のような感じです。
明るい部分は値が大きいところ、暗い部分は値が小さいところです。単純に足し算していけば全体的に値が大きくなっていくので、後で平均を取ることで値の範囲を固定していきます。掛け算をする場合は、大きいところはより大きく、小さいところはより小さくなります。
何パターンかを重ね合わせることで変化をつけようというのがこのページの目的です。
スクリプトの作成
任意のフォルダにスクリプトファイルを作成します。ここでは『FieldGeneratorMultiPerlin』という名前にしています。このスクリプトでは別のページで作成した『FieldGenerator』のスクリプトを継承します。
スクリプトでは、2通りの重ね合わせ方を実装してみます。
ひとつは加算する方法で、値をどんどん足していった後に足した回数で平均を取ります。どちらかというと山をどんどん隆起させていくようなイメージです。
もうひとつは乗算する方法で、値をどんどんかけていきます。パーリンノイズで0となるパターンを引くとその部分の高さが0になるため、後でフィールドの高さをかけても0のままになりやすくなり、平原が多くなるようなイメージです。
ボタンを2つ作成してそれぞれ呼び出せるようにしてみましょう。
スクリプトのサンプルは以下の通りです。
長くなってしまったので部分ごとに分けて解説します。
クラスの宣言部とフィールドの宣言です。このクラスでは『FieldGenerator』のクラスを継承して、作成する地形の大きさや高さに関するフィールドをそのまま使えるようにします。
このクラスのフィールドとしては、乱数のシードや重ね合わせの回数を保持するフィールドを追加しています。重ね合わせの回数は加算、乗算のそれぞれで設定できるようにしています。
isAddModeのフラグは加算ボタンが押された場合にtrue、乗算ボタンが押された場合にfalseを設定するようにして処理を分けています。
地形オブジェクトを生成する際、加算で作成するか、乗算で作成するかでメソッドを分けています。フィールドとして作成したisAddModeのフラグに値を設定してから、オブジェクトを生成するためのGenerateMultiPerlinFieldParts()のメソッドを呼び出します。
このメソッドではPrefabを生成する位置を計算してインスタンス化を行います。Prefabの位置ではY軸の値をパーリンノイズを使って計算しています。isAddModeの値に応じて、加算したパーリンノイズを使用するか、乗算したパーリンノイズを使用するかを分けています。三項演算子だと横長になってしまうのでif文で分けても良かったかも。
InstantiateFieldParts()のメソッドは『FieldGenerator』のクラスでprotectedに変更してこちらでも使えるようにしています。
こちらは指定された回数だけパーリンノイズを生成して加算するメソッドです。重ね合わせる回数が0の時はゼロ除算したくないので処理を抜けています。
パーリンノイズを生成する前にシード値を使って乱数を初期化しています。乱数はパーリンノイズのオフセット座標を計算するために使用していますが、各マスで乱数の初期値を揃えておかないと、計算するたびにバラバラの値になってしまうため、このタイミングで初期化しています。なだらかに変化する地形が欲しいのに、隣り合うオブジェクトでバラバラな高さになっているのは嬉しくないですからね。
scaleOffsetはパーリンノイズの座標をどんな粒度で取得するかの値です。scaleが小さいほどよりなだらかに変化する値が取得でき、scaleが大きいほどより急激に変化する値が取得できます。画像だと以下のようなイメージです。
こちらは上の画像よりスケールを上げています。より変化が激しくなっているのが分かります。
重ね合わせるごとにスケールを変化させることで、全体としてより調和した重ね合わせができます。パーリンノイズを加算し終わったら重ね合わせの回数で割って平均を取ります。こうすることで値のオーダーを合わせ、インスペクターウィンドウから『Field Height』の値を使って調整しやすくしています。
このメソッドでは計算したパーリンノイズの値を乗算しています。パーリンノイズの座標を取得する流れは足し算の時と同じです。
パーリンノイズは0よりちょっと小さくなったり、1よりちょっと大きくなったりすることがあるので、MathfクラスのClamp01メソッドを使って0から1の範囲に設定しています。
計算結果が0になることがあるので、地形の高さに適用すると平原のような部分ができやすくなります。
また、0から1までの値をどんどん掛け合わせていくので、計算回数が多くなるほど全体的に小さな値になります。この部分は『Field Height』の値を大きめに設定すると良いかもしれません。
スクリプトのアタッチ
スクリプトを保存したら任意のゲームオブジェクトにアタッチします。ここでは空のオブジェクト『MultiPerlinField』を作成してスクリプトをアタッチしました。
『Field Parts』のフィールドにはインスタンス化する地形オブジェクトのPrefabをアサインします。
『Field Parent』のフィールドにはインスタンス化したオブジェクトを格納するための親オブジェクトをアサインします。
『Field Size X』と『Field Size X』では地形の大きさを指定します。数が大きいとそれだけたくさんのオブジェクトを生成することになるので、結構重くなります。最初は50くらいで確認するといいかも?
『X Origin』と『Y Origin』はパーリンノイズを計算する際の原点となる座標を指定します。地形の見た目を変えたい時に値を変更するとグッド。
『Scale』は変化の度合いを設定します。0.05でも結構変化するので、この近辺で調整するといいかもしれません。
『Mat List』は個々の地形オブジェクトにセットするマテリアルです。パーリンノイズを使って地形を作る一連の記事で検証のために導入しているフィールドなので、実際に使う場合は『FieldGenerator』のクラスで削除してしまってもいいかも。
そのほかのフィールドは今回作成したものなので割愛します。
ボタンの作成
続いてボタンも作成しておきましょう。このシリーズでいくつかボタンを作ってある状態ですが、「生成(加算変化)」と「生成(乗算変化)」の2つのボタンを追加しました。ラベルは区別できればなんでもOKです。
メソッドの呼び出しも設定します。加算で生成するボタンでは『OnPressedAddPerlinButton()』のメソッドを、乗算で生成するボタンでは『OnPressedMultiplePerlinButton()』のメソッドをそれぞれ設定します。
動作確認
ゲームを実行してオブジェクトを生成してみましょう。
この画像はパーリンノイズを重ね合わせないで生成したものです。地形の高さの変化に注目したかったので、scaleを大きくしています。
この画像はパーリンノイズを加算して作成した地形です。ほんの少しですがメリハリがついたかも? 本人が比べてなんとなく感じるくらいなので、そこまで分かりやすい変化ではありませんでした(笑)
上の画像の地形を生成した時の設定は以下の通りです。『Field Height』の値でパーリンノイズの値を増幅させており、生成時はシーンの上の方に生成されることがあります。
続いてパーリンノイズを乗算して重ね合わせたものです。私がイメージしていたのはこちらでした。いい感じに地形のメリハリがついて、高い山、低い山、といった形で現れてきました。
上の画像を生成した時の設定は以下の通りです。掛け算を行う回数が6回なので、『Field Height』の値も大きくしています。例えば0.2を6回かけたとしたら0.000064と、めっちゃ小さい値になります。その分、大きなパターンを引いた地点では山が大きくなり、メリハリが生まれてきます。
上の画像ではscaleを大きめにしてみましたが、もう少し下げるとこんな感じ。こういうのが欲しかったんです。
パーリンノイズを1回計算してそのまま使うだけでも割といい感じの地形を作成できますが、重ね合わせ、特に乗算を使うとよりメリハリのついた形で作成することができます。
まとめ
パーリンノイズを複数回計算したものを重ね合わせて地形にメリハリをつけるサンプルを紹介しました。
流体などをシミュレーションするならCurlノイズの世界まで足を踏み入れる方がいいかもしれませんが(ようこそ物理の世界へ)、ここではシンプルに足し算、掛け算を使うだけでもある程度メリハリをつけた変化を出せる点に注目してみました。
地形を作る際にはTerrainを使うことが多いかもしれませんが、こうした方法もあることを知っていると、あなたのゲームを作る際に選択肢が広がっていくかもしれません。
パーリンノイズ関係やそれに関連するメッシュ関係の記事は以下のページでまとめています。
ゲーム開発の攻略チャートを作りました!
-
前の記事
【Unity】ドローコールを減らすためにいま僕たちができること 2021.01.06
-
次の記事
【Unity】TerrainのHeightMap(ハイトマップ)を生成するエディタ拡張のサンプル 2021.01.08
コメントを書く