There are many noise generators: perlin, brownian, simplex, value noise, etc.
I made this function, it's not a lot of code and the result seems somehow fine. I'm not sure if I can call this perlin noise, it just looks random. It requires C++11, especially for the random generator. I'm not using the standard rand() function since it's not implementation defined, while minstd_rand is.
Here is the result:
Here is the code, you can get different results by changing the seed.
#include <random>
using namespace std;
using namespace sf;
typedef Vector2f Vec2; // I'm lazy
VertexArray glines(Lines); // to show gradients
Sprite spr;
Texture tx;
// this allows the code to generate the same image depending on an integer, and it will still look random. I'm not using a permutation table.
int seed = 23;
// simple dot product
float prod(Vec2 a, Vec2 b) { return a.x*b.x + a.y*b.y; }
// linear interpolation between 2 vectors
Vec2 interp(Vec2 start,Vec2 end,float coef){return coef*(end-start)+start;}
// generates an array of float between 0 and 1, depending on a unsigned int seed
vector<float> seeded_rand_float(unsigned int seed, int many){
vector<float> ret;
minstd_rand rr; // C++11 template of std::linear_congruential_engine
if(seed%2147483647==0) // those values won't work and will break the generator (look at the definition of minstd_rand)
{
seed/=2; // dirty fix
msg("bad seed !");
}
rr.seed(seed);
for(int j = 0 ; j < many; ++j)
ret.push_back(float(rr())/0x7fffffff); // making sure values are [0,1]
return ret;
}
// generates a vector2f, taking two consecutive random float from the function above
vector<Vec2>seeded_rand_vec2(unsigned int seed, int many){
auto coeffs1 = seeded_rand_float(seed, many*2); // twice as many values
vector<Vec2> pushere;
for(int i = 0; i < many; ++i)
pushere.push_back(Vec2(coeffs1[2*i],coeffs1[2*i+1])); // using consecutive values from the array
return pushere;
}
void init()
{
// this is a lambda
// [this](){ // removed the lambda,
glines.clear();
int pixels = 500; // number of pixels in a row
int divisions = 10; // number of cells in a row
float cellsize = float(pixels)/divisions; // the size of one cell where interpolation will occur.
//generates the gradients vectors, gradients vector are random vector place at each corner of the square cells, (they're lattice). I've added +1 because there is one more at the end
auto randv = seeded_rand_vec2(seed,(divisions+1)*(divisions+1));
float factor=2.f;// that's not a very relevant variable, but nothing is perfect
for(auto&a:randv){ // rewrite vector so their range can go from [0,1] to [-1,1] instead
a*=factor;
a-=factor*.5f*Vec2(1.f,1.f);
}
Image img;
img.create(pixels,pixels,Color(0,0,0)); // black image of size pixels*pixels
for(int jj=0;jj<=divisions;++jj){ // this loop just help visualize the gradient vectors using simple lines
for(int ii=0;ii<=divisions;++ii)
{
Vec2
A = randv[divisions*jj +ii],
glines.append(Vertex(Vec2(10,10)+cellsize*Vec2(ii,jj)));
glines.append(Vertex(Vec2(10,10)+cellsize*Vec2(ii,jj)+A*cellsize*.5f));
}
}
for(int j=0;j<=pixels;++j)
{
for(int i=0;i<=pixels;++i)
{
int ii = int(i/cellsize);
int jj = int(j/cellsize);
// here we create 4 vectors, each one describing a corner of the square cell
// the indexing use is a little painful to understand
Vec2
&A = randv[divisions*jj +ii],
&B = randv[divisions*jj +ii+1],
&C = randv[divisions*(jj+1) +ii],
&D = randv[divisions*(jj+1) +ii+1];
// those steps generate x,y float values in [0,1], indicating the relative position of the pixel in a square cell
// for example if the cellsize is 50, the pixel at (142,351), (x,y) will be (float(42/50),float(1/50))
float x = float(i-cellsize*(i/int(cellsize)))/cellsize; // converting to int is like a floor() call
float y = float(j-cellsize*(j/int(cellsize)))/cellsize; // (I guess?)
// interpolate the 4 gradients into one vector, depending on the relative position of the pixel in the cell
// note that you can change the interp function for smoother results (those weird white artifacts)
Vec2 grad = interp(interp(A,B,x),interp(C,D,x),y);
// calculate the pixel value, it's the sum of the dot products between the interpolated gradient, and 4 vectors, which are made with the difference between the pixel relative value, and the corresponding corners.
float bright =
prod(grad ,Vec2(x ,y))+
prod(grad ,Vec2(1.f-x ,y))+
prod(grad ,Vec2(x ,1.f-y))+
prod(grad ,Vec2(1.f-x ,1.f-y));
bright=abs(bright);//making it positive
bright*=.25f; // divides by four, since the value is in [0.0,4.0]
img.setPixel(i,j,Color(255.f*bright,255.f*bright,255.f*bright)); // sets the pixel
}
}
tx.loadFromImage(img);
spr.setPosition(Vec2(10,10));// shifts the sprite
spr.setTexture(tx);
//}();
}
void draw(RenderWindow&window)
{
window.draw(spr);
}
It's not a compile-and-work example, but I made it easier for you to test. It might require many other headers like vector.
The interpolation is linear hence the ghostly white line artifacts between some gradients.
It's not the best implementation you could find, I think there are many errors (for instance, the gradients seem to loop over, the one on the left comes back on the right), but it's short and simple, and sort of look like random noise, I commented so you can understand the steps, because I had quite a lot of trouble understanding online examples, code and explanations, especially how the dot products are calculated.
Tell me what you think and ask about !
I also intend to use this to get a contour as points to draw polygons, to generate forest/grass/lakes/mountain patterns. Noise is interesting, but it's costly.