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 <cmath>
@@ -7,6 +8,7 @@
#include <iostream>
#include "./pnoise2.hpp"
#include "../timing_decorator.hpp"
#include "../../imgui/imgui.h"
#include <GLFW/glfw3.h>
@@ -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();
}
}
#endif

View File

@@ -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,