This Is what I have been able to come up with.
I want this for a sky texture. because I am using a dome I want the edges to line up.
so what I have tried to do was make the gradient on the x axis go back to where it was at the beginning. This would work quite well if a noise only needed one octave. but the effect of increasing the octaves creates a pattern and makes the corners uneven.
this is the code that I have been using.
#pragma once
#include <math.h>
inline float patRand(int x, int y) {
float xnum = (float)x * 102385.0f;
float ynum = (float)y * 128473.0f;
float dist = (sin(xnum) + sin(ynum)) * 2134800;
return fmod(dist, 1);
}
#pragma once
#include <cmath>
#include <vector>
#include <iostream>
#include <glm/glm.hpp> // For glm::vec2
#include "randPat.h"
// Utility functions
inline float fade(float t) {
return t * t * t * (t * (t * 6 - 15) + 10); // Perlin's fade function
}
inline float lerp1(float a, float b, float t) {
return a + t * (b - a); // Linear interpolation
}
inline float dotGridGradient(int ix, int iy, float x, float y, const std::vector<std::vector<glm::vec2>>& gradients, int gridWidth) {
// Compute the distance vector
float dx = x - ix;
float dy = y - iy;
// Wrap x index for seamless wrapping along x-axis
int wrappedIx = ix % gridWidth;
// Calculate the dot product
glm::vec2 gradient = gradients[iy][wrappedIx];
return (dx * gradient.x + dy * gradient.y);
}
// Generate wrapping 2D Perlin noise along x-axis only
inline float perlinNoise2D(float x, float y, int gridWidth, int gridHeight, const std::vector<std::vector<glm::vec2>>& gradients) {
// Determine grid cell coordinates
int x0 = static_cast<int>(floor(x)) % gridWidth;
int y0 = static_cast<int>(floor(y)) % gridHeight;
int x1 = (x0 + 1) % gridWidth;
int y1 = std::min(y0 + 1, gridHeight - 1); // No wrapping on y-axis
// Relative coordinates within the cell
float tx = x - floor(x);
float ty = y - floor(y);
// Smooth the coordinates
float sx = fade(tx);
float sy = fade(ty);
// Interpolate dot products of gradient vectors
float n0, n1, ix0, ix1, value;
n0 = dotGridGradient(x0, y0, x, y, gradients, gridWidth);
n1 = dotGridGradient(x1, y0, x, y, gradients, gridWidth);
ix0 = lerp1(n0, n1, sx);
n0 = dotGridGradient(x0, y1, x, y, gradients, gridWidth);
n1 = dotGridGradient(x1, y1, x, y, gradients, gridWidth);
ix1 = lerp1(n0, n1, sx);
value = lerp1(ix0, ix1, sy);
return value;
}
inline float perlinNoise2D_Octaves(float x, float y, int gridWidth, int gridHeight, const std::vector<std::vector<glm::vec2>>& gradients, int octaves, float persistence) {
float total = 0.0f;
float maxAmplitude = 0.0f; // Used for normalization
float frequency = 1.0f / 10.0f;
float amplitude = 1.0f;
for (int i = 0; i < octaves; ++i) {
// Adjust x, y coordinates by frequency
float sampleX = x * frequency;
float sampleY = y * frequency;
// Add the current octave's contribution
total += perlinNoise2D(sampleX, sampleY, gridWidth, gridHeight, gradients) * amplitude;
// Track max amplitude for normalization
maxAmplitude += amplitude;
// Update frequency and amplitude for next octave
frequency *= 1.45f; // Non-linear increase in frequency
amplitude *= persistence * 1.25f;
}
return (total / maxAmplitude) * 2; // Normalize to range [-1, 1]
}
inline float domainWarping(float x, float y, int gridWidth, int gridHeight, const std::vector<std::vector<glm::vec2>>& gradients) {
float warpX = perlinNoise2D(x * 0.5f, y * 0.5f, gridWidth, gridHeight, gradients) * 2.0f;
float warpY = perlinNoise2D(x * 0.5f, y * 0.5f, gridWidth, gridHeight, gradients) * 2.0f;
return perlinNoise2D(x + warpX, y + warpY, gridWidth, gridHeight, gradients);
}
// Initialize gradients for the grid
inline std::vector<std::vector<glm::vec2>> generateGradients(int gridWidth, int gridHeight) {
std::vector<std::vector<glm::vec2>> gradients(gridHeight, std::vector<glm::vec2>(gridWidth));
for (int y = 0; y < gridHeight; ++y) {
for (int x = 0; x < gridWidth; ++x) {
float angle = patRand(x, y) * 2.0f * 3.1415926535;
gradients[y][x] = glm::vec2(cos(angle), sin(angle));
}
}
return gradients;
}
#include "CloudTexture.h"
CloudTexture::CloudTexture()
{
}
CloudTexture::~CloudTexture()
{
}
void CloudTexture::init()
{
unsigned char* imageData = new unsigned char[1048576];
int octaves = 12; // Number of layers
float persistence = 0.5f;
int gridSize = 16; // Wraps every 8 units
auto gradients = generateGradients(gridSize, gridSize);
// Sample the noise function over a grid
int resolution = 512; // Number of points to evaluate
for (int j = 0; j < resolution; ++j) {
for (int i = 0; i < resolution; ++i) {
float x = static_cast<float>(i) / resolution * gridSize;
float y = static_cast<float>(j) / resolution * gridSize;
float val = perlinNoise2D_Octaves(x, y, gridSize, gridSize, gradients, octaves, persistence);
int index = (i + j * 512) * 4;
int res = (int)(((val + 1.0f) * .5f) * 255);
if (res < 0) res = 0;
if (res > 255) res = 255;
imageData[index] = (unsigned char)res;
imageData[index + 1] = (unsigned char)res;
imageData[index + 2] = 255;
imageData[index + 3] = 255;
}
}
glGenTextures(1, &mTexture);
glBindTexture(GL_TEXTURE_2D, mTexture); // all upcoming GL_TEXTURE_2D operations will affect our texture object (mTexture)
// Set the texture wrapping/filtering options (on the currently bound texture object)
// GL_CLAMP_TO_EDGE
// GL_REPEAT
// GL_MIRRORED_REPEAT
// GL_CLAMP_TO_BORDER
// GL_LINEAR
// GL_NEAREST
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 512, 512, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData);
glGenerateMipmap(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);
delete imageData;
}
void CloudTexture::update(float delta)
{
time += delta;
if (time >= 1) {
time = 0;
addunit += 10;
init();
}
}
void CloudTexture::bind(GLuint texUnit)
{
glActiveTexture(GL_TEXTURE0 + texUnit);
glBindTexture(GL_TEXTURE_2D, mTexture);
}
void CloudTexture::unbind(GLuint texUnit)
{
glActiveTexture(GL_TEXTURE0 + texUnit);
glBindTexture(GL_TEXTURE_2D, 0);
}
the update function would be used to make the pattern shift. I am not using it here.