can be improved.

This commit is contained in:
Yggdrasil75
2025-11-07 13:03:36 -05:00
parent cc81f08b11
commit 82c0e2527f
7 changed files with 818 additions and 30 deletions

300
util/noise2.hpp Normal file
View File

@@ -0,0 +1,300 @@
#ifndef NOISE2_HPP
#define NOISE2_HPP
#include "grid2.hpp"
#include <cmath>
#include <random>
#include <functional>
#include <algorithm>
struct Grad { float x, y; };
std::array<Grad, 256> gradients;
class Noise2 {
private:
std::mt19937 rng;
std::uniform_real_distribution<float> dist;
public:
Noise2(uint32_t seed = 0) : rng(seed), dist(0.0f, 1.0f) {}
// Set random seed
void setSeed(uint32_t seed) {
rng.seed(seed);
}
// Generate simple value noise
float valueNoise(float x, float y, int octaves = 1, float persistence = 0.5f, float lacunarity = 2.0f) {
float total = 0.0f;
float frequency = 1.0f;
float amplitude = 1.0f;
float maxValue = 0.0f;
for (int i = 0; i < octaves; i++) {
total += rawNoise(x * frequency, y * frequency) * amplitude;
maxValue += amplitude;
amplitude *= persistence;
frequency *= lacunarity;
}
return total / maxValue;
}
// Generate Perlin-like noise
float perlinNoise(float x, float y, int octaves = 1, float persistence = 0.5f, float lacunarity = 2.0f) {
float total = 0.0f;
float frequency = 1.0f;
float amplitude = 1.0f;
float maxValue = 0.0f;
for (int i = 0; i < octaves; i++) {
total += improvedNoise(x * frequency, y * frequency) * amplitude;
maxValue += amplitude;
amplitude *= persistence;
frequency *= lacunarity;
}
return (total / maxValue + 1.0f) * 0.5f; // Normalize to [0,1]
}
// Generate a grayscale noise grid
Grid2 generateGrayNoise(int width, int height,
float scale = 1.0f,
int octaves = 1,
float persistence = 0.5f,
uint32_t seed = 0,
const Vec2& offset = Vec2(0, 0)) {
if (seed != 0) setSeed(seed);
Grid2 grid(width * height);
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
float nx = (x + offset.x) / width * scale;
float ny = (y + offset.y) / height * scale;
float noiseValue = perlinNoise(nx, ny, octaves, persistence);
// Convert to position and grayscale color
Vec2 position(x, y);
Vec4 color(noiseValue, noiseValue, noiseValue, 1.0f);
grid.positions[y * width + x] = position;
grid.colors[y * width + x] = color;
}
}
return grid;
}
float pascalTri(const float& a, const float& b) {
TIME_FUNCTION;
int result = 1;
for (int i = 0; i < b; ++i){
result *= (a - 1) / (i + 1);
}
return result;
}
float genSmooth(int N, float x) {
TIME_FUNCTION;
x = clamp(x, 0, 1);
float result = 0;
for (int n = 0; n <= N; ++n){
result += pascalTri(-N - 1, n) * pascalTri(2 * N + 1, N-1) * pow(x, N + n + 1);
}
return result;
}
float inverse_smoothstep(float x) {
TIME_FUNCTION;
return 0.5 - sin(asin(1.0 - 2.0 * x) / 3.0);
}
// Generate multi-layered RGBA noise
Grid2 generateRGBANoise(int width, int height,
const Vec4& scale = Vec4(1.0f, 1.0f, 1.0f, 1.0f),
const Vec4& octaves = Vec4(1.0f, 1.0f, 1.0f, 1.0f),
const Vec4& persistence = Vec4(0.5f, 0.5f, 0.5f, 0.5f),
uint32_t seed = 0,
const Vec2& offset = Vec2(0, 0)) {
if (seed != 0) setSeed(seed);
Grid2 grid(width * height);
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
float nx = (x + offset.x) / width;
float ny = (y + offset.y) / height;
// Generate separate noise for each channel
float r = perlinNoise(nx * scale.x, ny * scale.x,
static_cast<int>(octaves.x), persistence.x);
float g = perlinNoise(nx * scale.y, ny * scale.y,
static_cast<int>(octaves.y), persistence.y);
float b = perlinNoise(nx * scale.z, ny * scale.z,
static_cast<int>(octaves.z), persistence.z);
float a = perlinNoise(nx * scale.w, ny * scale.w,
static_cast<int>(octaves.w), persistence.w);
Vec2 position(x, y);
Vec4 color(r, g, b, a);
grid.positions[y * width + x] = position;
grid.colors[y * width + x] = color;
}
}
return grid;
}
// Generate terrain-like noise (useful for heightmaps)
Grid2 generateTerrainNoise(int width, int height,
float scale = 1.0f,
int octaves = 4,
float persistence = 0.5f,
uint32_t seed = 0,
const Vec2& offset = Vec2(0, 0)) {
if (seed != 0) setSeed(seed);
Grid2 grid(width * height);
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
float nx = (x + offset.x) / width * scale;
float ny = (y + offset.y) / height * scale;
// Use multiple octaves for more natural terrain
float heightValue = perlinNoise(nx, ny, octaves, persistence);
// Apply some curve to make it more terrain-like
heightValue = std::pow(heightValue, 1.5f);
Vec2 position(x, y);
Vec4 color(heightValue, heightValue, heightValue, 1.0f);
grid.positions[y * width + x] = position;
grid.colors[y * width + x] = color;
}
}
return grid;
}
// Generate cloud-like noise
Grid2 generateCloudNoise(int width, int height,
float scale = 2.0f,
int octaves = 3,
float persistence = 0.7f,
uint32_t seed = 0,
const Vec2& offset = Vec2(0, 0)) {
auto grid = generateGrayNoise(width, height, scale, octaves, persistence, seed, offset);
// Apply soft threshold for cloud effect
for (auto& color : grid.colors) {
float value = color.x; // Assuming grayscale in red channel
// Soft threshold: values below 0.3 become 0, above 0.7 become 1, smooth in between
if (value < 0.3f) value = 0.0f;
else if (value > 0.7f) value = 1.0f;
else value = (value - 0.3f) / 0.4f; // Linear interpolation
color = Vec4(value, value, value, 1.0f);
}
return grid;
}
private:
// Raw noise function (simple hash-based)
float rawNoise(float x, float y) {
// Simple hash function for deterministic noise
int xi = static_cast<int>(std::floor(x));
int yi = static_cast<int>(std::floor(y));
// Use the RNG to generate consistent noise based on grid position
rng.seed(xi * 1619 + yi * 31337);
return dist(rng);
}
// Improved noise function (Perlin-like)
float improvedNoise(float x, float y) {
// Integer part
int xi = static_cast<int>(std::floor(x));
int yi = static_cast<int>(std::floor(y));
// Fractional part
float xf = x - xi;
float yf = y - yi;
// Smooth interpolation
float u = fade(xf);
float v = fade(yf);
// Gradient noise from corners
float n00 = gradNoise(xi, yi, xf, yf);
float n01 = gradNoise(xi, yi + 1, xf, yf - 1);
float n10 = gradNoise(xi + 1, yi, xf - 1, yf);
float n11 = gradNoise(xi + 1, yi + 1, xf - 1, yf - 1);
// Bilinear interpolation
float x1 = lerp(n00, n10, u);
float x2 = lerp(n01, n11, u);
return lerp(x1, x2, v);
}
// Fade function for smooth interpolation
float fade(float t) {
return t * t * t * (t * (t * 6 - 15) + 10);
}
// Linear interpolation
float lerp(float a, float b, float t) {
return a + t * (b - a);
}
float clamp(float x, float lowerlimit = 0.0f, float upperlimit = 1.0f) {
TIME_FUNCTION;
if (x < lowerlimit) return lowerlimit;
if (x > upperlimit) return upperlimit;
return x;
}
// float grad(const int& hash, const float& b, const float& c, const float& d) {
// TIME_FUNCTION;
// int h = hash & 15;
// float u = (h < 8) ? c : b;
// float v = (h < 4) ? b : ((h == 12 || h == 14) ? c : d);
// return (((h & 1) == 0) ? u : -u) + (((h & 2) == 0) ? v : -v);
// }
float gradNoise(int xi, int yi, float xf, float yf) {
// Generate deterministic "random" unit vector using hash
int hash = (xi * 1619 + yi * 31337);
// Use hash to generate angle in fixed steps (faster than trig)
float angle = (hash & 255) * (2.0f * 3.14159265f / 256.0f);
// Or even faster: use gradient table with 8 or 16 precomputed directions
int gradIndex = hash & 7; // 8 directions
static constexpr std::array<Grad, 8> grads = {
{1,0}, {0.707f,0.707f}, {0,1}, {-0.707f,0.707f},
{-1,0}, {-0.707f,-0.707f}, {0,-1}, {0.707f,-0.707f}
};
return xf * grads[gradIndex].x + yf * grads[gradIndex].y;
}
// Gradient noise function
float slowGradNoise(int xi, int yi, float xf, float yf) {
// Generate consistent random gradient from integer coordinates
rng.seed(xi * 1619 + yi * 31337);
float angle = dist(rng) * 2.0f * 3.14159265f;
// Gradient vector
float gx = std::cos(angle);
float gy = std::sin(angle);
// Dot product
return xf * gx + yf * gy;
}
};
#endif