# Perlin noise to cloud alike look texture

This topic is 832 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

So im trying to implement variation of perlin noise to get:

i am relating to this code:

https://en.wikipedia.org/wiki/Perlin_noise

it doesnt actualy provide me with good result from the thing i understand i just need to divide coords by some value to get different results etc.

so firs i used xy / pow(2.0, xy)

and i got:

then i saw that guy does: xy / 2, x / 4, x / 8 etc

that gives me poor result:

so how do i do that to get something like that first picture?

#ifndef perlin2dH
#define perlin2dH
#include "DxcMath.h"
#include "math.h"
#include "logme.h"
#include "txtfile/stringlist.h"

struct TPerlin2d
{

vec2  Normalize2d(vec2 v)
{
float magni;
vec2 result;
magni = magnitude(vec3(v));

if (magni <= 0.0)  magni = 1.0;

result.x = v.x/magni;
result.y = v.y/magni;

return result;
}

// Function to linearly interpolate between a0 and a1
// Weight w should be in the range [0.0, 1.0]
float Perlin2d_lerp(float a0, float a1, float w)
{
return (1.0 - w)*a0 + w*a1;
}

float Perlin2d_RandomFloat()
{
return	(float(rand() % 100) / 100.0);
}

void InitializePerlin2d(int width, int height)
{
Gradient = new vec2[width * height];

for (int i=0; i < width*height; i++)
Gradient[i] = Normalize2d( vec2(Perlin2d_RandomFloat() * 2.0 - 1.0, Perlin2d_RandomFloat()* 2.0 - 1.0) );

}

TPerlin2d()
{
//  InitializePerlin2d(4,4);
}

~TPerlin2d()
{
}

float bilinearInterpolation(float x, float y, float * table)
{

int ix = int(x);
int iy = int(y);

int ix1 = ix;
int ix2 = ix+1;

int iy1 = iy;
int iy2 = iy+1;

int cix2 = ix2;
int ciy2 = iy2;

if(cix2 > gradient_width-1) cix2 = 0;
if(ciy2 > gradient_height-1) ciy2 = 0;

float FQ11 = table[iy1 * gradient_width + ix1];
float FQ21 = table[iy1 * gradient_width + cix2];
float FQ12 = table[ciy2 * gradient_width + ix1];
float FQ22 = table[ciy2 * gradient_width + cix2];

float x1 = float(ix1);
float y1 = float(iy1);
float x2 = float(ix2);
float y2 = float(iy2);

float fxy1 = ( (x2 - x) / (x2 - x1) ) * FQ11 + ( (x - x1) / (x2 - x1) ) * FQ21;
float fxy2 = ( (x2 - x) / (x2 - x1) ) * FQ12 + ( (x - x1) / (x2 - x1) ) * FQ22;

return ( (y2 - y) / (y2 - y1) )*fxy1 + ( (y - y1) / (y2 - y1) )*fxy2;

}

float returnDot(int x, int y)
{
int ix = gradient_width-1 - x;
int iy = gradient_height-1 - y;
if (ix < 0) ix = gradient_width - 1;
if (iy < 0) iy = gradient_height - 1;
if (ix >= gradient_width) ix = 0;
if (iy >= gradient_height) iy = 0;

return (a+b) / 2.0;
}

// Computes the dot product of the distance and gradient vectors.
float Perlin2d_dotGridGradient(int ix, int iy, float x, float y)
{
// Compute the distance vector
float dx = x - (double)ix;
float dy = y - (double)iy;

// Compute the dot-product
}

// Compute Perlin noise at coordinates x, y
float GetPerlinNoiseAt(float x, float y)
{

// Determine grid cell coordinates
int x0 = (x > 0.0 ? (int)x : (int)x - 1);
int x1 = x0 + 1;
int y0 = (y > 0.0 ? (int)y : (int)y - 1);
int y1 = y0 + 1;

// Determine interpolation weights
// Could also use higher order polynomial/s-curve here
float sx = x - (double)x0;
float sy = y - (double)y0;

// Interpolate between grid point gradients
float n0, n1, ix0, ix1, value;
n0 = Perlin2d_dotGridGradient(x0, y0, x, y);
n1 = Perlin2d_dotGridGradient(x1, y0, x, y);

ix0 = Perlin2d_lerp(n0, n1, sx);

n0 = Perlin2d_dotGridGradient(x0, y1, x, y);
n1 = Perlin2d_dotGridGradient(x1, y1, x, y);

ix1 = Perlin2d_lerp(n0, n1, sx);

value = Perlin2d_lerp(ix0, ix1, sy);

return value*0.5+0.5;
}
};

