Sign in to follow this  

Using Perlin Noise

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

If you intended to correct an error in the post then please contact us.

Recommended Posts

Ive been looking over some of the functions and source others have used to generate Perlin noise...So far all Ive been able to produce is fractural textures...Im wanting to do more of the marble ones...Now if were loading into an image buffer the values for Perlin Noise when creating a marble texture I can guess that one would not do it pixel by pixel. As was done with my fractural code...But what Im trying to understand is HOW is it done? That is if anyone has done this before...Sourcecode and simple forumas will suffice as an explanation as mine all produce simple noise and not the products seen here: http://www.cs.utah.edu/~budge/Graphics/6620Html/ass5.html Perlin noise source:
#define P_B 0x100
#define P_BM 0xff
#define P_N 0x1000
#define P_NP 12   /* 2^N */
#define P_NM 0xfff

#define s_curve(t) ( t * t * (3. - 2. * t) )
#define lerp(t, a, b) ( a + t * (b - a) )
#define setup(i,b0,b1,r0,r1)        t = vec[i] + P_N;        b0 = ((int)t) & P_BM;        b1 = (b0+1) & P_BM;        r0 = t - (int)t;        r1 = r0 - 1.;
#define at2(rx,ry) ( rx * q[0] + ry * q[1] )
#define at3(rx,ry,rz) ( rx * q[0] + ry * q[1] + rz * q[2] )

static int p[P_B + P_B + 2];
static double g3[P_B + P_B + 2][3];
static double g2[P_B + P_B + 2][2];
static double g1[P_B + P_B + 2];
static int start = 1;

void normalize2(double v[2])
{
   double s;

   s = sqrt(v[0] * v[0] + v[1] * v[1]);
   v[0] = v[0] / s;
   v[1] = v[1] / s;
}

void normalize3(double v[3])
{
   double s;

   s = sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
   v[0] = v[0] / s;
   v[1] = v[1] / s;
   v[2] = v[2] / s;
}

void initP(void)
{
   int i, j, k;

   for (i = 0 ; i < P_B ; i++) {
      p[i] = i;
      g1[i] = (double)((rand() % (P_B + P_B)) - P_B) / P_B;

      for (j = 0 ; j < 2 ; j++)
         g2[i][j] = (double)((rand() % (P_B + P_B)) - P_B) / P_B;
      normalize2(g2[i]);

      for (j = 0 ; j < 3 ; j++)
         g3[i][j] = (double)((rand() % (P_B + P_B)) - P_B) / P_B;
      normalize3(g3[i]);
   }

   while (--i) {
      k = p[i];
      p[i] = p[j = rand() % P_B];
      p[j] = k;
   }

   for (i = 0 ; i < P_B + 2 ; i++) {
      p[P_B + i] = p[i];
      g1[P_B + i] = g1[i];
      for (j = 0 ; j < 2 ; j++)
         g2[P_B + i][j] = g2[i][j];
      for (j = 0 ; j < 3 ; j++)
         g3[P_B + i][j] = g3[i][j];
   }
}

double noise1(double arg)
{
   int bx0, bx1;
   double rx0, rx1, sx, t, u, v, vec[1];

   vec[0] = arg;
   if (start) {
      start = 0;
      initP();
   }

   setup(0,bx0,bx1,rx0,rx1);

   sx = s_curve(rx0);
   u = rx0 * g1[ p[ bx0 ] ];
   v = rx1 * g1[ p[ bx1 ] ];

   return(lerp(sx, u, v));
}

double noise2(double vec[2])
{
   int bx0, bx1, by0, by1, b00, b10, b01, b11;
   double rx0, rx1, ry0, ry1, *q, sx, sy, a, b, t, u, v;
   int i, j;

   if (start) {
      start = 0;
      initP();
   }

   setup(0, bx0,bx1, rx0,rx1);
   setup(1, by0,by1, ry0,ry1);

   i = p[ bx0 ];
   j = p[ bx1 ];

   b00 = p[ i + by0 ];
   b10 = p[ j + by0 ];
   b01 = p[ i + by1 ];
   b11 = p[ j + by1 ];

   sx = s_curve(rx0);
   sy = s_curve(ry0);

   q = g2[ b00 ] ; u = at2(rx0,ry0);
   q = g2[ b10 ] ; v = at2(rx1,ry0);
   a = lerp(sx, u, v);

   q = g2[ b01 ] ; u = at2(rx0,ry1);
   q = g2[ b11 ] ; v = at2(rx1,ry1);
   b = lerp(sx, u, v);

   return lerp(sy, a, b);
}

