From 62adc8575b40c0c2bae1aff0010a886bc72431d8 Mon Sep 17 00:00:00 2001 From: Yggdrasil75 Date: Tue, 3 Feb 2026 13:05:26 -0500 Subject: [PATCH] fix value and fractal noise --- util/noise/pnoise.cpp | 24 +++------ util/noise/pnoise2.hpp | 114 ++++++++++++++++++----------------------- 2 files changed, 56 insertions(+), 82 deletions(-) diff --git a/util/noise/pnoise.cpp b/util/noise/pnoise.cpp index f41acfb..97ee63d 100644 --- a/util/noise/pnoise.cpp +++ b/util/noise/pnoise.cpp @@ -1,4 +1,5 @@ -#pragma once +#ifndef PNOISE_CPP +#define PNOISE_CPP #include #include @@ -7,6 +8,7 @@ #include #include "./pnoise2.hpp" +#include "../timing_decorator.hpp" #include "../../imgui/imgui.h" #include @@ -110,6 +112,7 @@ inline float sampleNoiseLayer(PNoise2& gen, NoiseType type, Eigen::Vector2f poin } inline void updateNoiseTexture(NoisePreviewState& state) { + TIME_FUNCTION; if (state.textureId == 0) glGenTextures(1, &state.textureId); 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) { - // 1. Initial Coordinate float nx = (x + state.offset[0]); float ny = (y + state.offset[1]); Eigen::Vector2f point(nx, ny); float finalValue = 0.0f; - // 2. Process Layer Stack for (const auto& layer : state.layers) { if (!layer.enabled) continue; - // Adjust coordinate frequency for this layer Eigen::Vector2f samplePoint = point * layer.scale; - // Cheat seed offset samplePoint += Eigen::Vector2f((float)layer.seedOffset * 10.5f, (float)layer.seedOffset * -10.5f); - // Special Case: Coordinate Mutators if (layer.blend == BlendMode::DomainWarp) { if (layer.type == NoiseType::CurlNoise) { Eigen::Vector2f flow = generator.curlNoise(samplePoint); @@ -150,7 +148,6 @@ inline void updateNoiseTexture(NoisePreviewState& state) { continue; } - // Standard Arithmetic Layers float nVal = sampleNoiseLayer(generator, layer.type, samplePoint, layer); switch (layer.blend) { @@ -175,7 +172,6 @@ inline void updateNoiseTexture(NoisePreviewState& state) { } } - // 3. Normalize for display float norm = std::tanh(finalValue); norm = (norm + 1.0f) * 0.5f; norm = std::clamp(norm, 0.0f, 1.0f); @@ -223,16 +219,13 @@ inline void drawNoiseLab(NoisePreviewState& noiseState) { ImGui::BeginChild("LayersScroll", ImVec2(0, 300), true); - // Layer List for (int i = 0; i < noiseState.layers.size(); ++i) { NoiseLayer& layer = noiseState.layers[i]; ImGui::PushID(i); - // Header bool open = ImGui::CollapsingHeader(layer.name, ImGuiTreeNodeFlags_DefaultOpen); - // Context menu to move/delete if (ImGui::BeginPopupContextItem()) { if (ImGui::MenuItem("Move Up", nullptr, false, i > 0)) { std::swap(noiseState.layers[i], noiseState.layers[i-1]); @@ -265,19 +258,16 @@ inline void drawNoiseLab(NoisePreviewState& noiseState) { ImGui::SameLine(); 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* 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("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("Strength/Weight", &layer.strength, 0.0f, 5.0f)) changed = true; if (ImGui::SliderInt("Seed Offset", &layer.seedOffset, 0, 100)) changed = true; - // Conditional Params if (layer.type == NoiseType::Fractal || layer.type == NoiseType::Turbulence || layer.type == NoiseType::Ridged || layer.type == NoiseType::Billow) { @@ -300,14 +290,14 @@ inline void drawNoiseLab(NoisePreviewState& noiseState) { ImGui::Separator(); - // Preview ImGui::Text("Preview Output"); ImGui::Image((void*)(intptr_t)noiseState.textureId, ImVec2((float)noiseState.width, (float)noiseState.height)); - // Auto update logic if (changed || noiseState.needsUpdate) { updateNoiseTexture(noiseState); } ImGui::End(); -} \ No newline at end of file +} + +#endif \ No newline at end of file diff --git a/util/noise/pnoise2.hpp b/util/noise/pnoise2.hpp index 26a79ad..510024d 100644 --- a/util/noise/pnoise2.hpp +++ b/util/noise/pnoise2.hpp @@ -86,6 +86,19 @@ private: 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 /// @note Called on construction; changing seed or shuffle affects all noise patterns void initializePermutation() { @@ -236,11 +249,6 @@ public: int vBR = permutation[permutation[xmod+1]+ymod+0]; int vTL = permutation[permutation[xmod+0]+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 v = fade(yf); @@ -288,16 +296,6 @@ public: int vRBR = permutation[permutation[permutation[Z+1]+X+1]+Y+0]; int vRTL = permutation[permutation[permutation[Z+1]+X+0]+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 v = fade(yf); @@ -342,8 +340,8 @@ public: float sx = fade(tx); float sy = fade(ty); - float nx0 = lerp(c00, c10, sx); - float nx1 = lerp(c01, c11, sx); + float nx0 = lerp(sy, c00, c10); + float nx1 = lerp(sx, c01, c11); return lerp(nx0, nx1, sy); } @@ -382,15 +380,15 @@ public: float sy = fade(ty); float sz = fade(tz); - float nx00 = lerp(c000, c100, sx); - float nx10 = lerp(c010, c110, sx); - float nx01 = lerp(c001, c101, sx); - float nx11 = lerp(c011, c111, sx); + float nx00 = lerp(sx, c000, c100); + float nx10 = lerp(sx, c010, c110); + float nx01 = lerp(sx, c001, c101); + float nx11 = lerp(sx, c011, c111); - float ny0 = lerp(nx00, nx10, sy); - float ny1 = lerp(nx01, nx11, sy); + float ny0 = lerp(sy, nx00, nx10); + 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 @@ -424,20 +422,18 @@ public: /// @param lacunarity Frequency multiplier per octave /// @return Combined noise value /// @note Parameters control noise character: octaves=detail, persistence=roughness, lacunarity=frequency change - float fractalNoise(const Vector2f& point, int octaves, float persistence, float lacunarity) { - float total = 0.0f; + float fractalNoise(const Vector2f& point, int octaves, float persistence = 0.5, float lacunarity = 2.0) { float frequency = 1.f; float amplitude = 1.f; float maxV = 0.f; - Vector2f scaledPoint = point * frequency; for (int i = 0; i < octaves; i++) { - total += permute(scaledPoint) * amplitude; - maxV += amplitude; + Vector2f scaledPoint = point * frequency; + maxV += permute(scaledPoint) * amplitude; amplitude *= persistence; frequency *= lacunarity; } - return total / maxV; + return maxV; } /// @brief Generate 3D fractal (octave) noise @@ -446,20 +442,18 @@ public: /// @param persistence Amplitude multiplier per octave /// @param lacunarity Frequency multiplier per octave /// @return Combined noise value - float fractalNoise(const Vector3f& point, int octaves, float persistence, float lacunarity) { - float total = 0.0f; + float fractalNoise(const Vector3f& point, int octaves, float persistence = 0.5, float lacunarity = 2.0) { float frequency = 1.f; float amplitude = 1.f; float maxV = 0.f; - Vector3f scaledPoint = point * frequency; for (int i = 0; i < octaves; i++) { - total += permute(scaledPoint) * amplitude; - maxV += amplitude; + Vector3f scaledPoint = point * frequency; + maxV += permute(scaledPoint) * amplitude; amplitude *= persistence; frequency *= lacunarity; } - return total / maxV; + return maxV; } /// @brief Generate turbulence noise (absolute value of octaves) @@ -599,17 +593,14 @@ public: for (int y = -1; y <= 1; y++) { for (int x = -1; x <= 1; x++) { Vector2f neighbor(x, y); - // Get random point inside the neighbor cell Vector2f pointInCell = hashVector(Vector2f(p + neighbor)); - - // Vector from current pixel to that point Vector2f diff = neighbor + pointInCell - f; float dist = diff.norm(); if (dist < minDist) minDist = dist; } } - return minDist; // Usually clamped or inverted for visuals + return minDist; } /// @brief Worley Noise 3D @@ -650,7 +641,7 @@ public: Vector2f neighbor(x, y); Vector2f pointInCell = hashVector(Vector2f(p + neighbor)); Vector2f diff = neighbor + pointInCell - f; - float dist = diff.squaredNorm(); // Faster than norm + float dist = diff.squaredNorm(); if (dist < minDist) { minDist = dist; cellID = p + neighbor; @@ -668,8 +659,8 @@ public: Vector2f p = Vector2f(floor(point.x()), floor(point.y())); Vector2f f = Vector2f(point.x() - p.x(), point.y() - p.y()); - float d1 = 10.0f; // Closest - float d2 = 10.0f; // 2nd Closest + float d1 = 10.0f; + float d2 = 10.0f; for (int y = -1; y <= 1; y++) { for (int x = -1; x <= 1; x++) { @@ -717,16 +708,13 @@ public: /// @return Divergence-free vector field (useful for particle simulation) /// @note Calculated via finite difference curl of a potential field Vector2f curlNoise(const Vector2f& point) { - float e = 0.01f; // Epsilon - float n1 = permute(Vector2f(point + Vector2f(0, e))); - float n2 = permute(Vector2f(point + Vector2f(0, -e))); - float n3 = permute(Vector2f(point + Vector2f(e, 0))); - float n4 = permute(Vector2f(point + Vector2f(-e, 0))); + float n1 = permute(Vector2f(point + Vector2f(0, EPSILON))); + float n2 = permute(Vector2f(point + Vector2f(0, -EPSILON))); + float n3 = permute(Vector2f(point + Vector2f(EPSILON, 0))); + float n4 = permute(Vector2f(point + Vector2f(-EPSILON, 0))); - float dx = (n3 - n4) / (2.0f * e); - float dy = (n1 - n2) / (2.0f * e); - - // Curl of scalar field in 2D is (d/dy, -d/dx) + float dx = (n3 - n4) / (2.0f * EPSILON); + float dy = (n1 - n2) / (2.0f * EPSILON); return Vector2f(dy, -dx).normalized(); } @@ -735,14 +723,10 @@ public: /// @return Divergence-free vector field /// @note Uses 3 offsets of Perlin noise as Vector Potential Vector3f curlNoise(const Vector3f& point) { - float e = 0.01f; - - Vector3f dx(e, 0.0f, 0.0f); - Vector3f dy(0.0f, e, 0.0f); - Vector3f dz(0.0f, 0.0f, e); + Vector3f dx(EPSILON, 0.0f, 0.0f); + Vector3f dy(0.0f, EPSILON, 0.0f); + Vector3f dz(0.0f, 0.0f, EPSILON); - // 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 { return Vector3f( permute(p), @@ -759,12 +743,12 @@ public: Vector3f p_dz_m = potential(point - dz); // Finite difference - float dFz_dy = (p_dy_p.z() - p_dy_m.z()) / (2.0f * e); - float dFy_dz = (p_dz_p.y() - p_dz_m.y()) / (2.0f * e); - float dFx_dz = (p_dz_p.x() - p_dz_m.x()) / (2.0f * e); - float dFz_dx = (p_dx_p.z() - p_dx_m.z()) / (2.0f * e); - float dFy_dx = (p_dx_p.y() - p_dx_m.y()) / (2.0f * e); - float dFx_dy = (p_dy_p.x() - p_dy_m.x()) / (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 * EPSILON); + 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 * EPSILON); + 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 * EPSILON); return Vector3f( dFz_dy - dFy_dz,