#endif



usage:

	TPerlin2d perlin;
perlin.InitializePerlin2d(256, 256);

unsigned char pdata[256*256*3];
AnsiString tname = appdir+"NOISE.TGA";

for (int y=0; y < 256; y++)
for (int x=0; x < 256; x++)
{
float a = 0.0;
int cnt = 0;
for (int i=1; i < 20; i=i+2)
{
a = a + perlin.GetPerlinNoiseAt(float(x) / float(i)/*powf(2.0, float(i))*/, float(y) / float(i) /*powf(2.0, float(i))*/);
cnt = cnt + 1;
}

a = (a / float(cnt))*255.0;

pdata[y*256*3 + x*3 + 0] = Byte(a);
pdata[y*256*3 + x*3 + 1] = Byte(a);
pdata[y*256*3 + x*3 + 2] = Byte(a);
}

WriteTga(tname.c_str(),256,256,24,pdata);


##### Share on other sites

Well first off, wiki says "a random vector on the unit circle" and the method you use to calculate random vectors has higher distribution in the 4 corners of the resulting "circle" I think. (Might need to generate random angle and convert that to unit vector instead?)

EDIT:

I think the problem is, you are progressively sampling over smaller and smaller area of the gradient grid (imagine rectangle shrinking toward that pinched corner)

The only reason that is a problem, is that you use the SAME grid of random gradients for all the added layers.

Use 20 (or whatever i is) different random gradient grids and it should work. They get smaller the higher "i" is.

Edited by Waterlimon

##### Share on other sites

but

Gradient[i] = Normalize2d( vec2(Perlin2d_RandomFloat() * 2.0 - 1.0, Perlin2d_RandomFloat()* 2.0 - 1.0) );

gives random x and y from range -1..1 so i dont thik thats the issue

Perlin2d_RandomFloat() gives float from range 0..1

then i just normalize( x * 2.0 - 1.0, y * 2.0 - 1.0)  so i get normalized vector from -1..1 for both x and y

Edited by ?W ?I ?R ?E ?D ? ?C ?A ?T

##### Share on other sites

Yeah thats not where the issue was (check my edit), but its still different from the algorithm described in wiki (which says random vector on unit circle), you do have "random" vector on unit circle but the distribution is slightly biased because you are essentially projecting a box onto a sphere when you normalize.

##### Share on other sites

i still dont get you about making that random angle and normalizing the result since it will provide similar vectors to what i get there

now i do sth like this:

  void InitializePerlin2d(int width, int height)
{
Gradient = new vec2[width * height];

Gradient2 = new vec2[(width/2) * (height / 2)];
Gradient4 = new vec2[(width/4) * (height / 4)];
Gradient8 = new vec2[(width/8) * (height / 8)];
Gradient16 = new vec2[(width/16) * (height / 16)];
Gradient32 = new vec2[(width/32) * (height / 32)];

for (int i=0; i < width*height; i++)
Gradient[i] = Normalize2d( vec2(Perlin2d_RandomFloat() * 2.0 - 1.0, Perlin2d_RandomFloat()* 2.0 - 1.0) );

for (int i=0; i < (width/2) * (height / 2); i++)
Gradient2[i] = Normalize2d( vec2(Perlin2d_RandomFloat() * 2.0 - 1.0, Perlin2d_RandomFloat()* 2.0 - 1.0) );

for (int i=0; i < (width/4) * (height / 4); i++)
Gradient4[i] = Normalize2d( vec2(Perlin2d_RandomFloat() * 2.0 - 1.0, Perlin2d_RandomFloat()* 2.0 - 1.0) );

for (int i=0; i < (width/8) * (height / 8); i++)
Gradient8[i] = Normalize2d( vec2(Perlin2d_RandomFloat() * 2.0 - 1.0, Perlin2d_RandomFloat()* 2.0 - 1.0) );

for (int i=0; i < (width/16) * (height / 16); i++)
Gradient16[i] = Normalize2d( vec2(Perlin2d_RandomFloat() * 2.0 - 1.0, Perlin2d_RandomFloat()* 2.0 - 1.0) );

for (int i=0; i < (width/32) * (height / 32); i++)
Gradient32[i] = Normalize2d( vec2(Perlin2d_RandomFloat() * 2.0 - 1.0, Perlin2d_RandomFloat()* 2.0 - 1.0) );

}