double noise3(double vec[3])
{
   int bx0, bx1, by0, by1, bz0, bz1, b00, b10, b01, b11;
   double rx0, rx1, ry0, ry1, rz0, rz1, *q, sy, sz, a, b, c, d, t, u, v;
   int i, j;

   if (start) {
      start = 0;
      initP();
   }

   setup(0, bx0,bx1, rx0,rx1);
   setup(1, by0,by1, ry0,ry1);
   setup(2, bz0,bz1, rz0,rz1);

   i = p[ bx0 ];
   j = p[ bx1 ];

   b00 = p[ i + by0 ];
   b10 = p[ j + by0 ];
   b01 = p[ i + by1 ];
   b11 = p[ j + by1 ];

   t  = s_curve(rx0);
   sy = s_curve(ry0);
   sz = s_curve(rz0);

   q = g3[ b00 + bz0 ] ; u = at3(rx0,ry0,rz0);
   q = g3[ b10 + bz0 ] ; v = at3(rx1,ry0,rz0);
   a = lerp(t, u, v);

   q = g3[ b01 + bz0 ] ; u = at3(rx0,ry1,rz0);
   q = g3[ b11 + bz0 ] ; v = at3(rx1,ry1,rz0);
   b = lerp(t, u, v);

   c = lerp(sy, a, b);

   q = g3[ b00 + bz1 ] ; u = at3(rx0,ry0,rz1);
   q = g3[ b10 + bz1 ] ; v = at3(rx1,ry0,rz1);
   a = lerp(t, u, v);

   q = g3[ b01 + bz1 ] ; u = at3(rx0,ry1,rz1);
   q = g3[ b11 + bz1 ] ; v = at3(rx1,ry1,rz1);
   b = lerp(t, u, v);

   d = lerp(sy, a, b);

   return lerp(sz, c, d);
}

/* --- My harmonic summing functions - PDB --------------------------*/

/*
   In what follows "alpha" is the weight when the sum is formed.
   Typically it is 2, As this approaches 1 the function is noisier.
   "beta" is the harmonic scaling/spacing, typically 2.
*/

double PerlinNoise1D(double x,double alpha,double beta,int n)
{
   int i;
   double val,sum = 0;
   double p,scale = 1;

   p = x;

   for (i=0;i<n;i++) {
      val = noise1(p);
      sum += val / scale;
      scale *= alpha;
      p *= beta;
   }
   return(sum);
}

double PerlinNoise2D(double x,double y,double alpha,double beta,int n)
{
   int i;
   double val,sum = 0;
   double p[2],scale = 1;

   p[0] = x;
   p[1] = y;
   for (i=0;i<n;i++) {
      val = noise2(p);
      sum += val / scale;
      scale *= alpha;
      p[0] *= beta;
      p[1] *= beta;
   }
   return(sum);
}

double PerlinNoise3D(double x,double y,double z,double alpha,double beta,int n)
{
   int i;
   double val,sum = 0;
   double p[3],scale = 1;

   p[0] = x;
   p[1] = y;
   p[2] = z;
   for (i=0;i<n;i++) {
      val = noise3(p);
      sum += val / scale;
      scale *= alpha;
      p[0] *= beta;
      p[1] *= beta;
      p[2] *= beta;
   }
   return(sum);
}

My fractural function (as you can see I didnt use the above functions) I ended up morphing this from another source into my own code)
unsigned char *createFractal(int w, int h, GLuint ptype, int depth, double warp, int tscale, int color_depth, GLfloat dcolors[], GLfloat lcolors[]) {
	int i;
	int x, z;
	int bsize, csize;
	int r;
	int red,green,blue,alpha;
	int c;
	int byte_count;
	int *hf2 = new int[w*h];
	int bpp;
	int width = w;
	int height = h;
	GLuint type = ptype;
	unsigned char *imageData;
	
	if(warp == 0) warp = 1;

	switch (type)
	{
	case GL_ALPHA:
	case GL_LUMINANCE:
	case GL_INTENSITY:
		bpp = 8; break;
			
	case GL_LUMINANCE_ALPHA:	bpp = 16; break;
	case GL_RGB:		bpp = 24; break;
	case GL_RGBA:		bpp = 32; break;
	}
	byte_count = bpp/8;

	imageData = new unsigned char[(width * height)*byte_count];

	for(i=0; i<(int)(width * height * byte_count); i++) {
		imageData[i] = (unsigned char) (255); 
	}

	for(i=0; i<(int)(width*height); i++) {
		hf2[i] = 0;
	}

	r = depth; // height values range

	bsize = w;

	csize = bsize/2;

	while(csize > 0) {
	
		for(x=0; x<(int)width; x+=bsize) {
			for(z=0; z<(int)height; z+=bsize) {			
				hf2[index(x+csize, z+csize, w, h)] = f4(hf2[index(x, z,w,h)],
												 hf2[index(x+bsize, z,w,h)],
												 hf2[index(x+bsize, z+bsize,w,h)],
												 hf2[index(x, z+bsize,w,h)]) + rand()%(r+1) - r/2;
			}
		} 

		for(x=0; x<(int)width; x+=bsize) {
			for(z=0; z<(int)height; z+=bsize) {			
				hf2[index(x+csize, z,w,h)] += f4(hf2[index(x, z,w,h)],
											hf2[index(x+bsize, z,w,h)],
											hf2[index(x+csize, z-csize,w,h)],
											hf2[index(x+csize, z+csize,w,h)]) + rand()%(r+1) - r/2;
				hf2[index(x, z+csize,w,h)] += f4(hf2[index(x, z,w,h)],
											hf2[index(x+csize, z+csize,w,h)],
											hf2[index(x, z+bsize,w,h)],
											hf2[index(x-csize, z+csize,w,h)]) + rand()%(r+1) - r/2;
			}
		} 

		if(csize<tscale) {
			r = r/2;
		}
			
		bsize /= (int)(1 + (warp * 0.1));
		csize = bsize/2;
	}; 

	for(x=0; x<(int)(width*height); x++) {
		if(hf2[x] < 0) 
			hf2[x] /= 2;
	} 

	x = width*height;

	for(i=0; i<x; i++) {
		c = (220 + (hf2[i] - hf2[i+1]));

		if(c<color_depth) c = color_depth;
		else if(c>255) c = 255;
		//print(c);
		red = BlendRGB(dcolors[0]*255,lcolors[0]*255,c);
		green = BlendRGB(dcolors[1]*255,lcolors[1]*255,c);
		blue = BlendRGB(dcolors[2]*255,lcolors[2]*255,c);
		//print(red);
		//print(green);
		//print(blue);
		alpha = (red+green+blue+1) / 3;
			
		imageData[i*byte_count] = red;
		if (byte_count > 1)
		{
			imageData[i*byte_count+1] = green;
			imageData[i*byte_count+2] = blue;
			if (byte_count == 4)
				imageData[i*byte_count+3] = alpha;
		}
	}

	imageData[(x-1)*byte_count] = 255;
	if (byte_count > 1)
	{
		imageData[(x-1)*byte_count+1] = 255;
		imageData[(x-1)*byte_count+2] = 255;
		if (byte_count == 4)
			imageData[(x-1)*byte_count+3] = 255;
	}
	return imageData;
}

