Wavelet noise

Blitz3D Forums/Blitz3D Programming/Wavelet noise

Axel Wheeler(Posted 2010) [#1]
Ok, here's the deal. I've ported some code from c (or c++?) to B3D from a paper on wavelet noise functions:

Wavelets are thought to be superior and faster than Perlin noise, my other favorite cloudy noise function. This type of function is great for generating random clouds, terrain, and other kinds of "intelligent noise" within your programs without having to resort to Photoshop's Clouds filter (which of course is itself using a similar function).

The original paper is here, and is I believe it is the seminal work on Wavelets:

http://graphics.pixar.com/library/WaveletNoise/paper.pdf

(The code is Appendix A, but is also reproduced below)

Now, since I don't actually know much of the c/c++ languages I am stunned that it does indeed seem to produce something. I changed some ways of doing things into Blitz-style ways, but otherwise it's pretty similar, I think. It is reproduced below, along with a little program to demonstrate a tile.

It seems not to be concerned about things like frequency scaling (the noise is pretty dense as is and not too useful, I wouldn't think). Also, it seems to produce a 3D noise range and then show a 2D slice of it, which seems pretty wasteful.

Really I'm just looking for thoughts, corrections from any c/c++ weens out there. Or if there are any other great wavelet noise functions out there I'd love to see them.

First, my port of the routines:
;/* Note: this code is designed For brevity, Not efficiency; many operations can be hoisted,
;	* precomputed, Or vectorized. Some of the straightforward details, such as tile meshing,
;	* decorrelating bands And fading out the Last band, are omitted in the interest of space.*/

Global noiseTileSize

Const ARAD=16
Global aCoeffs[2*ARAD]
Restore datastart
For i=0 To 2*ARAD-1
	Read aCoeffs[i]
Next

Global pCoeffs[4]
pCoeffs[0]=0.25
pCoeffs[1]=0.75
pCoeffs[2]=0.75
pCoeffs[3]=0.25

Dim noise#(1)
Dim temp1#(1)
Dim temp2#(1)

Function OtherMod(x,n)
	Local m=x Mod n
	If m<0
		Return m+n
	Else
		Return m
	EndIf
End Function
	
Function Downsample (from#, dest#, n, stride ) 
	
	For i=0 To n/2-1
		temp1(i*stride)=0
		For k=2*i-ARAD To 2*i+ARAD
			temp1(i*stride) = temp1(i*stride) + aCoeffs[k-2*i] * noise(OtherMod(k,n)*stride);
		Next
		Next
			
End Function

.datastart
Data 0.000334,-0.001528, 0.000410, 0.003545,-0.000938,-0.008233, 0.002172, 0.019120
Data -0.005040,-0.044412, 0.011655, 0.103311,-0.025936,-0.243780, 0.033979, 0.655340
Data 0.655340, 0.033979,-0.243780,-0.025936, 0.103311, 0.011655,-0.044412,-0.005040
Data 0.019120, 0.002172,-0.008233,-0.000938, 0.003546, 0.000410,-0.001528, 0.000334

			
Function Upsample( from#, dest#, n, stride) 
	For i=0 To n-1
		temp2(i*stride) = 0;
		For k=i/2 To i/2+1
			temp2(i*stride) = temp2(i*stride)+pCoeffs[i-2*k] * temp1(OtherMod(k,n/2)*stride)
		Next
	Next
End Function
					
Function GenerateNoiseTile( n, olap) 
	If n Mod 2<>n Then n=n+1; /* tile size must be even */
	Local ix, iy, iz, i
	Local sz=n*n*n;*sizeof(Float)
	
	Dim temp1#(sz)
	Dim temp2#(sz)
	Dim noise#(sz)
		;/* Step 1. Fill the tile with random numbers in the range -1 To 1. */
	For i=0 To n*n*n 
		noise(i) = (Rnd(1)+Rnd(1))/2.0;gaussianNoise()
	Next
							;/* Steps 2 And 3. Downsample And upsample the tile */
	
	
	For iy=0 To n
		For iz=0 To n
								;{ /* Each x row */
			i = iy*n + iz*n*n
			Downsample( noise(i), temp1(i), n, 1 )
			Upsample( temp1(i), temp2(i), n, 1 )
		Next
	Next
	
	For ix=0 To n
		For iz=0 To n 
										;{ /* Each y row */
			i = ix + iz*n*n
			Downsample( temp2(i), temp1(i), n, n )
			Upsample( temp1(i), temp2(i), n, n )
		Next
	Next
	
	For ix=0 To n
		For iy=0 To n 
										;/* Each z row */
			i = ix + iy*n
			Downsample( temp2(i), temp1(i), n, n*n )
			Upsample( temp1(i), temp2(i), n, n*n )
		Next
	Next
	
										;/* Step 4. Subtract out the coarse-scale contribution */
	
	For i=0 To n*n*n
		noise(i)=noise(i)-temp2(i)
	Next
	
;			/* Avoid even/odd variance difference by adding odd-offset version of noise To itself.*/
	Local offset=n/2
	If offset Mod 2=0 Then offset=offset+1
	
	i=0
	For ix=0 To n 
		For iy=0 To n 
			For iz=0 To n
				temp1(i) = noise( OtherMod(ix+offset,n) + OtherMod(iy+offset,n)*n + OtherMod(iz+offset,n)*n*n )
				i=i+1
			Next
		Next
	Next
	
	For i=0 To n*n*n 
		noise(i)=noise(i)+temp1(i)
		noiseTileSize=n
	Next
	
End Function


Next, the original:
/* Note: this code is designed for brevity, not efficiency; many operations can be hoisted,
* precomputed, or vectorized. Some of the straightforward details, such as tile meshing,
* decorrelating bands and fading out the last band, are omitted in the interest of space.*/
static float *noiseTileData; static int noiseTileSize;
int Mod(int x, int n) {int m=x%n; return (m<0) ? m+n : m;}
#define ARAD 16
void Downsample (float *from, float *to, int n, int stride ) {
float *a, aCoeffs[2*ARAD] = {
0.000334,-0.001528, 0.000410, 0.003545,-0.000938,-0.008233, 0.002172, 0.019120,
-0.005040,-0.044412, 0.011655, 0.103311,-0.025936,-0.243780, 0.033979, 0.655340,
0.655340, 0.033979,-0.243780,-0.025936, 0.103311, 0.011655,-0.044412,-0.005040,
0.019120, 0.002172,-0.008233,-0.000938, 0.003546, 0.000410,-0.001528, 0.000334};
a = &aCoeffs[ARAD];
for (int i=0; i<n/2; i++) {
to[i*stride] = 0;
for (int k=2*i-ARAD; k<=2*i+ARAD; k++)
to[i*stride] += a[k-2*i] * from[Mod(k,n)*stride];
}
}
void Upsample( float *from, float *to, int n, int stride) {
float *p, pCoeffs[4] = { 0.25, 0.75, 0.75, 0.25 };
p = &pCoeffs[2];
for (int i=0; i<n; i++) {
to[i*stride] = 0;
for (int k=i/2; k<=i/2+1; k++)
to[i*stride] += p[i-2*k] * from[Mod(k,n/2)*stride];
}
}
void GenerateNoiseTile( int n, int olap) {
if (n%2) n++; /* tile size must be even */
int ix, iy, iz, i, sz=n*n*n*sizeof(float);
float *temp1=(float *)malloc(sz),*temp2=(float *)malloc(sz),*noise=(float *)malloc(sz);
/* Step 1. Fill the tile with random numbers in the range -1 to 1. */
for (i=0; i<n*n*n; i++) noise[i] = gaussianNoise();
/* Steps 2 and 3. Downsample and upsample the tile */
for (iy=0; iy<n; iy++) for (iz=0; iz<n; iz++) { /* each x row */
i = iy*n + iz*n*n; Downsample( &noise[i], &temp1[i], n, 1 );
Upsample( &temp1[i], &temp2[i], n, 1 );
}
for (ix=0; ix<n; ix++) for (iz=0; iz<n; iz++) { /* each y row */
i = ix + iz*n*n; Downsample( &temp2[i], &temp1[i], n, n );
Upsample( &temp1[i], &temp2[i], n, n );
}
for (ix=0; ix<n; ix++) for (iy=0; iy<n; iy++) { /* each z row */
i = ix + iy*n; Downsample( &temp2[i], &temp1[i], n, n*n );
Upsample( &temp1[i], &temp2[i], n, n*n );
}
/* Step 4. Subtract out the coarse-scale contribution */
for (i=0; i<n*n*n; i++) {noise[i]-=temp2[i];}
/* Avoid even/odd variance difference by adding odd-offset version of noise to itself.*/
int offset=n/2; if (offset%2==0) offset++;
for (i=0,ix=0; ix<n; ix++) for (iy=0; iy<n; iy++) for (iz=0; iz<n; iz++)
temp1[i++] = noise[ Mod(ix+offset,n) + Mod(iy+offset,n)*n + Mod(iz+offset,n)*n*n ];
for (i=0; i<n*n*n; i++) {noise[i]+=temp1[i];}
noiseTileData=noise; noiseTileSize=n; free(temp1); free(temp2);
}


And finally my little program to display them. Blitz blurs the image, though, so if you hit Enter it will save to C:\noise.bmp. You can also zoom in/out with the arrow keys.
Include ".\wavelets.bb"

Graphics3D(1024,768,32,1)
SetBuffer(BackBuffer())
SeedRnd(MilliSecs())

Global cam=CreateCamera()
MoveEntity(cam,0,0,-2)
CameraRange(cam,0.001,10)
Global sun=CreateLight()
LightColor(sun,255,255,200)
AmbientLight(200,200,255)


Global size=128
Global sprite
Global tex

createWavelet(32)

While Not KeyHit(1)
	If KeyHit(200)
		PositionEntity(cam,0,0,EntityZ(cam)*.9)
	EndIf
	If KeyHit(208)
		PositionEntity(cam,0,0,EntityZ(cam)*1.111111)
	EndIf
	If KeyHit(28)
		SaveBuffer(TextureBuffer(tex),"c:\noise.bmp")
	EndIf
	Delay(10)
	RenderWorld()
	
	Flip()
Wend


Function createWavelet(size)
	generatenoisetile(32,1)
	createNoiseTexture()
	
End Function

Function createNoiseTexture()
	tex=CreateTexture(size,size)
	SetBuffer(TextureBuffer(tex))
	
	For y=0 To size-1
		For x=0 To size-1
			c#=255*noise(y*size+x)
			Color(c,c,c)
			Oval(x,y,1,1)
		Next
	Next
	
	sprite=CreateSprite()
	EntityTexture(sprite,tex)
	
	SetBuffer(BackBuffer())
End Function



puki(Posted 2010) [#2]
Interesting stuff.


Axel Wheeler(Posted 2010) [#3]
Thanks. It's not clear how to tweak the noise to produce less cluttered patterns to make it really useful.

Considering how revolutionary wavelet noise is supposed to be (compared to Perlin noise, for example) it's odd that there are so few examples in pseodocode (or any actual code) out there, at least that I could find. All the articles appear to be aimed at methematicians and cover the theory rather than the practice. Oh, well.

-Pete