float Perlin2d_dotGridGradientA(int ix, int iy, float x, float y, int w, int h, vec2 * grida)
{
// Compute the distance vector
float dx = x - (double)ix;
float dy = y - (double)iy;

// Compute the dot-product
return (dx*grida[iy*w + ix].x + dy*grida[iy*h + ix].y);
}

// Compute Perlin noise at coordinates x, y
float GetPerlinNoiseAtA(float x, float y, int w, int h, vec2 * grida)
{

// Determine grid cell coordinates
int x0 = (x > 0.0 ? (int)x : (int)x - 1);
int x1 = x0 + 1;
int y0 = (y > 0.0 ? (int)y : (int)y - 1);
int y1 = y0 + 1;

// Determine interpolation weights
// Could also use higher order polynomial/s-curve here
float sx = x - (double)x0;
float sy = y - (double)y0;

// Interpolate between grid point gradients
float n0, n1, ix0, ix1, value;
n0 = Perlin2d_dotGridGradientA(x0, y0, x, y,w,h,grida);
n1 = Perlin2d_dotGridGradientA(x1, y0, x, y,w,h,grida);

ix0 = Perlin2d_lerp(n0, n1, sx);

n0 = Perlin2d_dotGridGradientA(x0, y1, x, y,w,h,grida);
n1 = Perlin2d_dotGridGradientA(x1, y1, x, y,w,h,grida);

ix1 = Perlin2d_lerp(n0, n1, sx);

value = Perlin2d_lerp(ix0, ix1, sy);

return value*0.5+0.5;
}



and usage

for (int y=0; y < 256; y++)
for (int x=0; x < 256; x++)
{
float a = 0.0;

a = a + perlin.GetPerlinNoiseAtA(x, y, 256, 256,perlin.Gradient );
a = a + perlin.GetPerlinNoiseAtA(x/2.0, y/2.0, 256/2, 256/2,perlin.Gradient2 );
a = a + perlin.GetPerlinNoiseAtA(x/4.0, y/4.0, 256/4, 256/4,perlin.Gradient4 );
a = a + perlin.GetPerlinNoiseAtA(x/8.0, y/8.0, 256/8, 256/8,perlin.Gradient8 );
a = a + perlin.GetPerlinNoiseAtA(x/16.0, y/16.0, 256/16, 256/16,perlin.Gradient16 );
a = a + perlin.GetPerlinNoiseAtA(x/32.0, y/32.0, 256/32, 256/32,perlin.Gradient32 );

a = (a / float(6.0))*255.0;

pdata[y*256*3 + x*3 + 0] = Byte(a);
pdata[y*256*3 + x*3 + 1] = Byte(a);
pdata[y*256*3 + x*3 + 2] = Byte(a);
}



resuklt:

:/

i cant go further than 128

##### Share on other sites

That looks fine.

I suspect the artifacts are because of the round-toward-negativity thing you do at beginning of GetPerlinNoiseAtA.

Because if the float is like 5.9999999 You want 6 but instead get 5.

I use the following to do such "round down division":

template <typename T>
T roundDownDivision(T a, T b)
{
T temp = a / b;
if ((a % b < 0) && (temp*b != a))
{
return temp - 1;
}
return temp;
}


I think I basic algo from stackoverflow long time ago.

Even if it isnt this, still make sure floating point error wont cause issues with that code of yours there.

EDIT: nowait thats the wrong one one sec

