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/pcg/fractal.hpp>
|
||||||
#include <psemek/random/device.hpp>
|
#include <psemek/random/device.hpp>
|
||||||
#include <psemek/random/generator.hpp>
|
#include <psemek/random/generator.hpp>
|
||||||
|
#include <psemek/random/uniform.hpp>
|
||||||
#include <psemek/random/uniform_sphere.hpp>
|
#include <psemek/random/uniform_sphere.hpp>
|
||||||
#include <psemek/gfx/pixmap.hpp>
|
#include <psemek/gfx/pixmap.hpp>
|
||||||
#include <psemek/util/hash_table.hpp>
|
#include <psemek/util/hash_table.hpp>
|
||||||
|
|
@ -10,10 +11,32 @@
|
||||||
using namespace psemek;
|
using namespace psemek;
|
||||||
|
|
||||||
template <int D>
|
template <int D>
|
||||||
util::array<std::uint8_t, D> generate(std::array<std::size_t, D> const & size, int octaves, int minoctave, float power,
|
auto moore_neighbourhood()
|
||||||
bool tile, float gamma, bool remap, std::uint64_t seed)
|
|
||||||
{
|
{
|
||||||
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 ";
|
std::cout << "Size ";
|
||||||
for (int d = 0; d < D; ++d)
|
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 << "\nOctaves: " << minoctave << ".." << (minoctave + octaves - 1) << "\n";
|
||||||
std::cout << "Power: " << power << "\n";
|
std::cout << "Power: " << power << "\n";
|
||||||
|
std::cout << "Invert: " << (invert ? "true" : "false") << "\n";
|
||||||
std::cout << "Tile: " << (tile ? "true" : "false") << "\n";
|
std::cout << "Tile: " << (tile ? "true" : "false") << "\n";
|
||||||
std::cout << "Gamma: " << gamma << "\n";
|
std::cout << "Gamma: " << gamma << "\n";
|
||||||
std::cout << "Remap: " << (remap ? "true" : "false") << "\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);
|
float v = result_float(idx);
|
||||||
if (remap)
|
if (remap)
|
||||||
v = math::unlerp(value_range, v);
|
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);
|
v = std::pow(v, gamma);
|
||||||
result(idx) = static_cast<std::uint8_t>(std::clamp(v * 255.f, 0.f, 255.f));
|
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"(
|
R"(
|
||||||
Available options:
|
Available options:
|
||||||
|
|
||||||
|
type
|
||||||
|
Noise type (perlin or worley)
|
||||||
|
Default = worley
|
||||||
|
|
||||||
dim
|
dim
|
||||||
Noise dimension (2 or 3)
|
Noise dimension (2 or 3)
|
||||||
Default = 2
|
Default = 2
|
||||||
|
|
@ -132,6 +301,10 @@ Available options:
|
||||||
Octaves weight power (0.0 means all octaves have equal contributions), can be negative
|
Octaves weight power (0.0 means all octaves have equal contributions), can be negative
|
||||||
Default = 1.0
|
Default = 1.0
|
||||||
|
|
||||||
|
invert
|
||||||
|
Replace noise values with 1-value (makes sense for worley noise only)
|
||||||
|
Default = false
|
||||||
|
|
||||||
tile
|
tile
|
||||||
Whether to make the noise tileable (seamless)
|
Whether to make the noise tileable (seamless)
|
||||||
Default = true
|
Default = true
|
||||||
|
|
@ -200,6 +373,7 @@ std::optional<bool> parse_bool(std::string const & str)
|
||||||
int main(int argc, char * argv[])
|
int main(int argc, char * argv[])
|
||||||
{
|
{
|
||||||
util::hash_map<std::string, std::string> values;
|
util::hash_map<std::string, std::string> values;
|
||||||
|
values["type"] = "perlin";
|
||||||
values["dim"] = "2";
|
values["dim"] = "2";
|
||||||
values["sizex"] = "256";
|
values["sizex"] = "256";
|
||||||
values["sizey"] = "256";
|
values["sizey"] = "256";
|
||||||
|
|
@ -207,6 +381,7 @@ int main(int argc, char * argv[])
|
||||||
values["octaves"] = "8";
|
values["octaves"] = "8";
|
||||||
values["minoctave"] = "0";
|
values["minoctave"] = "0";
|
||||||
values["power"] = "1.0";
|
values["power"] = "1.0";
|
||||||
|
values["invert"] = "false";
|
||||||
values["tile"] = "true";
|
values["tile"] = "true";
|
||||||
values["gamma"] = "1.0";
|
values["gamma"] = "1.0";
|
||||||
values["remap"] = "true";
|
values["remap"] = "true";
|
||||||
|
|
@ -243,6 +418,7 @@ int main(int argc, char * argv[])
|
||||||
values[key] = value;
|
values[key] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto type = values["type"];
|
||||||
auto dim = parse_int(values["dim"]);
|
auto dim = parse_int(values["dim"]);
|
||||||
auto sizex = parse_int(values["sizex"]);
|
auto sizex = parse_int(values["sizex"]);
|
||||||
auto sizey = parse_int(values["sizey"]);
|
auto sizey = parse_int(values["sizey"]);
|
||||||
|
|
@ -250,6 +426,7 @@ int main(int argc, char * argv[])
|
||||||
auto octaves = parse_int(values["octaves"]);
|
auto octaves = parse_int(values["octaves"]);
|
||||||
auto minoctave = parse_int(values["minoctave"]);
|
auto minoctave = parse_int(values["minoctave"]);
|
||||||
auto power = parse_float(values["power"]);
|
auto power = parse_float(values["power"]);
|
||||||
|
auto invert = parse_bool(values["invert"]);
|
||||||
auto tile = parse_bool(values["tile"]);
|
auto tile = parse_bool(values["tile"]);
|
||||||
auto gamma = parse_float(values["gamma"]);
|
auto gamma = parse_float(values["gamma"]);
|
||||||
auto remap = parse_bool(values["remap"]);
|
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"], 10);
|
||||||
if (!seed) seed = parse_seed(values["seed"], 16);
|
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)) {
|
if (!dim || (*dim != 2 && *dim != 3)) {
|
||||||
std::cerr << "Unknown noise dimension: " << values["dim"] << "\n";
|
std::cerr << "Unknown noise dimension: " << values["dim"] << "\n";
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
|
|
@ -297,6 +480,11 @@ int main(int argc, char * argv[])
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!invert) {
|
||||||
|
std::cerr << "Invert must be true or false: " << values["invert"] << "\n";
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
if (!tile) {
|
if (!tile) {
|
||||||
std::cerr << "Tile must be true or false: " << values["tile"] << "\n";
|
std::cerr << "Tile must be true or false: " << values["tile"] << "\n";
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
|
|
@ -319,7 +507,11 @@ int main(int argc, char * argv[])
|
||||||
|
|
||||||
if (*dim == 2)
|
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)};
|
io::file_ostream out{std::filesystem::path(output_path)};
|
||||||
|
|
||||||
|
|
@ -347,7 +539,11 @@ int main(int argc, char * argv[])
|
||||||
|
|
||||||
if (*dim == 3)
|
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)};
|
io::file_ostream out{std::filesystem::path(output_path)};
|
||||||
std::cout << "Saving raw image to " << output_path << std::endl;
|
std::cout << "Saving raw image to " << output_path << std::endl;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue