Skip to content

Update from Ruputeless #2

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
766 changes: 509 additions & 257 deletions PerlinNoise.hpp

Large diffs are not rendered by default.

114 changes: 106 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,120 @@
# siv::PerlinNoise
**siv::PerlinNoise** is a header-only Perlin noise library for modern C++.
Based on Ken Perlin's [Improved Noise](<http://mrl.nyu.edu/~perlin/noise/>).
# siv::PerlinNoise
![noise](images/top.png)

**siv::PerlinNoise** is a header-only Perlin noise library for modern C++ (C++17/20).
The implementation is based on Ken Perlin's [Improved Noise](https://cs.nyu.edu/~perlin/noise/).

## Features
* random seed
* 1D / 2D / 3D noise
* octave noise (accumulated / normalized)
* [0.0, 1.0] noise
* octave noise
* initial seed
* *(✨ new in v3.0)* produce the same output on any platform (except for floating point errors)

## License
siv::PerlinNoise is distributed under the MIT license.
siv::PerlinNoise is distributed under the **MIT license**.

## Usage

```cpp
# include <iostream>
# include "PerlinNoise.hpp"

int main()
{
const siv::PerlinNoise::seed_type seed = 123456u;

const siv::PerlinNoise perlin{ seed };

for (int y = 0; y < 5; ++y)
{
for (int x = 0; x < 5; ++x)
{
const double noise = perlin.octave2D_01((x * 0.01), (y * 0.01), 4);

std::cout << noise << '\t';
}

std::cout << '\n';
}
}
```

## API

### `template <class Float> class BasicPerlinNoise`

- Typedefs
- `using PerlinNoise = BasicPerlinNoise<double>;`
- `using state_type = std::array<std::uint8_t, 256>;`
- `using value_type = Float;`
- `using default_random_engine = std::mt19937;`
- `using seed_type = typename default_random_engine::result_type;`
- Constructors
- `constexpr BasicPerlinNoise();`
- `BasicPerlinNoise(seed_type seed);`
- `BasicPerlinNoise(URBG&& urbg);`
- Reseed
- `void reseed(seed_type seed);`
- `void reseed(URBG&& urbg);`
- Serialization
- `constexpr const state_type& serialize() const noexcept;`
- `constexpr void deserialize(const state_type& state) noexcept;`
- Noise (The result is **in the range [-1, 1]**)
- `value_type noise1D(value_type x) const noexcept;`
- `value_type noise2D(value_type x, value_type y) const noexcept;`
- `value_type noise3D(value_type x, value_type y, value_type z) const noexcept;`
- Noise (The result is **remapped to the range [0, 1]**)
- `value_type noise1D_01(value_type x) const noexcept;`
- `value_type noise2D_01(value_type x, value_type y) const noexcept;`
- `value_type noise3D_01(value_type x, value_type y, value_type z) const noexcept;`
- Octave noise (The result **can be out of the range [-1, 1]**)
- `value_type octave1D(value_type x, std::int32_t octaves, value_type persistence = value_type(0.5)) const noexcept;`
- `value_type octave2D(value_type x, value_type y, std::int32_t octaves, value_type persistence = value_type(0.5)) const noexcept;`
- `value_type octave3D(value_type x, value_type y, value_type z, std::int32_t octaves, value_type persistence = value_type(0.5)) const noexcept;`
- Octave noise (The result is **clamped to the range [-1, 1]**)
- `value_type octave1D_11(value_type x, std::int32_t octaves, value_type persistence = value_type(0.5)) const noexcept;`
- `value_type octave2D_11(value_type x, value_type y, std::int32_t octaves, value_type persistence = value_type(0.5)) const noexcept;`
- `value_type octave3D_11(value_type x, value_type y, value_type z, std::int32_t octaves, value_type persistence = value_type(0.5)) const noexcept;`
- Octave noise (The result is **clamped and remapped to the range [0, 1]**)
- `value_type octave1D_01(value_type x, std::int32_t octaves, value_type persistence = value_type(0.5)) const noexcept;`
- `value_type octave2D_01(value_type x, value_type y, std::int32_t octaves, value_type persistence = value_type(0.5)) const noexcept;`
- `value_type octave3D_01(value_type x, value_type y, value_type z, std::int32_t octaves, value_type persistence = value_type(0.5)) const noexcept;`
- Octave noise (The result is **normalized to the range [-1, 1]**)
- `value_type normalizedOctave1D(value_type x, std::int32_t octaves, value_type persistence = value_type(0.5)) const noexcept;`
- `value_type normalizedOctave2D(value_type x, value_type y, std::int32_t octaves, value_type persistence = value_type(0.5)) const noexcept;`
- `value_type normalizedOctave3D(value_type x, value_type y, value_type z, std::int32_t octaves, value_type persistence = value_type(0.5)) const noexcept;`
- Octave noise (The result is **normalized and remapped to the range [0, 1]**)
- `value_type normalizedOctave1D_01(value_type x, std::int32_t octaves, value_type persistence = value_type(0.5)) const noexcept;`
- `value_type normalizedOctave2D_01(value_type x, value_type y, std::int32_t octaves, value_type persistence = value_type(0.5)) const noexcept;`
- `value_type normalizedOctave3D_01(value_type x, value_type y, value_type z, std::int32_t octaves, value_type persistence = value_type(0.5)) const noexcept;`

## Example
Run example.cpp with the following parameters.

```
frequency = 8.0
octaves = 8
seed = 12345
```

![noise](f8o8_12345.bmp)
![noise](images/f8o8_12345.png)

---

```
frequency = 8.0
octaves = 8
seed = 23456
```

![noise](images/f8o8_23456.png)

---

```
frequency = 8.0
octaves = 3
seed = 23456
```

![noise](images/f8o3_23456.png)
119 changes: 54 additions & 65 deletions example.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

# include <cassert>
# include <iostream>
# include <fstream>
Expand All @@ -25,7 +24,6 @@ struct BMPHeader
std::uint32_t biClrUsed;
std::uint32_t biClrImportant;
};

static_assert(sizeof(BMPHeader) == 54);
# pragma pack (pop)

Expand All @@ -36,57 +34,35 @@ struct RGB
double b = 0.0;
constexpr RGB() = default;
explicit constexpr RGB(double _rgb) noexcept
: r(_rgb), g(_rgb), b(_rgb) {}
: r{ _rgb }, g{ _rgb }, b{ _rgb } {}
constexpr RGB(double _r, double _g, double _b) noexcept
: r(_r), g(_g), b(_b) {}
: r{ _r }, g{ _g }, b{ _b } {}
};

class Image
{
private:

std::vector<RGB> m_data;

std::int32_t m_width = 0, m_height = 0;

bool inBounds(std::int32_t y, std::int32_t x) const noexcept
{
return (0 <= y) && (y < m_height) && (0 <= x) && (x < m_width);
}

static constexpr std::uint8_t ToUint8(double x) noexcept
{
return x >= 1.0 ? 255 : x <= 0.0 ? 0 : static_cast<std::uint8_t>(x * 255.0 + 0.5);
}

public:

Image() = default;

Image(std::size_t width, std::size_t height)
: m_data(width * height)
, m_width(static_cast<std::int32_t>(width))
, m_height(static_cast<std::int32_t>(height)) {}
: m_data(width* height)
, m_width{ static_cast<std::int32_t>(width) }
, m_height{ static_cast<std::int32_t>(height) } {}

void set(std::int32_t x, std::int32_t y, const RGB& color)
{
if (!inBounds(y, x))
if (not inBounds(y, x))
{
return;
}

m_data[static_cast<std::size_t>(y) * m_width + x] = color;
}

std::int32_t width() const
{
return m_width;
}
std::int32_t width() const noexcept { return m_width; }

std::int32_t height() const
{
return m_height;
}
std::int32_t height() const noexcept { return m_height; }

bool saveBMP(const std::string& path)
{
Expand All @@ -96,20 +72,9 @@ class Image
{
0x4d42,
static_cast<std::uint32_t>(bmpsize + sizeof(BMPHeader)),
0,
0,
sizeof(BMPHeader),
40,
m_width,
m_height,
1,
24,
0,
bmpsize,
0,
0,
0,
0
0, 0, sizeof(BMPHeader), 40,
m_width, m_height, 1, 24,
0, bmpsize, 0, 0, 0, 0
};

if (std::ofstream ofs{ path, std::ios_base::binary })
Expand Down Expand Up @@ -140,38 +105,62 @@ class Image
return false;
}
}