template<typename T, typename S>
//handles rounding to integer correctly
T intFloor(S value)
{
static_assert(std::is_integral<T>::value, "Return type must be integral");
S temp = std::floor(value) + static_cast<S>(0.5);
T error = temp < 0 ? -1 : 0; //int conversion truncated toward 0 - dont want that
return static_cast<T>(temp) + error;
}


This is what I meant.

Edited by Waterlimon

##### Share on other sites

Just looking at your result image I think you are close. Problem is not with the noise but more with the application of noise.

Really the perlin noise can be substituted with other variations and still get the result you want. I recommend taking a look also at simplex noise, value noise, gradient noise, etc which all offer different levels of code speed, complexity and refinement of resulting noise.

I believe the problem is your implementation of 2d fbm which has no scaling or rotation. The last picture you posted looks like it might be correct but is scaled so that the pattern is very small. Try also adding global scaling for all the x/y coordinates by dividing them by maybe 50 or so. The other thing is to get a nice result like you want you need to rotate your coordinates between each level of noise as well as scaling them.

I almost always implement these types of noise functions in a gpu shader so I will post glsl example code and I hope you can interpret it.

vec2 uv = vec2(X, Y);
float result = 0.0;
uv *= 12.0; //scaling factor
mat2 m = mat2( 1.6,  1.2, -1.2,  1.6 ); //rotation matrix
result  = 0.5000*noise( uv ); //1st octave weighted perlin noise
uv = m*uv; //rotate coordinates
result += 0.2500*noise( uv * 0.5); //2nd octave weighted perlin noise scaled coords
uv = m*uv; //rotate coordinates
result += 0.1250*noise( uv * 0.25); //3rd octave weighted perlin noise scaled coords
uv = m*uv; //rotate coordinates
result += 0.0625*noise( uv * 0.125) //4th octave weighted perlin noise scaled coords

result = 0.5 + 0.5*result; // scale from -1.0/1.0 to 0.0/1.0

vec3 rgb = vec3(result, result, result);


The line commented scaling factor is the global scale, each octave has a hard coded scale and weight.

For what it's worth I normally use a simplex noise which you may find easier to implement at least until you get your head around how everything is working.

//pseudorandom number function
vec2 hash( vec2 p ){
p = vec2( dot(p,vec2(127.1,311.7)), dot(p,vec2(269.5,183.3)) );
return -1.0 + 2.0*fract(sin(p)*43758.5453123);
}

//simplex noise
float noise( in vec2 p ){
const float K1 = 0.366025404; // (sqrt(3)-1)/2;
const float K2 = 0.211324865; // (3-sqrt(3))/6;

vec2 i = floor( p + (p.x+p.y)*K1 );
vec2 a = p - i + (i.x+i.y)*K2;
vec2 o = (a.x>a.y) ? vec2(1.0,0.0) : vec2(0.0,1.0); //vec2 of = 0.5 + 0.5*vec2(sign(a.x-a.y), sign(a.y-a.x));
vec2 b = a - o + K2;
vec2 c = a - 1.0 + 2.0*K2;
vec3 h = max( 0.5-vec3(dot(a,a), dot(b,b), dot(c,c) ), 0.0 );
vec3 n = h*h*h*h*vec3( dot(a,hash(i+0.0)), dot(b,hash(i+o)), dot(c,hash(i+1.0)));

return dot( n, vec3(70.0) );
}


I also find it easier to put the fbm function into a loop with some global variables to control the output. This makes it easier to change the weighting and the scaling of each octave of noise with single variables and also easily animate the results.

Something like this:

float result = 0.0;
uv *= scale;
uv += offset;
weight = 0.7;
for (int i=0; i<Iterations; i++)
{
result += weight * noise( uv );
uv = rotationMatrix * uv + offset;
weight *= 0.6;
}

Edited by JeffRS

##### Share on other sites
A couple of issues I see here.

1)
a = a + perlin.GetPerlinNoiseAtA(x, y, 256, 256,perlin.Gradient );
a = a + perlin.GetPerlinNoiseAtA(x/2.0, y/2.0, 256/2, 256/2,perlin.Gradient2 );
a = a + perlin.GetPerlinNoiseAtA(x/4.0, y/4.0, 256/4, 256/4,perlin.Gradient4 );
a = a + perlin.GetPerlinNoiseAtA(x/8.0, y/8.0, 256/8, 256/8,perlin.Gradient8 );
a = a + perlin.GetPerlinNoiseAtA(x/16.0, y/16.0, 256/16, 256/16,perlin.Gradient16 );
a = a + perlin.GetPerlinNoiseAtA(x/32.0, y/32.0, 256/32, 256/32,perlin.Gradient32 );

You are not scaling each successive layer, so each layer has exactly the same weight of contribution to the final output. This has a tendency to produce white noise as more layers are added. Instead, you'll probably get better results doing:

a = a + perlin.GetPerlinNoiseAtA(x, y, 256, 256,perlin.Gradient );
a = a + perlin.GetPerlinNoiseAtA(x/2.0, y/2.0, 256/2, 256/2,perlin.Gradient2 )*0.5;
a = a + perlin.GetPerlinNoiseAtA(x/4.0, y/4.0, 256/4, 256/4,perlin.Gradient4 )*0.25;
a = a + perlin.GetPerlinNoiseAtA(x/8.0, y/8.0, 256/8, 256/8,perlin.Gradient8 )*0.125;
a = a + perlin.GetPerlinNoiseAtA(x/16.0, y/16.0, 256/16, 256/16,perlin.Gradient16 )*0.0625;
a = a + perlin.GetPerlinNoiseAtA(x/32.0, y/32.0, 256/32, 256/32,perlin.Gradient32 )*0.03125;

Note that hard-coding the constants like this is not really optimal, but that's the gist of it. This way, the smaller-scale features don't have as much overall impact as the larger-scale features. So layer 0 defines the overall large-scale shape of the terrain, and each successive layer adds smaller and smaller detail.

2) Storing a MxN array of gradients for each layer is just not a very elegant way of doing things. Essentially, the heart of Perlin noise is determining which unit-sized grid cell a value lies within and determining the set of 4 enclosing gradients. Most implementations do this by performing a hash on the integral coordinates of the 4 corners, and using this hash to access a 1 dimensional table of gradients. Look at the reference implementation. (Although the reference implementation is not as clear as I would like.) It uses a table called p[], which is filled with the positive integers from 0 to 255, each entered twice and the whole table randomly shuffled. Then input coordinates (in the range of 0 to 255) are hashed using this table:

int A = p[X  ]+Y, AA = p[A]+Z, AB = p[A+1]+Z,      // HASH COORDINATES OF
B = p[X+1]+Y, BA = p[B]+Z, BB = p[B+1]+Z;      // THE 8 CUBE CORNERS,

The AA, AB, BA, BB values are then the hashes for the 4 corners, and can be used to reference a look-up table. (The reference implementation 'cheats' a little bit by eliminating the actual lookup table, and using the grad() function to generate the gradient based on the actual hash value).

Using a hash like this saves you from having to generate a 2D table for each layer. By combining a seed with your hash (hashing integral coords + seed together) you can generate a different 'pattern' for each layer.

The reference implementation's hash algorithm is limited to a period of 256, which means that for input values greater than 255, the pattern will start to repeat itself. For many uses, this is okay, but if you are doing something like implementing large-scale worlds such as in Minecraft, then you'll probably want to look into a different hashing algorithm. This paper by Ares Lagae and Philip Dutré describes a form of long-period hashing that works similarly to how Perlin's hashing works, but allows hashing to much larger periods than 256. I implement a form of this in my noise library hash routines.

##### Share on other sites

btw is that correct that wit code perlin.GetPerlinNoise(x,y); i will always get 0 because theres no fractional part in it? coz when putting there pure integers it does that

##### Share on other sites
Yes, gradient noise has the effect that if you input integer coordinates, it always returns 0. This was by design; Perlin wanted a function that would throw the peaks and valleys off of the integral lattice, hence the somewhat non-obvious approach of using a gradient vector and a dot product.

##### Share on other sites

Thanks to JTippets i have working generator:

#ifndef perlin2dH
#define perlin2dH
#include "DxcMath.h"
#include "math.h"

struct TPerlin2d
{

vec2  Normalize2d(vec2 v)
{
float magni;
vec2 result;
magni = magnitude(vec3(v));

if (magni <= 0.0)  magni = 1.0;

result.x = v.x/magni;
result.y = v.y/magni;

return result;
}

// Function to linearly interpolate between a0 and a1
// Weight w should be in the range [0.0, 1.0]
float Perlin2d_lerp(float a0, float a1, float w)
{
return (1.0 - w)*a0 + w*a1;
}

float Perlin2d_RandomFloat()
{
return	(float(rand() % 100) / 100.0);
}

void InitializePerlin2d(int width, int height, bool random_grad)
{
Gradient = new vec2[width * height];

{
{0,1},
{1,0},
{-1,0},
{0,-1}
};

for (int i=0; i < width*height; i++)
{
int which=rand()%4;
}

for (int i=0; i < width*height; i++)
Gradient[i] =  Normalize2d( vec2(Perlin2d_RandomFloat() * 2.0 - 1.0, Perlin2d_RandomFloat()* 2.0 - 1.0) );

}

TPerlin2d()
{
//  InitializePerlin2d(4,4);
}

~TPerlin2d()
{
}

// Computes the dot product of the distance and gradient vectors.
float Perlin2d_dotGridGradient(int ix, int iy, float x, float y)
{
// Compute the distance vector
float dx = x - (double)ix;
float dy = y - (double)iy;

// Compute the dot-product
}

// Compute Perlin noise at coordinates x, y
float GetPerlinNoiseAt(float x, float y)
{

// Determine grid cell coordinates
int x0 = (x > 0.0 ? (int)x : (int)x - 1);
int x1 = x0 + 1;
int y0 = (y > 0.0 ? (int)y : (int)y - 1);
int y1 = y0 + 1;

// Determine interpolation weights
// Could also use higher order polynomial/s-curve here
float sx = x - (double)x0;
float sy = y - (double)y0;

// Interpolate between grid point gradients
float n0, n1, ix0, ix1, value;
n0 = Perlin2d_dotGridGradient(x0, y0, x, y);
n1 = Perlin2d_dotGridGradient(x1, y0, x, y);

ix0 = Perlin2d_lerp(n0, n1, sx);

n0 = Perlin2d_dotGridGradient(x0, y1, x, y);
n1 = Perlin2d_dotGridGradient(x1, y1, x, y);

ix1 = Perlin2d_lerp(n0, n1, sx);

value = Perlin2d_lerp(ix0, ix1, sy);

return value*0.5+0.5;
}

unsigned char * make_texture24bpp()
{
unsigned char * pdata = new unsigned char[gradient_width * gradient_height * 3];
for (int y=0; y < gradient_height; y++)
for (int x=0; x < gradient_width; x++)
{
float a = 0.0;

a = a + GetPerlinNoiseAt(nx * 2.0, ny * 2.0) / 2.0;
a = a + GetPerlinNoiseAt(nx * 4.0, ny * 4.0) / 4.0;
a = a + GetPerlinNoiseAt(nx * 8.0, ny * 8.0) / 8.0;
a = a + GetPerlinNoiseAt(nx * 16.0, ny * 16.0) / 16.0;
a = a + GetPerlinNoiseAt(nx * 32.0, ny * 32.0) / 32.0;
a = (a / 0.977864);
a = powf(a, 2.0);
a=a*255.0;

pdata[y*256*3 + x*3 + 0] = (unsigned char)a;
pdata[y*256*3 + x*3 + 1] = (unsigned char)a;
pdata[y*256*3 + x*3 + 2] = (unsigned char)a;
}

return pdata;
}

};

#endif



along with usage:

	TPerlin2d perlin;
perlin.InitializePerlin2d(256, 256, false);

unsigned char * pdata;
AnsiString tname = appdir+"NOISE.TGA";

pdata = perlin.make_texture24bpp();

WriteTga(tname.c_str(),256,256,24,pdata);

delete [] pdata;



even if code implies that you could use different dimension than 256x256 it wont work so if someone want to use that code fix it or make it 256x256 like in example

Edited by ?W ?I ?R ?E ?D ? ?C ?A ?T

##### Share on other sites

This topic is 832 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Create an account

Register a new account

• ### Forum Statistics

• Total Topics
628730
• Total Posts
2984427

• 25
• 11
• 10
• 16
• 14