C++
game
アルゴリズム
ゲーム制作
DungeonGeneration

🌏母なる大地を創造する🌏【パーリンノイズ法】


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

PN512

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


🌏パーリンノイズとは🌏

"パーリンノイズ"は地形生成に適した"ノイズアルゴリズム"です。

あの超有名なゲームである"マインクラフト"でも使われています。

mcp

ゲームで使用されるパーリンノイズの例


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

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


クラス名

class PerlinNoise;



private

//メンバ変数

using Pint = std::uint_fast8_t;
std::array<Pint, 512> p{ {} };


🌏コンストラクタ🌏


public

constexpr PerlinNoise() = default;

explicit PerlinNoise(const std::uint_fast32_t seed_) {
this->setSeed(seed_);
}

コンストラクタから"初期SEED値"を設定します。

"SEED値"とは地形の形を決定するために使用する乱数の初期値です。


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

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


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


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_);
}

パーリンノイズを生成する核となる関数です。

説明は難しいので省きます。詳細はこのサイトの解説をご参考にしてください。

pib


🌏ノイズ生成🌏


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))));
}


ノイズを生成する関数です。

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


public

//オクターブ無しノイズを取得する

template <typename... Args>
double noise(const Args... args_) const noexcept {
return this->setNoise(args_...) * 0.5 + 0.5;
}

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

pin

ノイズ関数で作成したパーリンノイズ図です。

丸みを帯びていますね。


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

オクターブをつけてより自然な地形を

生成できるように工夫します。


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;
}


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

徐々にオクターブ値を大きくすると

こんな感じに徐々に複雑になっていきます。


🌏完成🌏

無事に実装できました。

pib pii


🌏【オマケ】Unreal Engineで使用してみる🌏

UE4

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


🌏実装ライブラリ(ソースコード)🌏

今回解説した諸島の自動生成は"Dungeon Template Library"に"PerlinIsland"として実装されています。

ぜひ、活用してみてください!

logo640.gif


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

These codes are licensed under CC0.

CC0

この記事のソースコードはCC0ライセンスとします。

ぜひ、自由に改変して遊んでみてください。

"いいね"をつけてくださると大変励みになります。

最後までお読みいただきありがとうございました!