private:

std::vector<RGB> m_data;

std::int32_t m_width = 0, m_height = 0;

bool inBounds(std::int32_t y, std::int32_t x) const noexcept
{
return (0 <= y) && (y < m_height) && (0 <= x) && (x < m_width);
}

static constexpr std::uint8_t ToUint8(double x) noexcept
{
return (x <= 0.0) ? 0 : (1.0 <= x) ? 255 : static_cast<std::uint8_t>(x * 255.0 + 0.5);
}
};

void Test()
{
siv::PerlinNoise perlinA(std::random_device{});
siv::PerlinNoise perlinA{ std::random_device{} };
siv::PerlinNoise perlinB;

std::array<std::uint8_t, 256> state;
perlinA.serialize(state);
perlinB.deserialize(state);
perlinB.deserialize(perlinA.serialize());

assert(perlinA.octave3D(0.1, 0.2, 0.3, 4)
== perlinB.octave3D(0.1, 0.2, 0.3, 4));

assert(perlinA.accumulatedOctaveNoise3D(0.1, 0.2, 0.3, 4)
== perlinB.accumulatedOctaveNoise3D(0.1, 0.2, 0.3, 4));
perlinA.reseed(12345u);
perlinB.reseed(12345u);

perlinA.reseed(1234);
perlinB.reseed(1234);
assert(perlinA.octave3D(0.1, 0.2, 0.3, 4)
== perlinB.octave3D(0.1, 0.2, 0.3, 4));

assert(perlinA.accumulatedOctaveNoise3D(0.1, 0.2, 0.3, 4)
== perlinB.accumulatedOctaveNoise3D(0.1, 0.2, 0.3, 4));
perlinA.reseed(std::mt19937{ 67890u });
perlinB.reseed(std::mt19937{ 67890u });

perlinA.reseed(std::mt19937{ 1234 });
perlinB.reseed(std::mt19937{ 1234 });
assert(perlinA.octave3D(0.1, 0.2, 0.3, 4)
== perlinB.octave3D(0.1, 0.2, 0.3, 4));

assert(perlinA.accumulatedOctaveNoise3D(0.1, 0.2, 0.3, 4)
== perlinB.accumulatedOctaveNoise3D(0.1, 0.2, 0.3, 4));
for (std::int32_t y = 0; y < 20; ++y)
{
for (std::int32_t x = 0; x < 20; ++x)
{
const double noise = perlinA.octave2D_01(x * 0.1, y * 0.1, 6);
std::cout << static_cast<int>(std::floor(noise * 10) - 0.5);
}
std::cout << '\n';
}
}

int main()
{
Test();

Image image(512, 512);
Image image{ 512, 512 };

std::cout << "---------------------------------\n";
std::cout << "* frequency [0.1 .. 8.0 .. 64.0] \n";
Expand All @@ -195,15 +184,15 @@ int main()
std::cout << "uint32 seed = ";
std::cin >> seed;

const siv::PerlinNoise perlin(seed);
const double fx = image.width() / frequency;
const double fy = image.height() / frequency;
const siv::PerlinNoise perlin{ seed };
const double fx = (frequency / image.width());
const double fy = (frequency / image.height());

for (std::int32_t y = 0; y < image.height(); ++y)
{
for (std::int32_t x = 0; x < image.width(); ++x)
{
const RGB color(perlin.accumulatedOctaveNoise2D_0_1(x / fx, y / fy, octaves));
const RGB color(perlin.octave2D_01((x * fx), (y * fy), octaves));
image.set(x, y, color);
}
}
Expand Down
Binary file removed f8o8_12345.bmp
Binary file not shown.
Binary file added images/f8o3_23456.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/f8o8_12345.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/f8o8_23456.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/top.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.