fix value and fractal noise

This commit is contained in:
Yggdrasil75
2026-02-03 13:05:26 -05:00
parent d51f6ba3da
commit 62adc8575b
2 changed files with 56 additions and 82 deletions

View File

@@ -1,4 +1,5 @@
#pragma once #ifndef PNOISE_CPP
#define PNOISE_CPP
#include <vector> #include <vector>
#include <cmath> #include <cmath>
@@ -7,6 +8,7 @@
#include <iostream> #include <iostream>
#include "./pnoise2.hpp" #include "./pnoise2.hpp"
#include "../timing_decorator.hpp"
#include "../../imgui/imgui.h" #include "../../imgui/imgui.h"
#include <GLFW/glfw3.h> #include <GLFW/glfw3.h>
@@ -110,6 +112,7 @@ inline float sampleNoiseLayer(PNoise2& gen, NoiseType type, Eigen::Vector2f poin
} }
inline void updateNoiseTexture(NoisePreviewState& state) { inline void updateNoiseTexture(NoisePreviewState& state) {
TIME_FUNCTION;
if (state.textureId == 0) glGenTextures(1, &state.textureId); if (state.textureId == 0) glGenTextures(1, &state.textureId);
state.pixelBuffer.resize(state.width * state.height * 3); state.pixelBuffer.resize(state.width * state.height * 3);
@@ -120,24 +123,19 @@ inline void updateNoiseTexture(NoisePreviewState& state) {
for (int x = 0; x < state.width; ++x) { for (int x = 0; x < state.width; ++x) {
// 1. Initial Coordinate
float nx = (x + state.offset[0]); float nx = (x + state.offset[0]);
float ny = (y + state.offset[1]); float ny = (y + state.offset[1]);
Eigen::Vector2f point(nx, ny); Eigen::Vector2f point(nx, ny);
float finalValue = 0.0f; float finalValue = 0.0f;
// 2. Process Layer Stack
for (const auto& layer : state.layers) { for (const auto& layer : state.layers) {
if (!layer.enabled) continue; if (!layer.enabled) continue;
// Adjust coordinate frequency for this layer
Eigen::Vector2f samplePoint = point * layer.scale; Eigen::Vector2f samplePoint = point * layer.scale;
// Cheat seed offset
samplePoint += Eigen::Vector2f((float)layer.seedOffset * 10.5f, (float)layer.seedOffset * -10.5f); samplePoint += Eigen::Vector2f((float)layer.seedOffset * 10.5f, (float)layer.seedOffset * -10.5f);
// Special Case: Coordinate Mutators
if (layer.blend == BlendMode::DomainWarp) { if (layer.blend == BlendMode::DomainWarp) {
if (layer.type == NoiseType::CurlNoise) { if (layer.type == NoiseType::CurlNoise) {
Eigen::Vector2f flow = generator.curlNoise(samplePoint); Eigen::Vector2f flow = generator.curlNoise(samplePoint);
@@ -150,7 +148,6 @@ inline void updateNoiseTexture(NoisePreviewState& state) {
continue; continue;
} }
// Standard Arithmetic Layers
float nVal = sampleNoiseLayer(generator, layer.type, samplePoint, layer); float nVal = sampleNoiseLayer(generator, layer.type, samplePoint, layer);
switch (layer.blend) { switch (layer.blend) {
@@ -175,7 +172,6 @@ inline void updateNoiseTexture(NoisePreviewState& state) {
} }
} }
// 3. Normalize for display
float norm = std::tanh(finalValue); float norm = std::tanh(finalValue);
norm = (norm + 1.0f) * 0.5f; norm = (norm + 1.0f) * 0.5f;
norm = std::clamp(norm, 0.0f, 1.0f); norm = std::clamp(norm, 0.0f, 1.0f);
@@ -223,16 +219,13 @@ inline void drawNoiseLab(NoisePreviewState& noiseState) {
ImGui::BeginChild("LayersScroll", ImVec2(0, 300), true); ImGui::BeginChild("LayersScroll", ImVec2(0, 300), true);
// Layer List
for (int i = 0; i < noiseState.layers.size(); ++i) { for (int i = 0; i < noiseState.layers.size(); ++i) {
NoiseLayer& layer = noiseState.layers[i]; NoiseLayer& layer = noiseState.layers[i];
ImGui::PushID(i); ImGui::PushID(i);
// Header
bool open = ImGui::CollapsingHeader(layer.name, ImGuiTreeNodeFlags_DefaultOpen); bool open = ImGui::CollapsingHeader(layer.name, ImGuiTreeNodeFlags_DefaultOpen);
// Context menu to move/delete
if (ImGui::BeginPopupContextItem()) { if (ImGui::BeginPopupContextItem()) {
if (ImGui::MenuItem("Move Up", nullptr, false, i > 0)) { if (ImGui::MenuItem("Move Up", nullptr, false, i > 0)) {
std::swap(noiseState.layers[i], noiseState.layers[i-1]); std::swap(noiseState.layers[i], noiseState.layers[i-1]);
@@ -265,19 +258,16 @@ inline void drawNoiseLab(NoisePreviewState& noiseState) {
ImGui::SameLine(); ImGui::SameLine();
ImGui::InputText("##name", layer.name, 32); ImGui::InputText("##name", layer.name, 32);
// Type and Blend
const char* types[] = { "Perlin", "Value", "Fractal", "Turbulence", "Ridged", "Billow", "White", "Worley", "Voronoi", "Crystal", "Domain Warp", "Curl" }; const char* types[] = { "Perlin", "Value", "Fractal", "Turbulence", "Ridged", "Billow", "White", "Worley", "Voronoi", "Crystal", "Domain Warp", "Curl" };
const char* blends[] = { "Add", "Subtract", "Multiply", "Min", "Max", "Replace", "Domain Warp (Coord)" }; const char* blends[] = { "Add", "Subtract", "Multiply", "Min", "Max", "Replace", "Domain Warp (Coord)" };
if (ImGui::Combo("Type", (int*)&layer.type, types, IM_ARRAYSIZE(types))) changed = true; if (ImGui::Combo("Type", (int*)&layer.type, types, IM_ARRAYSIZE(types))) changed = true;
if (ImGui::Combo("Blend", (int*)&layer.blend, blends, IM_ARRAYSIZE(blends))) changed = true; if (ImGui::Combo("Blend", (int*)&layer.blend, blends, IM_ARRAYSIZE(blends))) changed = true;
// Common Params
if (ImGui::SliderFloat("Scale", &layer.scale, 0.0001f, 0.2f, "%.5f")) changed = true; if (ImGui::SliderFloat("Scale", &layer.scale, 0.0001f, 0.2f, "%.5f")) changed = true;
if (ImGui::SliderFloat("Strength/Weight", &layer.strength, 0.0f, 5.0f)) changed = true; if (ImGui::SliderFloat("Strength/Weight", &layer.strength, 0.0f, 5.0f)) changed = true;
if (ImGui::SliderInt("Seed Offset", &layer.seedOffset, 0, 100)) changed = true; if (ImGui::SliderInt("Seed Offset", &layer.seedOffset, 0, 100)) changed = true;
// Conditional Params
if (layer.type == NoiseType::Fractal || layer.type == NoiseType::Turbulence || if (layer.type == NoiseType::Fractal || layer.type == NoiseType::Turbulence ||
layer.type == NoiseType::Ridged || layer.type == NoiseType::Billow) { layer.type == NoiseType::Ridged || layer.type == NoiseType::Billow) {
@@ -300,14 +290,14 @@ inline void drawNoiseLab(NoisePreviewState& noiseState) {
ImGui::Separator(); ImGui::Separator();
// Preview
ImGui::Text("Preview Output"); ImGui::Text("Preview Output");
ImGui::Image((void*)(intptr_t)noiseState.textureId, ImVec2((float)noiseState.width, (float)noiseState.height)); ImGui::Image((void*)(intptr_t)noiseState.textureId, ImVec2((float)noiseState.width, (float)noiseState.height));
// Auto update logic
if (changed || noiseState.needsUpdate) { if (changed || noiseState.needsUpdate) {
updateNoiseTexture(noiseState); updateNoiseTexture(noiseState);
} }
ImGui::End(); ImGui::End();
} }
#endif

View File

@@ -86,6 +86,19 @@ private:
return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v); return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v);
} }
/// @brief Gradient function for 2D only Perlin noise
/// @param hash Hash value for gradient selection
/// @param x X coordinate
/// @param y Y coordinate
/// @return Dot product of gradient vector and distance vector
/// @note Core of Perlin noise; changes affect basic noise character
inline static float grad2(int hash, float x, float y) {
int h = hash & 15;
float u = h < 8 ? x : y;
float v = h < 4 ? y : ((h == 12 || h == 14) ? x : 0.0f);
return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v);
}
/// @brief Initialize permutation table with shuffled values /// @brief Initialize permutation table with shuffled values
/// @note Called on construction; changing seed or shuffle affects all noise patterns /// @note Called on construction; changing seed or shuffle affects all noise patterns
void initializePermutation() { void initializePermutation() {
@@ -237,11 +250,6 @@ public:
int vTL = permutation[permutation[xmod+0]+ymod+1]; int vTL = permutation[permutation[xmod+0]+ymod+1];
int vTR = permutation[permutation[xmod+1]+ymod+1]; int vTR = permutation[permutation[xmod+1]+ymod+1];
// float dBL = BL.dot(GetConstantVector(vBL));
// float dBR = BR.dot(GetConstantVector(vBR));
// float dTL = TL.dot(GetConstantVector(vTL));
// float dTR = TR.dot(GetConstantVector(vTR));
float u = fade(xf); float u = fade(xf);
float v = fade(yf); float v = fade(yf);
@@ -289,16 +297,6 @@ public:
int vRTL = permutation[permutation[permutation[Z+1]+X+0]+Y+1]; int vRTL = permutation[permutation[permutation[Z+1]+X+0]+Y+1];
int vRTR = permutation[permutation[permutation[Z+1]+X+1]+Y+1]; int vRTR = permutation[permutation[permutation[Z+1]+X+1]+Y+1];
// float dFBL = FBL.dot(GetConstantVector3(vFBL));
// float dFBR = FBR.dot(GetConstantVector3(vFBR));
// float dFTL = FTL.dot(GetConstantVector3(vFTL));
// float dFTR = FTR.dot(GetConstantVector3(vFTR));
// float dRBL = RBL.dot(GetConstantVector3(vRBL));
// float dRBR = RBR.dot(GetConstantVector3(vRBR));
// float dRTL = RTL.dot(GetConstantVector3(vRTL));
// float dRTR = RTR.dot(GetConstantVector3(vRTR));
float u = fade(xf); float u = fade(xf);
float v = fade(yf); float v = fade(yf);
float w = fade(zf); float w = fade(zf);
@@ -342,8 +340,8 @@ public:
float sx = fade(tx); float sx = fade(tx);
float sy = fade(ty); float sy = fade(ty);
float nx0 = lerp(c00, c10, sx); float nx0 = lerp(sy, c00, c10);
float nx1 = lerp(c01, c11, sx); float nx1 = lerp(sx, c01, c11);
return lerp(nx0, nx1, sy); return lerp(nx0, nx1, sy);
} }
@@ -382,15 +380,15 @@ public:
float sy = fade(ty); float sy = fade(ty);
float sz = fade(tz); float sz = fade(tz);
float nx00 = lerp(c000, c100, sx); float nx00 = lerp(sx, c000, c100);
float nx10 = lerp(c010, c110, sx); float nx10 = lerp(sx, c010, c110);
float nx01 = lerp(c001, c101, sx); float nx01 = lerp(sx, c001, c101);
float nx11 = lerp(c011, c111, sx); float nx11 = lerp(sx, c011, c111);
float ny0 = lerp(nx00, nx10, sy); float ny0 = lerp(sy, nx00, nx10);
float ny1 = lerp(nx01, nx11, sy); float ny1 = lerp(sy, nx01, nx11);
return lerp(ny0, ny1, sz); return lerp(sz, ny0, ny1);
} }
/// @brief Generate RGBA color from 3D noise with offset channels /// @brief Generate RGBA color from 3D noise with offset channels
@@ -424,20 +422,18 @@ public:
/// @param lacunarity Frequency multiplier per octave /// @param lacunarity Frequency multiplier per octave
/// @return Combined noise value /// @return Combined noise value
/// @note Parameters control noise character: octaves=detail, persistence=roughness, lacunarity=frequency change /// @note Parameters control noise character: octaves=detail, persistence=roughness, lacunarity=frequency change
float fractalNoise(const Vector2f& point, int octaves, float persistence, float lacunarity) { float fractalNoise(const Vector2f& point, int octaves, float persistence = 0.5, float lacunarity = 2.0) {
float total = 0.0f;
float frequency = 1.f; float frequency = 1.f;
float amplitude = 1.f; float amplitude = 1.f;
float maxV = 0.f; float maxV = 0.f;
Vector2f scaledPoint = point * frequency;
for (int i = 0; i < octaves; i++) { for (int i = 0; i < octaves; i++) {
total += permute(scaledPoint) * amplitude; Vector2f scaledPoint = point * frequency;
maxV += amplitude; maxV += permute(scaledPoint) * amplitude;
amplitude *= persistence; amplitude *= persistence;
frequency *= lacunarity; frequency *= lacunarity;
} }
return total / maxV; return maxV;
} }
/// @brief Generate 3D fractal (octave) noise /// @brief Generate 3D fractal (octave) noise
@@ -446,20 +442,18 @@ public:
/// @param persistence Amplitude multiplier per octave /// @param persistence Amplitude multiplier per octave
/// @param lacunarity Frequency multiplier per octave /// @param lacunarity Frequency multiplier per octave
/// @return Combined noise value /// @return Combined noise value
float fractalNoise(const Vector3f& point, int octaves, float persistence, float lacunarity) { float fractalNoise(const Vector3f& point, int octaves, float persistence = 0.5, float lacunarity = 2.0) {
float total = 0.0f;
float frequency = 1.f; float frequency = 1.f;
float amplitude = 1.f; float amplitude = 1.f;
float maxV = 0.f; float maxV = 0.f;
Vector3f scaledPoint = point * frequency;
for (int i = 0; i < octaves; i++) { for (int i = 0; i < octaves; i++) {
total += permute(scaledPoint) * amplitude; Vector3f scaledPoint = point * frequency;
maxV += amplitude; maxV += permute(scaledPoint) * amplitude;
amplitude *= persistence; amplitude *= persistence;
frequency *= lacunarity; frequency *= lacunarity;
} }
return total / maxV; return maxV;
} }
/// @brief Generate turbulence noise (absolute value of octaves) /// @brief Generate turbulence noise (absolute value of octaves)
@@ -599,17 +593,14 @@ public:
for (int y = -1; y <= 1; y++) { for (int y = -1; y <= 1; y++) {
for (int x = -1; x <= 1; x++) { for (int x = -1; x <= 1; x++) {
Vector2f neighbor(x, y); Vector2f neighbor(x, y);
// Get random point inside the neighbor cell
Vector2f pointInCell = hashVector(Vector2f(p + neighbor)); Vector2f pointInCell = hashVector(Vector2f(p + neighbor));
// Vector from current pixel to that point
Vector2f diff = neighbor + pointInCell - f; Vector2f diff = neighbor + pointInCell - f;
float dist = diff.norm(); float dist = diff.norm();
if (dist < minDist) minDist = dist; if (dist < minDist) minDist = dist;
} }
} }
return minDist; // Usually clamped or inverted for visuals return minDist;
} }
/// @brief Worley Noise 3D /// @brief Worley Noise 3D
@@ -650,7 +641,7 @@ public:
Vector2f neighbor(x, y); Vector2f neighbor(x, y);
Vector2f pointInCell = hashVector(Vector2f(p + neighbor)); Vector2f pointInCell = hashVector(Vector2f(p + neighbor));
Vector2f diff = neighbor + pointInCell - f; Vector2f diff = neighbor + pointInCell - f;
float dist = diff.squaredNorm(); // Faster than norm float dist = diff.squaredNorm();
if (dist < minDist) { if (dist < minDist) {
minDist = dist; minDist = dist;
cellID = p + neighbor; cellID = p + neighbor;
@@ -668,8 +659,8 @@ public:
Vector2f p = Vector2f(floor(point.x()), floor(point.y())); Vector2f p = Vector2f(floor(point.x()), floor(point.y()));
Vector2f f = Vector2f(point.x() - p.x(), point.y() - p.y()); Vector2f f = Vector2f(point.x() - p.x(), point.y() - p.y());
float d1 = 10.0f; // Closest float d1 = 10.0f;
float d2 = 10.0f; // 2nd Closest float d2 = 10.0f;
for (int y = -1; y <= 1; y++) { for (int y = -1; y <= 1; y++) {
for (int x = -1; x <= 1; x++) { for (int x = -1; x <= 1; x++) {
@@ -717,16 +708,13 @@ public:
/// @return Divergence-free vector field (useful for particle simulation) /// @return Divergence-free vector field (useful for particle simulation)
/// @note Calculated via finite difference curl of a potential field /// @note Calculated via finite difference curl of a potential field
Vector2f curlNoise(const Vector2f& point) { Vector2f curlNoise(const Vector2f& point) {
float e = 0.01f; // Epsilon float n1 = permute(Vector2f(point + Vector2f(0, EPSILON)));
float n1 = permute(Vector2f(point + Vector2f(0, e))); float n2 = permute(Vector2f(point + Vector2f(0, -EPSILON)));
float n2 = permute(Vector2f(point + Vector2f(0, -e))); float n3 = permute(Vector2f(point + Vector2f(EPSILON, 0)));
float n3 = permute(Vector2f(point + Vector2f(e, 0))); float n4 = permute(Vector2f(point + Vector2f(-EPSILON, 0)));
float n4 = permute(Vector2f(point + Vector2f(-e, 0)));
float dx = (n3 - n4) / (2.0f * e); float dx = (n3 - n4) / (2.0f * EPSILON);
float dy = (n1 - n2) / (2.0f * e); float dy = (n1 - n2) / (2.0f * EPSILON);
// Curl of scalar field in 2D is (d/dy, -d/dx)
return Vector2f(dy, -dx).normalized(); return Vector2f(dy, -dx).normalized();
} }
@@ -735,14 +723,10 @@ public:
/// @return Divergence-free vector field /// @return Divergence-free vector field
/// @note Uses 3 offsets of Perlin noise as Vector Potential /// @note Uses 3 offsets of Perlin noise as Vector Potential
Vector3f curlNoise(const Vector3f& point) { Vector3f curlNoise(const Vector3f& point) {
float e = 0.01f; Vector3f dx(EPSILON, 0.0f, 0.0f);
Vector3f dy(0.0f, EPSILON, 0.0f);
Vector3f dz(0.0f, 0.0f, EPSILON);
Vector3f dx(e, 0.0f, 0.0f);
Vector3f dy(0.0f, e, 0.0f);
Vector3f dz(0.0f, 0.0f, e);
// We need a vector potential (3 uncorrelated noise values)
// We reuse permuteColor's logic but keep it local to avoid overhead
auto potential = [&](const Vector3f& p) -> Vector3f { auto potential = [&](const Vector3f& p) -> Vector3f {
return Vector3f( return Vector3f(
permute(p), permute(p),
@@ -759,12 +743,12 @@ public:
Vector3f p_dz_m = potential(point - dz); Vector3f p_dz_m = potential(point - dz);
// Finite difference // Finite difference
float dFz_dy = (p_dy_p.z() - p_dy_m.z()) / (2.0f * e); float dFz_dy = (p_dy_p.z() - p_dy_m.z()) / (2.0f * EPSILON);
float dFy_dz = (p_dz_p.y() - p_dz_m.y()) / (2.0f * e); float dFy_dz = (p_dz_p.y() - p_dz_m.y()) / (2.0f * EPSILON);
float dFx_dz = (p_dz_p.x() - p_dz_m.x()) / (2.0f * e); float dFx_dz = (p_dz_p.x() - p_dz_m.x()) / (2.0f * EPSILON);
float dFz_dx = (p_dx_p.z() - p_dx_m.z()) / (2.0f * e); float dFz_dx = (p_dx_p.z() - p_dx_m.z()) / (2.0f * EPSILON);
float dFy_dx = (p_dx_p.y() - p_dx_m.y()) / (2.0f * e); float dFy_dx = (p_dx_p.y() - p_dx_m.y()) / (2.0f * EPSILON);
float dFx_dy = (p_dy_p.x() - p_dy_m.x()) / (2.0f * e); float dFx_dy = (p_dy_p.x() - p_dy_m.x()) / (2.0f * EPSILON);
return Vector3f( return Vector3f(
dFz_dy - dFy_dz, dFz_dy - dFy_dz,