Using Perlin Noise

Started by
8 comments, last by keyofrassilon 19 years, 7 months ago
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 + 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;
      g1 = (double)((rand() % (P_B + P_B)) - P_B) / P_B;

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

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

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

   for (i = 0 ; i < P_B + 2 ; i++) {
      p[P_B + i] = p;
      g1[P_B + i] = g1;
      for (j = 0 ; j < 2 ; j++)
         g2[P_B + i][j] = g2[j];
      for (j = 0 ; j < 3 ; j++)
         g3[P_B + i][j] = g3[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;
   b10 = p[ j + by0 ];
   b01 = p;
   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);

   <span class="cpp-keyword">return</span> lerp(sy, a, b);
}

<span class="cpp-keyword">double</span> noise3(<span class="cpp-keyword">double</span> vec[<span class="cpp-number">3</span>])
{
   <span class="cpp-keyword">int</span> bx0, bx1, by0, by1, bz0, bz1, b00, b10, b01, b11;
   <span class="cpp-keyword">double</span> rx0, rx1, ry0, ry1, rz0, rz1, *q, sy, sz, a, b, c, d, t, u, v;
   <span class="cpp-keyword">int</span> i, j;

   <span class="cpp-keyword">if</span> (start) {
      start = <span class="cpp-number">0</span>;
      initP();
   }

   setup(<span class="cpp-number">0</span>, bx0,bx1, rx0,rx1);
   setup(<span class="cpp-number">1</span>, by0,by1, ry0,ry1);
   setup(<span class="cpp-number">2</span>, bz0,bz1, rz0,rz1);

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

   b00 = p;
   b10 = p[ j + by0 ];
   b01 = p;
   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);

   <span class="cpp-keyword">return</span> lerp(sz, c, d);
}

<span class="cpp-comment">/* — My harmonic summing functions - PDB ————————–*/</span>

<span class="cpp-comment">/*
   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.
*/</span>

<span class="cpp-keyword">double</span> PerlinNoise1D(<span class="cpp-keyword">double</span> x,<span class="cpp-keyword">double</span> alpha,<span class="cpp-keyword">double</span> beta,<span class="cpp-keyword">int</span> n)
{
   <span class="cpp-keyword">int</span> i;
   <span class="cpp-keyword">double</span> val,sum = <span class="cpp-number">0</span>;
   <span class="cpp-keyword">double</span> p,scale = <span class="cpp-number">1</span>;

   p = x;

   <span class="cpp-keyword">for</span> (i=<span class="cpp-number">0</span>;i&lt;n;i++) {
      val = noise1(p);
      sum += val / scale;
      scale *= alpha;
      p *= beta;
   }
   <span class="cpp-keyword">return</span>(sum);
}

<span class="cpp-keyword">double</span> PerlinNoise2D(<span class="cpp-keyword">double</span> x,<span class="cpp-keyword">double</span> y,<span class="cpp-keyword">double</span> alpha,<span class="cpp-keyword">double</span> beta,<span class="cpp-keyword">int</span> n)
{
   <span class="cpp-keyword">int</span> i;
   <span class="cpp-keyword">double</span> val,sum = <span class="cpp-number">0</span>;
   <span class="cpp-keyword">double</span> p[<span class="cpp-number">2</span>],scale = <span class="cpp-number">1</span>;

   p[<span class="cpp-number">0</span>] = x;
   p[<span class="cpp-number">1</span>] = y;
   <span class="cpp-keyword">for</span> (i=<span class="cpp-number">0</span>;i&lt;n;i++) {
      val = noise2(p);
      sum += val / scale;
      scale *= alpha;
      p[<span class="cpp-number">0</span>] *= beta;
      p[<span class="cpp-number">1</span>] *= beta;
   }
   <span class="cpp-keyword">return</span>(sum);
}

<span class="cpp-keyword">double</span> PerlinNoise3D(<span class="cpp-keyword">double</span> x,<span class="cpp-keyword">double</span> y,<span class="cpp-keyword">double</span> z,<span class="cpp-keyword">double</span> alpha,<span class="cpp-keyword">double</span> beta,<span class="cpp-keyword">int</span> n)
{
   <span class="cpp-keyword">int</span> i;
   <span class="cpp-keyword">double</span> val,sum = <span class="cpp-number">0</span>;
   <span class="cpp-keyword">double</span> p[<span class="cpp-number">3</span>],scale = <span class="cpp-number">1</span>;

   p[<span class="cpp-number">0</span>] = x;
   p[<span class="cpp-number">1</span>] = y;
   p[<span class="cpp-number">2</span>] = z;
   <span class="cpp-keyword">for</span> (i=<span class="cpp-number">0</span>;i&lt;n;i++) {
      val = noise3(p);
      sum += val / scale;
      scale *= alpha;
      p[<span class="cpp-number">0</span>] *= beta;
      p[<span class="cpp-number">1</span>] *= beta;
      p[<span class="cpp-number">2</span>] *= beta;
   }
   <span class="cpp-keyword">return</span>(sum);
}

