🌏母なる大地を創造する🌏
今回はパーリンノイズ法による地形生成について簡単に紹介します。
🌏パーリンノイズとは🌏
**"パーリンノイズ"は地形生成に適した"ノイズアルゴリズム"です。
あの超有名なゲームである"マインクラフト"**でも使われています。
🌏パーリンノイズの実装🌏
今回はパーリンノイズの実装法を簡単に解説します。
class PerlinNoise;
//メンバ変数
using Pint = std::uint_fast8_t;
std::array<Pint, 512> p{ {} };
🌏コンストラクタ🌏
constexpr PerlinNoise() = default;
explicit PerlinNoise(const std::uint_fast32_t seed_) {
this->setSeed(seed_);
}
コンストラクタから**"初期SEED値"**を設定します。
**"SEED値"**とは地形の形を決定するために使用する乱数の初期値です。
//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];
}
毎回同じ**"SEED値"**を設定すると同じ地形が生成されます。
🌏パーリンノイズ生成関数群🌏
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_);
}
パーリンノイズを生成する核となる関数です。
説明は難しいので省きます。詳細はこのサイトの解説をご参考にしてください。
🌏ノイズ生成🌏
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))));
}
ノイズを生成する関数です。
パーリンノイズ生成関数群の呼び出しをまとめてあります。
//オクターブ無しノイズを取得する
template <typename... Args>
double noise(const Args... args_) const noexcept {
return this->setNoise(args_...) * 0.5 + 0.5;
}
ユーザが直接的に値を取得するために別途**"noise関数"**を作成します。
ノイズ関数で作成したパーリンノイズ図です。
丸みを帯びていますね。
🌏オクターブ有りノイズ生成🌏
オクターブをつけてより自然な地形を
生成できるように工夫します。
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;
}
//オクターブ有りノイズを取得する
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くらいの間で設定します。
値が大きいほど複雑な地形になります。
徐々にオクターブ値を大きくすると
こんな感じに徐々に複雑になっていきます。
🌏完成🌏
無事に実装できました。
🌏【オマケ】Unreal Engineで使用してみる🌏
こんな感じでゲームエンジンで使用するとゲーム制作の幅も広がりそうですね!
🌏実装ライブラリ(ソースコード)🌏
今回解説した諸島の自動生成は**"Dungeon Template Library"に"PerlinIsland"**として実装されています。
ぜひ、活用してみてください!
🌏ソースコードのライセンス🌏
These codes are licensed under CC0.
この記事のソースコードはCC0ライセンスとします。
ぜひ、自由に改変して遊んでみてください。
"いいね"をつけてくださると大変励みになります。
最後までお読みいただきありがとうございました!