地形プロシージャル生成 - パーリンノイズアルゴリズム

# 🌏母なる大地を創造する🌏

![PN512](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213065/0bb5da63-38a6-14b5-fdff-91c52e8d9aa7.gif)

**今回はパーリンノイズ法による地形生成について簡単に紹介します。**

# 🌏パーリンノイズとは🌏

**"パーリンノイズ"**は地形生成に適した**"ノイズアルゴリズム"**です。
あの超有名なゲームである**"マインクラフト"**でも使われています。

![mcp](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213065/77c3394b-40e5-5bb4-c800-d4f1f6f68c5a.png)
**ゲームで使用されるパーリンノイズの例**

# 🌏パーリンノイズの実装🌏

今回はパーリンノイズの実装法を簡単に解説します。

```cpp:クラス名
class PerlinNoise;
```

```cpp:private
//メンバ変数
using Pint = std::uint_fast8_t;
std::array<Pint, 512> p{ {} };
```

### 🌏コンストラクタ🌏

```cpp:public
constexpr PerlinNoise() = default;
explicit PerlinNoise(const std::uint_fast32_t seed_) {
	this->setSeed(seed_);
}
```

コンストラクタから**"初期SEED値"**を設定します。
**"SEED値"**とは地形の形を決定するために使用する乱数の初期値です。

```cpp:public
//SEED値を設定する
void setSeed(const std::uint_fast32_t seed_) {
	for (std::size_t i{}; i < 256; ++i)
		this->p[i] = static_cast<Pint>(i);
	std::shuffle(this->p.begin(), this->p.begin() + 256, std::default_random_engine(seed_));
	for (std::size_t i{}; i < 256; ++i)
		this->p[256 + i] = this->p[i];
}
```

![pnseed](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213065/5bf0aeb0-092b-aaf4-1a86-5713eaf98533.png)


毎回同じ**"SEED値"**を設定すると同じ地形が生成されます。

### 🌏パーリンノイズ生成関数群🌏

```cpp:private
constexpr double getFade(const double t_) const noexcept {
	return t_ * t_ * t_ * (t_ * (t_ * 6 - 15) + 10);
}
constexpr double getLerp(const double t_, const double a_, const double b_) const noexcept {
	return a_ + t_ * (b_ - a_);
}
constexpr double makeGrad(const Pint hash_, const double u_, const double v_) const noexcept {
	return (((hash_ & 1) == 0) ? u_ : -u_) + (((hash_ & 2) == 0) ? v_ : -v_);
}
constexpr double makeGrad(const Pint hash_, const double x_, const double y_, const double z_) const noexcept {
	return this->makeGrad(hash_, hash_ < 8 ? x_ : y_, hash_ < 4 ? y_ : hash_ == 12 || hash_ == 14 ? x_ : z_);
}
constexpr double getGrad(const Pint hash_, const double x_, const double y_, const double z_) const noexcept {
	return this->makeGrad(hash_ & 15, x_, y_, z_);
}
```