</pre></div><!–ENDSCRIPT–>

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)
<!–STARTSCRIPT–><!–source lang="cpp"–><div class="source"><pre>
<span class="cpp-keyword">unsigned</span> <span class="cpp-keyword">char</span> *createFractal(<span class="cpp-keyword">int</span> w, <span class="cpp-keyword">int</span> h, GLuint ptype, <span class="cpp-keyword">int</span> depth, <span class="cpp-keyword">double</span> warp, <span class="cpp-keyword">int</span> tscale, <span class="cpp-keyword">int</span> color_depth, GLfloat dcolors[], GLfloat lcolors[]) {
	<span class="cpp-keyword">int</span> i;
	<span class="cpp-keyword">int</span> x, z;
	<span class="cpp-keyword">int</span> bsize, csize;
	<span class="cpp-keyword">int</span> r;
	<span class="cpp-keyword">int</span> red,green,blue,alpha;
	<span class="cpp-keyword">int</span> c;
	<span class="cpp-keyword">int</span> byte_count;
	<span class="cpp-keyword">int</span> *hf2 = <span class="cpp-keyword">new</span> <span class="cpp-keyword">int</span>[w*h];
	<span class="cpp-keyword">int</span> bpp;
	<span class="cpp-keyword">int</span> width = w;
	<span class="cpp-keyword">int</span> height = h;
	GLuint type = ptype;
	<span class="cpp-keyword">unsigned</span> <span class="cpp-keyword">char</span> *imageData;
	
	<span class="cpp-keyword">if</span>(warp == <span class="cpp-number">0</span>) warp = <span class="cpp-number">1</span>;

	<span class="cpp-keyword">switch</span> (type)
	{
	<span class="cpp-keyword">case</span> GL_ALPHA:
	<span class="cpp-keyword">case</span> GL_LUMINANCE:
	<span class="cpp-keyword">case</span> GL_INTENSITY:
		bpp = <span class="cpp-number">8</span>; <span class="cpp-keyword">break</span>;
			
	<span class="cpp-keyword">case</span> GL_LUMINANCE_ALPHA:	bpp = <span class="cpp-number">16</span>; <span class="cpp-keyword">break</span>;
	<span class="cpp-keyword">case</span> GL_RGB:		bpp = <span class="cpp-number">24</span>; <span class="cpp-keyword">break</span>;
	<span class="cpp-keyword">case</span> GL_RGBA:		bpp = <span class="cpp-number">32</span>; <span class="cpp-keyword">break</span>;
	}
	byte_count = bpp/<span class="cpp-number">8</span>;

	imageData = <span class="cpp-keyword">new</span> <span class="cpp-keyword">unsigned</span> <span class="cpp-keyword">char</span>[(width * height)*byte_count];

	<span class="cpp-keyword">for</span>(i=<span class="cpp-number">0</span>; i&lt;(<span class="cpp-keyword">int</span>)(width * height * byte_count); i++) {
		imageData<span style="font-weight:bold;"> = (<span class="cpp-keyword">unsigned</span> <span class="cpp-keyword">char</span>) (<span class="cpp-number">255</span>); 
	}

	<span class="cpp-keyword">for</span>(i=<span class="cpp-number">0</span>; i&lt;(<span class="cpp-keyword">int</span>)(width*height); i++) {
		hf2<span style="font-weight:bold;"> = <span class="cpp-number">0</span>;
	}

	r = depth; <span class="cpp-comment">// height values range</span>

	bsize = w;

	csize = bsize/<span class="cpp-number">2</span>;

	<span class="cpp-keyword">while</span>(csize &gt; <span class="cpp-number">0</span>) {
	
		<span class="cpp-keyword">for</span>(x=<span class="cpp-number">0</span>; x&lt;(<span class="cpp-keyword">int</span>)width; x+=bsize) {
			<span class="cpp-keyword">for</span>(z=<span class="cpp-number">0</span>; z&lt;(<span class="cpp-keyword">int</span>)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+<span class="cpp-number">1</span>) - r/<span class="cpp-number">2</span>;
			}
		} 

		<span class="cpp-keyword">for</span>(x=<span class="cpp-number">0</span>; x&lt;(<span class="cpp-keyword">int</span>)width; x+=bsize) {
			<span class="cpp-keyword">for</span>(z=<span class="cpp-number">0</span>; z&lt;(<span class="cpp-keyword">int</span>)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+<span class="cpp-number">1</span>) - r/<span class="cpp-number">2</span>;
				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+<span class="cpp-number">1</span>) - r/<span class="cpp-number">2</span>;
			}
		} 

		<span class="cpp-keyword">if</span>(csize&lt;tscale) {
			r = r/<span class="cpp-number">2</span>;
		}
			
		bsize /= (<span class="cpp-keyword">int</span>)(<span class="cpp-number">1</span> + (warp * <span class="cpp-number">0</span>.<span class="cpp-number">1</span>));
		csize = bsize/<span class="cpp-number">2</span>;
	}; 

	<span class="cpp-keyword">for</span>(x=<span class="cpp-number">0</span>; x&lt;(<span class="cpp-keyword">int</span>)(width*height); x++) {
		<span class="cpp-keyword">if</span>(hf2[x] &lt; <span class="cpp-number">0</span>) 
			hf2[x] /= <span class="cpp-number">2</span>;
	} 

	x = width*height;

	<span class="cpp-keyword">for</span>(i=<span class="cpp-number">0</span>; i&lt;x; i++) {
		c = (<span class="cpp-number">220</span> + (hf2<span style="font-weight:bold;"> - hf2[i+<span class="cpp-number">1</span>]));

		<span class="cpp-keyword">if</span>(c&lt;color_depth) c = color_depth;
		<span class="cpp-keyword">else</span> <span class="cpp-keyword">if</span>(c&gt;<span class="cpp-number">255</span>) c = <span class="cpp-number">255</span>;
		<span class="cpp-comment">//print©;</span>
		red = BlendRGB(dcolors[<span class="cpp-number">0</span>]*<span class="cpp-number">255</span>,lcolors[<span class="cpp-number">0</span>]*<span class="cpp-number">255</span>,c);
		green = BlendRGB(dcolors[<span class="cpp-number">1</span>]*<span class="cpp-number">255</span>,lcolors[<span class="cpp-number">1</span>]*<span class="cpp-number">255</span>,c);
		blue = BlendRGB(dcolors[<span class="cpp-number">2</span>]*<span class="cpp-number">255</span>,lcolors[<span class="cpp-number">2</span>]*<span class="cpp-number">255</span>,c);
		<span class="cpp-comment">//print(red);</span>
		<span class="cpp-comment">//print(green);</span>
		<span class="cpp-comment">//print(blue);</span>
		alpha = (red+green+blue+<span class="cpp-number">1</span>) / <span class="cpp-number">3</span>;
			
		imageData[i*byte_count] = red;
		<span class="cpp-keyword">if</span> (byte_count &gt; <span class="cpp-number">1</span>)
		{
			imageData[i*byte_count+<span class="cpp-number">1</span>] = green;
			imageData[i*byte_count+<span class="cpp-number">2</span>] = blue;
			<span class="cpp-keyword">if</span> (byte_count == <span class="cpp-number">4</span>)
				imageData[i*byte_count+<span class="cpp-number">3</span>] = alpha;
		}
	}

	imageData[(x-<span class="cpp-number">1</span>)*byte_count] = <span class="cpp-number">255</span>;
	<span class="cpp-keyword">if</span> (byte_count &gt; <span class="cpp-number">1</span>)
	{
		imageData[(x-<span class="cpp-number">1</span>)*byte_count+<span class="cpp-number">1</span>] = <span class="cpp-number">255</span>;
		imageData[(x-<span class="cpp-number">1</span>)*byte_count+<span class="cpp-number">2</span>] = <span class="cpp-number">255</span>;
		<span class="cpp-keyword">if</span> (byte_count == <span class="cpp-number">4</span>)
			imageData[(x-<span class="cpp-number">1</span>)*byte_count+<span class="cpp-number">3</span>] = <span class="cpp-number">255</span>;
	}
	<span class="cpp-keyword">return</span> imageData;
}

</pre></div><!–ENDSCRIPT–>

Now how does &#111;ne 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!
Advertisement
Does anyone have some sort of sourcecode example or links to them that could help me along with this?

Thanks!
Why wouldn't you do it per pixel? If you're computing the whole texture in advance you only need to do it once.
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...
^me


What I need is a decent tutorial showing me source code examples in C or C++ explaining whats going on...
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.
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...

Hugo Elias has written a fairly decent tutorial here. It's quite comprehensive, and I believe it mentions how to generate marble-like effects.

Hope this helps.
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


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...

This topic is closed to new replies.

Advertisement