Now how does one use these functions correctly if not pixel by pixel how else are they computed? Using matrices? I would think that somehow the last pixel set would have to be remembered and the value next up would be a shade lighter or darker to clump them into a pattern more than just random pixels... Thanks!

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Why wouldn't you do it per pixel? If you're computing the whole texture in advance you only need to do it once.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
I tried that and ended up with 1 pixel sized noise...My problem is I do not know how to create a marble function using the perlin source I posted earlier...And the net is no help. Most of what I found I couldnt get to work...

Share this post


Link to post
Share on other sites
read this

and specifically this

the short answer is that, instead of using the noise function directly to generate a texture, you use it indirectly to perturb something like a simple gradient into something much more interesting, like marble.

Quote:
Now how does one use these functions correctly if not pixel by pixel how else are they computed? Using matrices? I would think that somehow the last pixel set would have to be remembered and the value next up would be a shade lighter or darker to clump them into a pattern more than just random pixels...


if i understand you right, i think you need to study up a bit more on noise...read the pages that first link goes to. the point of procedural textures is that you dont need to keep track of the points around it to generate a certain point, because they are all described by the same equations.

good luck.

Share this post


Link to post
Share on other sites
So basically I should be able to do this:

z=0
for loop for x
for loop for y
buffer[z] = PerlinNoise2D(x,y,2,2,1);
buffer[z+1] = PerlinNoise2D(x,y,2,2,1);
buffer[z+2] = PerlinNoise2D(x,y,2,2,1);
z+=4
end for loop y
end for loop x

or do I pass color values from another texture into x and y on the perlin funtion?

Im just a little confused on what goes where is all...

Share this post


Link to post
Share on other sites
Ok so in this formula:

texture = cosine( x + perlin(x,y,z) )

let me see if I understand this

I create a striped texture then I apply perlin noise to it to produce a marble effect. how do I transfer the striped texture to the new texture modified is what has got me confused. I have not found an example yet in sourcecode (except what Ive already posted) showing me what step by step is done to produce a marble texture...Not the Perlin functions but after the perlin functions inside a function called drawMarble using these perlin functions. How is it transfered from perlin noise using a texture that has stripes or whatever and apply noise to it and move it pixel by pixel into a imagebuffer to be wrapped around a sphere, cube, whatever...Thats what I dont understand!

Sorry if I dont get it but you might as well be talking martian to me trying to explain trig functions...When I see it in action in sourcecode without any extra hidden functions or the like just straight code I then understand...If someone here really knows how to implement this please post at the very least a pseudo code diagram of the implementation telling me what goes where...Then Ill stop asking these dumb questions lol


Share this post


Link to post
Share on other sites
Ok after searching here with different keyphrases I found this thread and came up with this:


for(x=0;x<1024;x++) {
for(y=0;y<1024;y++) {
for(w = 0;w < 3;w++) {
temp_texRGB[z+w] = 255 * ((PerlinNoise2D(y*0.01,x*0.01,2,2,5)+1)/2);
}
z += 3;
}
}



Which produced this

You know Im done looking on Google...Ill just stick with searching these forums from now on lol...

Share this post


Link to post
Share on other sites

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

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this