Perlin Noise
Posted: 12.09.2004, 05:19
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/ ... /ass5.html
Perlin noise source:
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...
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/ ... /ass5.html
Perlin noise source:
Code: Select all
#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);
}
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...