パーリンノイズを生成する核となる関数です。
説明は難しいので省きます。詳細は[このサイトの解説](https://postd.cc/understanding-perlin-noise/)をご参考にしてください。

[![pib](https://Kasugaccho.github.io/DungeonPicture/Picture/Terrain/PIM256.gif)](https://github.com/Kasugaccho/DungeonTemplateLibrary/wiki/dtl::shape::PerlinIsland-(%E5%BD%A2%E7%8A%B6%E3%82%AF%E3%83%A9%E3%82%B9))

### 🌏ノイズ生成🌏

```cpp:private
double setNoise(double x_ = 0.0, double y_ = 0.0, double z_ = 0.0) const noexcept {
	const std::size_t x_int{ static_cast<std::size_t>(static_cast<std::size_t>(std::floor(x_)) & 255) };
	const std::size_t y_int{ static_cast<std::size_t>(static_cast<std::size_t>(std::floor(y_)) & 255) };
	const std::size_t z_int{ static_cast<std::size_t>(static_cast<std::size_t>(std::floor(z_)) & 255) };
	x_ -= std::floor(x_);
	y_ -= std::floor(y_);
	z_ -= std::floor(z_);
	const double u{ this->getFade(x_) };
	const double v{ this->getFade(y_) };
	const double w{ this->getFade(z_) };
	const std::size_t a0{ static_cast<std::size_t>(this->p[x_int] + y_int) };
	const std::size_t a1{ static_cast<std::size_t>(this->p[a0] + z_int) };
	const std::size_t a2{ static_cast<std::size_t>(this->p[a0 + 1] + z_int) };
	const std::size_t b0{ static_cast<std::size_t>(this->p[x_int + 1] + y_int) };
	const std::size_t b1{ static_cast<std::size_t>(this->p[b0] + z_int) };
	const std::size_t b2{ static_cast<std::size_t>(this->p[b0 + 1] + z_int) };

	return this->getLerp(w,
		this->getLerp(v,
			this->getLerp(u, this->getGrad(this->p[a1], x_, y_, z_), this->getGrad(this->p[b1], x_ - 1, y_, z_)),
			this->getLerp(u, this->getGrad(this->p[a2], x_, y_ - 1, z_), this->getGrad(this->p[b2], x_ - 1, y_ - 1, z_))),
		this->getLerp(v,
			this->getLerp(u, this->getGrad(this->p[a1 + 1], x_, y_, z_ - 1), this->getGrad(this->p[b1 + 1], x_ - 1, y_, z_ - 1)),
			this->getLerp(u, this->getGrad(this->p[a2 + 1], x_, y_ - 1, z_ - 1), this->getGrad(this->p[b2 + 1], x_ - 1, y_ - 1, z_ - 1))));
}
```

ノイズを生成する関数です。
パーリンノイズ生成関数群の呼び出しをまとめてあります。

```cpp:public
//オクターブ無しノイズを取得する
template <typename... Args>
double noise(const Args... args_) const noexcept {
	return this->setNoise(args_...) * 0.5 + 0.5;
}
```

ユーザが直接的に値を取得するために別途**"noise関数"**を作成します。

![pin](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213065/fdac9298-3f27-a334-28be-3ac24aa4700b.gif)

ノイズ関数で作成したパーリンノイズ図です。
丸みを帯びていますね。

### 🌏オクターブ有りノイズ生成🌏

オクターブをつけてより自然な地形を
生成できるように工夫します。

```cpp:private
double setOctaveNoise(const std::size_t octaves_, double x_) const noexcept {
	double noise_value{};
	double amp{ 1.0 };
	for (std::size_t i{}; i < octaves_; ++i) {
		noise_value += this->setNoise(x_) * amp;
		x_ *= 2.0;
		amp *= 0.5;
	}
	return noise_value;
}
double setOctaveNoise(const std::size_t octaves_, double x_, double y_) const noexcept {
	double noise_value{};
	double amp{ 1.0 };
	for (std::size_t i{}; i < octaves_; ++i) {
		noise_value += this->setNoise(x_, y_) * amp;
		x_ *= 2.0;
		y_ *= 2.0;
		amp *= 0.5;
	}
	return noise_value;
}
double setOctaveNoise(const std::size_t octaves_, double x_, double y_, double z_) const noexcept {
	double noise_value{};
	double amp{ 1.0 };
	for (std::size_t i{}; i < octaves_; ++i) {
		noise_value += this->setNoise(x_, y_, z_) * amp;
		x_ *= 2.0;
		y_ *= 2.0;
		z_ *= 2.0;
		amp *= 0.5;
	}
	return noise_value;
}
```

```cpp:public
//オクターブ有りノイズを取得する
template <typename... Args>
double octaveNoise(const std::size_t octaves_, const Args... args_) const noexcept {
	return this->setOctaveNoise(octaves_, args_...) * 0.5 + 0.5;
}
```

オクターブ値は1~10くらいの間で設定します。
値が大きいほど複雑な地形になります。

![pio](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213065/473c6fc6-8d34-0be5-7b58-f2bda38658c9.gif)

徐々にオクターブ値を大きくすると
こんな感じに徐々に複雑になっていきます。

# 🌏完成🌏

無事に実装できました。

[![pib](https://Kasugaccho.github.io/DungeonPicture/Picture/Terrain/PIB256.gif)](https://github.com/Kasugaccho/DungeonTemplateLibrary/wiki/dtl::shape::PerlinIsland-(%E5%BD%A2%E7%8A%B6%E3%82%AF%E3%83%A9%E3%82%B9)) ![pii](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213065/1ee81ea3-6b99-e06d-3f80-963ee9956082.gif)


### [🌏【オマケ】Unreal Engineで使用してみる🌏](https://github.com/Kasugaccho/DungeonTemplateLibrary/wiki/Unreal-Engine-4)

[![UE4](https://kasugaccho.github.io/DungeonPicture/Picture/UE4/drawLandscape.png)](https://github.com/Kasugaccho/DungeonTemplateLibrary/wiki/Unreal-Engine-4)

こんな感じでゲームエンジンで使用するとゲーム制作の幅も広がりそうですね！

# [🌏実装ライブラリ(ソースコード)🌏](https://github.com/Kasugaccho/DungeonTemplateLibrary)

今回解説した諸島の自動生成は**["Dungeon Template Library"](https://github.com/Kasugaccho/DungeonTemplateLibrary)**に**["PerlinIsland"](https://github.com/Kasugaccho/DungeonTemplateLibrary)**として実装されています。
ぜひ、活用してみてください！

[![logo640.gif](https://kasugaccho.github.io/DungeonPicture/Picture/Logo/logo_color640_3.gif)](https://github.com/Kasugaccho/DungeonTemplateLibrary)

# 🌏ソースコードのライセンス🌏

These codes are licensed under CC0.
[![CC0](https://mirrors.creativecommons.org/presskit/buttons/88x31/svg/cc-zero.svg "CC0")](http://creativecommons.org/publicdomain/zero/1.0/deed.ja)

この記事のソースコードはCC0ライセンスとします。
ぜひ、自由に改変して遊んでみてください。

**"いいね"をつけてくださると大変励みになります。**
**最後までお読みいただきありがとうございました！**