Support worley noise in noise-generator tool
This commit is contained in:
parent
08510e265b
commit
afd942af14
1 changed files with 201 additions and 5 deletions
|
|
@ -2,6 +2,7 @@
|
|||
#include <psemek/pcg/fractal.hpp>
|
||||
#include <psemek/random/device.hpp>
|
||||
#include <psemek/random/generator.hpp>
|
||||
#include <psemek/random/uniform.hpp>
|
||||
#include <psemek/random/uniform_sphere.hpp>
|
||||
#include <psemek/gfx/pixmap.hpp>
|
||||
#include <psemek/util/hash_table.hpp>
|
||||
|
|
@ -10,10 +11,32 @@
|
|||
using namespace psemek;
|
||||
|
||||
template <int D>
|
||||
util::array<std::uint8_t, D> generate(std::array<std::size_t, D> const & size, int octaves, int minoctave, float power,
|
||||
bool tile, float gamma, bool remap, std::uint64_t seed)
|
||||
auto moore_neighbourhood()
|
||||
{
|
||||
std::cout << "Generating " << D << "D noise\n";
|
||||
if constexpr (D == 2)
|
||||
{
|
||||
std::array<math::vector<int, D>, 9> result;
|
||||
for (int dy = -1; dy <= 1; ++dy)
|
||||
for (int dx = -1; dx <= 1; ++dx)
|
||||
result[(dy + 1) * 3 + (dx + 1)] = {dx, dy};
|
||||
return result;
|
||||
}
|
||||
else if constexpr (D == 3)
|
||||
{
|
||||
std::array<math::vector<int, D>, 27> result;
|
||||
for (int dz = -1; dz <= 1; ++dz)
|
||||
for (int dy = -1; dy <= 1; ++dy)
|
||||
for (int dx = -1; dx <= 1; ++dx)
|
||||
result[(dz + 1) * 9 + (dy + 1) * 3 + (dx + 1)] = {dx, dy, dz};
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
template <int D>
|
||||
util::array<std::uint8_t, D> generate_perlin(std::array<std::size_t, D> const & size, int octaves, int minoctave, float power,
|
||||
bool invert, bool tile, float gamma, bool remap, std::uint64_t seed)
|
||||
{
|
||||
std::cout << "Generating " << D << "D perlin noise\n";
|
||||
std::cout << "Size ";
|
||||
for (int d = 0; d < D; ++d)
|
||||
{
|
||||
|
|
@ -24,6 +47,7 @@ util::array<std::uint8_t, D> generate(std::array<std::size_t, D> const & size, i
|
|||
|
||||
std::cout << "\nOctaves: " << minoctave << ".." << (minoctave + octaves - 1) << "\n";
|
||||
std::cout << "Power: " << power << "\n";
|
||||
std::cout << "Invert: " << (invert ? "true" : "false") << "\n";
|
||||
std::cout << "Tile: " << (tile ? "true" : "false") << "\n";
|
||||
std::cout << "Gamma: " << gamma << "\n";
|
||||
std::cout << "Remap: " << (remap ? "true" : "false") << "\n";
|
||||
|
|
@ -93,6 +117,147 @@ util::array<std::uint8_t, D> generate(std::array<std::size_t, D> const & size, i
|
|||
float v = result_float(idx);
|
||||
if (remap)
|
||||
v = math::unlerp(value_range, v);
|
||||
if (invert)
|
||||
v = 1.f - v;
|
||||
v = std::pow(v, gamma);
|
||||
result(idx) = static_cast<std::uint8_t>(std::clamp(v * 255.f, 0.f, 255.f));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template <int D>
|
||||
util::array<std::uint8_t, D> generate_worley(std::array<std::size_t, D> const & size, int octaves, int minoctave, float power,
|
||||
bool invert, bool tile, float gamma, bool remap, std::uint64_t seed)
|
||||
{
|
||||
std::cout << "Generating " << D << "D worley noise\n";
|
||||
std::cout << "Size ";
|
||||
for (int d = 0; d < D; ++d)
|
||||
{
|
||||
if (d > 0)
|
||||
std::cout << "x";
|
||||
std::cout << size[d];
|
||||
}
|
||||
|
||||
std::cout << "\nOctaves: " << minoctave << ".." << (minoctave + octaves - 1) << "\n";
|
||||
std::cout << "Power: " << power << "\n";
|
||||
std::cout << "Invert: " << (invert ? "true" : "false") << "\n";
|
||||
std::cout << "Tile: " << (tile ? "true" : "false") << "\n";
|
||||
std::cout << "Gamma: " << gamma << "\n";
|
||||
std::cout << "Remap: " << (remap ? "true" : "false") << "\n";
|
||||
std::cout << "Seed: " << std::hex << std::setw(16) << std::setfill('0') << seed << "\n";
|
||||
std::cout << std::flush;
|
||||
|
||||
random::generator rng{seed, 0};
|
||||
|
||||
std::vector<util::array<math::point<float, D>, D>> octave_points;
|
||||
std::vector<float> octave_weights;
|
||||
float sum_octave_weights = 0.f;
|
||||
|
||||
for (int o = minoctave; o < minoctave + octaves; ++o)
|
||||
{
|
||||
std::array<std::size_t, D> octave_size;
|
||||
for (int d = 0; d < D; ++d)
|
||||
octave_size[d] = (1 << o);
|
||||
|
||||
util::array<math::point<float, D>, D> octave(octave_size);
|
||||
|
||||
for (auto idx : octave.indices())
|
||||
{
|
||||
auto & p = octave(idx);
|
||||
for (int d = 0; d < D; ++d)
|
||||
p[d] = idx[d] + random::uniform<float>(rng);
|
||||
}
|
||||
|
||||
octave_points.push_back(std::move(octave));
|
||||
|
||||
float weight = std::pow(static_cast<float>(1 << o), - power);
|
||||
octave_weights.push_back(weight);
|
||||
sum_octave_weights += weight;
|
||||
}
|
||||
|
||||
for (auto & w : octave_weights)
|
||||
w /= sum_octave_weights;
|
||||
|
||||
util::array<float, D> result_float(size);
|
||||
math::interval<float> value_range;
|
||||
|
||||
for (auto idx : result_float.indices())
|
||||
{
|
||||
math::point<float, D> p;
|
||||
|
||||
for (int d = 0; d < D; ++d)
|
||||
p[d] = (idx[d] + 0.5f) / size[d] * (1 << minoctave);
|
||||
|
||||
float v = 0.f;
|
||||
|
||||
for (int o = 0; o < octaves; ++o)
|
||||
{
|
||||
math::point<int, D> i;
|
||||
for (int d = 0; d < D; ++d)
|
||||
i[d] = std::floor(p[d]);
|
||||
|
||||
int octave_size = 1 << (o + minoctave);
|
||||
|
||||
float closest_distance = std::numeric_limits<float>::infinity();
|
||||
|
||||
for (auto n : moore_neighbourhood<D>())
|
||||
{
|
||||
math::point<int, D> j = i + n;
|
||||
|
||||
if (tile)
|
||||
{
|
||||
for (int d = 0; d < D; ++d)
|
||||
j[d] = (j[d] + octave_size) % octave_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool good = true;
|
||||
for (int d = 0; d < D; ++d)
|
||||
{
|
||||
good &= (j[d] >= 0);
|
||||
good &= (j[d] < octave_size);
|
||||
}
|
||||
|
||||
if (!good)
|
||||
continue;
|
||||
}
|
||||
|
||||
math::vector<float, D> r = octave_points[o](j) - p;
|
||||
|
||||
if (tile)
|
||||
{
|
||||
for (int d = 0; d < D; ++d)
|
||||
{
|
||||
if (i[d] + n[d] == -1)
|
||||
r[d] -= octave_size;
|
||||
else if (i[d] + n[d] == octave_size)
|
||||
r[d] += octave_size;
|
||||
}
|
||||
}
|
||||
|
||||
math::make_min(closest_distance, math::length(r));
|
||||
}
|
||||
|
||||
v += closest_distance * octave_weights[o] / std::sqrt(2.f);
|
||||
|
||||
for (int d = 0; d < D; ++d)
|
||||
p[d] *= 2.f;
|
||||
}
|
||||
|
||||
result_float(idx) = v;
|
||||
value_range |= v;
|
||||
}
|
||||
|
||||
util::array<std::uint8_t, D> result(size);
|
||||
|
||||
for (auto idx : result.indices())
|
||||
{
|
||||
float v = result_float(idx);
|
||||
if (remap)
|
||||
v = math::unlerp(value_range, v);
|
||||
if (invert)
|
||||
v = 1.f - v;
|
||||
v = std::pow(v, gamma);
|
||||
result(idx) = static_cast<std::uint8_t>(std::clamp(v * 255.f, 0.f, 255.f));
|
||||
}
|
||||
|
|
@ -104,6 +269,10 @@ char const options_usage[] =
|
|||
R"(
|
||||
Available options:
|
||||
|
||||
type
|
||||
Noise type (perlin or worley)
|
||||
Default = worley
|
||||
|
||||
dim
|
||||
Noise dimension (2 or 3)
|
||||
Default = 2
|
||||
|
|
@ -132,6 +301,10 @@ Available options:
|
|||
Octaves weight power (0.0 means all octaves have equal contributions), can be negative
|
||||
Default = 1.0
|
||||
|
||||
invert
|
||||
Replace noise values with 1-value (makes sense for worley noise only)
|
||||
Default = false
|
||||
|
||||
tile
|
||||
Whether to make the noise tileable (seamless)
|
||||
Default = true
|
||||
|
|
@ -200,6 +373,7 @@ std::optional<bool> parse_bool(std::string const & str)
|
|||
int main(int argc, char * argv[])
|
||||
{
|
||||
util::hash_map<std::string, std::string> values;
|
||||
values["type"] = "perlin";
|
||||
values["dim"] = "2";
|
||||
values["sizex"] = "256";
|
||||
values["sizey"] = "256";
|
||||
|
|
@ -207,6 +381,7 @@ int main(int argc, char * argv[])
|
|||
values["octaves"] = "8";
|
||||
values["minoctave"] = "0";
|
||||
values["power"] = "1.0";
|
||||
values["invert"] = "false";
|
||||
values["tile"] = "true";
|
||||
values["gamma"] = "1.0";
|
||||
values["remap"] = "true";
|
||||
|
|
@ -243,6 +418,7 @@ int main(int argc, char * argv[])
|
|||
values[key] = value;
|
||||
}
|
||||
|
||||
auto type = values["type"];
|
||||
auto dim = parse_int(values["dim"]);
|
||||
auto sizex = parse_int(values["sizex"]);
|
||||
auto sizey = parse_int(values["sizey"]);
|
||||
|
|
@ -250,6 +426,7 @@ int main(int argc, char * argv[])
|
|||
auto octaves = parse_int(values["octaves"]);
|
||||
auto minoctave = parse_int(values["minoctave"]);
|
||||
auto power = parse_float(values["power"]);
|
||||
auto invert = parse_bool(values["invert"]);
|
||||
auto tile = parse_bool(values["tile"]);
|
||||
auto gamma = parse_float(values["gamma"]);
|
||||
auto remap = parse_bool(values["remap"]);
|
||||
|
|
@ -262,6 +439,12 @@ int main(int argc, char * argv[])
|
|||
if (!seed) seed = parse_seed(values["seed"], 10);
|
||||
if (!seed) seed = parse_seed(values["seed"], 16);
|
||||
|
||||
if (type != "perlin" && type != "worley")
|
||||
{
|
||||
std::cerr << "Unknown noise type: " << values["type"] << "\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (!dim || (*dim != 2 && *dim != 3)) {
|
||||
std::cerr << "Unknown noise dimension: " << values["dim"] << "\n";
|
||||
return EXIT_FAILURE;
|
||||
|
|
@ -297,6 +480,11 @@ int main(int argc, char * argv[])
|
|||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (!invert) {
|
||||
std::cerr << "Invert must be true or false: " << values["invert"] << "\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (!tile) {
|
||||
std::cerr << "Tile must be true or false: " << values["tile"] << "\n";
|
||||
return EXIT_FAILURE;
|
||||
|
|
@ -319,7 +507,11 @@ int main(int argc, char * argv[])
|
|||
|
||||
if (*dim == 2)
|
||||
{
|
||||
auto result = generate<2>({*sizex, *sizey}, *octaves, *minoctave, *power, *tile, *gamma, *remap, *seed);
|
||||
util::array<std::uint8_t, 2> result;
|
||||
if (type == "perlin")
|
||||
result = generate_perlin<2>({*sizex, *sizey}, *octaves, *minoctave, *power, *invert, *tile, *gamma, *remap, *seed);
|
||||
else if (type == "worley")
|
||||
result = generate_worley<2>({*sizex, *sizey}, *octaves, *minoctave, *power, *invert, *tile, *gamma, *remap, *seed);
|
||||
|
||||
io::file_ostream out{std::filesystem::path(output_path)};
|
||||
|
||||
|
|
@ -347,7 +539,11 @@ int main(int argc, char * argv[])
|
|||
|
||||
if (*dim == 3)
|
||||
{
|
||||
auto result = generate<3>({*sizex, *sizey, *sizez}, *octaves, *minoctave, *power, *tile, *gamma, *remap, *seed);
|
||||
util::array<std::uint8_t, 3> result;
|
||||
if (type == "perlin")
|
||||
generate_perlin<3>({*sizex, *sizey, *sizez}, *octaves, *minoctave, *power, *invert, *tile, *gamma, *remap, *seed);
|
||||
else if (type == "worley")
|
||||
result = generate_worley<3>({*sizex, *sizey, *sizez}, *octaves, *minoctave, *power, *invert, *tile, *gamma, *remap, *seed);
|
||||
|
||||
io::file_ostream out{std::filesystem::path(output_path)};
|
||||
std::cout << "Saving raw image to " << output_path << std::endl;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue