I'm here to teach you something about Perlin Noise. You know, that magical thing that gives infinite possibilties and is usefull in every single game.
To be honest with you, it's not that very difficult, but it can be if it's not explained right.
First, the theoratical side:
Now, what exactly is perlin noise? Well, it is some pseudo random coherent noise. Let me explain that bit by bit:
*Pseudo random: This means that it appears random, but you get the same result twice every time you call it. Think of the rand() function. You might have
noticed that if you not seed it it always will give number 13 as first. If it was to be fully random you would get a different number everytime.
*Coherent: This means that it is very smooth. Here's a picture of some non-coherent noise.

This is coherent noise

Imagine that you zoom in on that non-coherent noise, and as you zoom in, you see that there are transitions between those pixels, and there you go,
coherent noise!

But that contrast is a bit high, don't you, let's smooth it out a bit:

There we go, looks much better, doesn't it?
But if you want to use it as a heightmap, it will be a bit too smooth, don't you think? Well, let me show you how to improve that by using octaves.
Maybe this image will help explain:

So, the smoothed noise is image a. If We zoom out on that noise, we get image b, and if we zoom out on b, u get image c. If u add a,b,c,... U get this:

And this is what we are going for.
Now, lets take that what we have learned and bring it in the practical side.
First of all, we'll need a pseudo-random number generator:
I searched a bit on the web and I came across this one:
1D
inline double findnoise(double x)
{
int x = (x<<13) ^ x;
return (double)( 1.0 - ( (x * (x * x * 15731 + 789221) + 1376312589) & Ox7fffffff) / 1073741824.0);
}
Here's the 2D version
inline double findnoise2(double x,double y)
{
int n=(int)x+(int)y*57;
n=(n<<13)^n;
int nn=(n*(n*n*60493+19990303)+1376312589)&0x7fffffff;
return 1.0-((double)nn/1073741824.0);
}
Don't ask me how it works because I dont know how it works myself, something with prime numbers... But what I do know is that it returns a value between
-1 and 1, just like our perlin noise function.
Now, as I've explained before, we get coherent noise because we look between the pixels, so our perlin noise function must be able to accept
double numbers. So we have to interpolate between the pixels. Interpolate is like taking the average, but a little bit more complicated. We'll be using
cosine interpoliation. It gives a very nice effect because it looks like a curve. But I wont explain too much, that's maybe for another tutorial.
Here is it.
inline double interpolate(double a,double b,double x)
{
double ft=x * 3.1415927;
double f=(1.0-cos(ft))* 0.5;
return a*(1.0-f)+b*f;
}
Now you might ask: "Hey, what about the 2D version?". Well, let me explain this. Let's take the average of a and b and call it d.
Let's also take c and d and call it e. And if we take the average of d and e it would equal to the average of a b c and d.
This enables us to stick to 1 dimension, making the code alot easier. Now, I'll combine these into one function. It uses SDL for image handling.
double noise(double x,double y)
{
double floorx=(double)((int)x);//This is kinda a cheap way to floor a double integer.
double floory=(double)((int)y);
double s,t,u,v;//Integer declaration
s=findnoise2(floorx,floory);
t=findnoise2(floorx+1,floory);
u=findnoise2(floorx,floory+1);//Get the surrounding pixels to calculate the transition.
v=findnoise2(floorx+1,floory+1);
double int1=interpolate1(s,t,x-floorx);//Interpolate between the values.
double int2=interpolate1(u,v,x-floorx);//Here we use x-floorx, to get 1st dimension. Don't mind the x-floorx thingie, it's part of the cosine formula.
return interpolate1(int1,int2,y-floory);//Here we use y-floory, to get the 2nd dimension.
}
And there we are, a perlin noise function. It returns a value between -1 and 1. To convert this to a 0-256 RGB value, we simple multiply it by 128,
then add 128 to it. But in the theory section I mentioned something about octaves, well I'll show it now. I call this result clouds.
SDL_Surface *Render_Clouds(int w,int h,double zoom,double p, int r, int g, int b)//w and h speak for themselves, zoom wel zoom in and out on it, I usually
{// use 75. P stands for persistence, this controls the roughness of the picture, i use 1/2
int octaves=2;
SDL_Surface *ret=SDL_CreateRGBSurface(SDL_SWSURFACE,w,h,24,0,0,0,0);//Create an empty image.
for(int y=0;y<h;y++)
{//Loops to loop trough all the pixels
for(int x=0;x<w;x++)
{
double getnoise =0;
for(int a=0;a<octaves-1;a++)//This loops trough the octaves.
{
double frequency = pow(2,a);//This increases the frequency with every loop of the octave.
double amplitude = pow(p,a);//This decreases the amplitude with every loop of the octave.
getnoise += noise2(((double)x)*frequency/zoom,((double)y)/zoom*frequency)*amplitude;//This uses our perlin noise functions. It calculates all our zoom and frequency and amplitude
}// It gives a decimal value, you know, between the pixels. Like 4.2 or 5.1
int color= (int)((getnoise*128.0)+128.0);//Convert to 0-256 values.
if(color>255)
color=255;
if(color<0)
color=0;
SetPixel(ret,x,y,(int)((r/255.0)*(double)color),(int)((g/255.0)*(double)color),(int)((b/255.0)*(double)color));//This colours the image with the RGB values
}// given at the beginning in the function.
}
return ret;
}
And this gives some nice clouds. I hope you enjoyed this tutorial, and I there's enough request, I'll make a more in-depth sequel.






MultiQuote






|